From 29e00d1ec225eb52b04b4f663f408c8d7b110371 Mon Sep 17 00:00:00 2001 From: Tom Page Date: Thu, 4 Jan 2018 09:05:18 +0000 Subject: [PATCH 1/2] RM-5344 Only gather DOD audit entries if there is a DOD RM site. This creates an edge case where DOD audit entries are no longer displayed if a DOD RM site is deleted and then a standard RM site is created to replace it. However this seems like an unlikely use case, and there is a workaround of exporting the audit log before deleting the DOD RM site. Two audit queries are still made for DOD RM sites because DOD sites cause some standard audit entries to be created (e.g. the holds container created event, etc.). --- .../rm-service-context.xml | 1 + .../RecordsManagementAuditServiceImpl.java | 22 ++- ...rdsManagementAuditServiceImplUnitTest.java | 176 ++++++++++++++++++ 3 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImplUnitTest.java diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml index 01ac11f766..1d2f094680 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -932,6 +932,7 @@ + diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java index 573dc9d2b4..11df111ad5 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java @@ -27,6 +27,9 @@ package org.alfresco.module.org_alfresco_module_rm.audit; +import static org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model.TYPE_DOD_5015_SITE; +import static org.alfresco.module.org_alfresco_module_rm.model.rma.type.RmSiteType.DEFAULT_SITE_NAME; + import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; @@ -51,6 +54,7 @@ import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction import org.alfresco.module.org_alfresco_module_rm.audit.event.AuditEvent; import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; +import org.alfresco.module.org_alfresco_module_rm.model.rma.type.RmSiteType; import org.alfresco.repo.audit.AuditComponent; import org.alfresco.repo.audit.model.AuditApplication; import org.alfresco.repo.content.MimetypeMap; @@ -72,6 +76,7 @@ import org.alfresco.service.cmr.repository.MLText; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; @@ -186,6 +191,7 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean private DictionaryService dictionaryService; private TransactionService transactionService; private NodeService nodeService; + private SiteService siteService; private ContentService contentService; private AuditComponent auditComponent; private AuditService auditService; @@ -237,6 +243,13 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean this.nodeService = nodeService; } + /** + * Set the site service (used to check the type of RM site created). + * + * @param siteService The site service. + */ + public void setSiteService(SiteService siteService) { this.siteService = siteService; } + /** * Sets the ContentService instance */ @@ -820,7 +833,7 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean * @param writer Writer to write the audit trail * @param reportFormat Format to write the audit trail in, ignored if writer is null */ - private void getAuditTrailImpl( + protected void getAuditTrailImpl( final RecordsManagementAuditQueryParameters params, final List results, final Writer writer, @@ -1091,7 +1104,12 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean } // Get audit entries - auditService.auditQuery(callback, dod5015AuditQueryParams, maxEntries); + QName siteType = nodeService.getType(siteService.getSite(DEFAULT_SITE_NAME).getNodeRef()); + if (siteType.equals(TYPE_DOD_5015_SITE)) + { + auditService.auditQuery(callback, dod5015AuditQueryParams, maxEntries); + } + // We always need to make the standard query - regardless of the type of RM site (to get events like RM site created). auditService.auditQuery(callback, auditQueryParams, maxEntries); // finish off the audit trail report diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImplUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImplUnitTest.java new file mode 100644 index 0000000000..2f1b39fa0c --- /dev/null +++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImplUnitTest.java @@ -0,0 +1,176 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.module.org_alfresco_module_rm.audit; + +import static java.util.Collections.emptyList; + +import static org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService.ReportFormat.JSON; +import static org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditServiceImpl.DOD5015_AUDIT_APPLICATION_NAME; +import static org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditServiceImpl.RM_AUDIT_APPLICATION_NAME; +import static org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditServiceImpl.RM_AUDIT_PATH_ROOT; +import static org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model.TYPE_DOD_5015_SITE; +import static org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService.DEFAULT_RM_SITE_ID; +import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.TYPE_RM_SITE; +import static org.alfresco.module.org_alfresco_module_rm.model.rma.type.RmSiteType.DEFAULT_SITE_NAME; +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import com.google.common.collect.Sets; + +import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; +import org.alfresco.repo.audit.AuditComponent; +import org.alfresco.service.cmr.audit.AuditQueryParameters; +import org.alfresco.service.cmr.audit.AuditService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +/** + * Unit tests for {@link RecordsManagementAuditServiceImpl}. + * + * @author Tom Page + * @since 2.7 + */ +public class RecordsManagementAuditServiceImplUnitTest +{ + /** The maximum entries to return in the audit query. */ + private static final int MAX_ENTRIES = 10; + /** A node representing the file plan root. */ + private static final NodeRef FILE_PLAN_NODE = new NodeRef("file://plan/node"); + /** A node representing the RM site. */ + private static final NodeRef RM_SITE_NODE = new NodeRef("rm://site/node"); + /** The class under test. */ + @InjectMocks + private RecordsManagementAuditServiceImpl recordsManagementAuditServiceImpl; + @Mock + private NodeService mockNodeService; + @Mock + private SiteService mockSiteService; + @Mock + private AuditService mockAuditService; + @Mock + private FilePlanService mockFilePlanService; + @Mock + AuditComponent mockAuditComponent; + @Mock + private Writer mockWriter; + @Mock + private SiteInfo mockSiteInfo; + @Captor + private ArgumentCaptor queryParamsCaptor; + + /** Set up the mocks. */ + @Before + public void setUp() + { + initMocks(this); + + when(mockFilePlanService.getFilePlanBySiteId(DEFAULT_RM_SITE_ID)).thenReturn(FILE_PLAN_NODE); + when(mockSiteService.getSite(DEFAULT_SITE_NAME)).thenReturn(mockSiteInfo); + when(mockSiteInfo.getNodeRef()).thenReturn(RM_SITE_NODE); + + recordsManagementAuditServiceImpl.setIgnoredAuditProperties(emptyList()); + } + + /** + * Check that if the RM site is not a DOD site then the audit trail doesn't make a query for DOD events. + * + * @throws IOException Unexpected. + */ + @Test + public void testAuditWithoutDOD() throws IOException + { + RecordsManagementAuditQueryParameters params = new RecordsManagementAuditQueryParameters(); + params.setMaxEntries(MAX_ENTRIES); + List results = new ArrayList<>(); + // Return a standard site type. + when(mockNodeService.getType(RM_SITE_NODE)).thenReturn(TYPE_RM_SITE); + + // Call the method under test. + recordsManagementAuditServiceImpl.getAuditTrailImpl(params, results, mockWriter, JSON); + + // Check that exactly one audit query was performed. + verify(mockAuditService, times(1)) + .auditQuery(any(AuditService.AuditQueryCallback.class), queryParamsCaptor.capture(), + eq(MAX_ENTRIES)); + // We always need to make the standard query - regardless of the type of RM site (to get events like RM site created). + assertEquals("The application name should be the standard RM application", RM_AUDIT_APPLICATION_NAME, + queryParamsCaptor.getValue().getApplicationName()); + // Check that the event of viewing the audit log was itself audited. + verify(mockAuditComponent).recordAuditValues(eq(RM_AUDIT_PATH_ROOT), any(Map.class)); + } + + /** + * Check that if the RM site is a DOD site then the audit trail makes a query for DOD events and the standard events. + * + * @throws IOException Unexpected. + */ + @Test + public void testAuditWithDOD() throws IOException + { + RecordsManagementAuditQueryParameters params = new RecordsManagementAuditQueryParameters(); + params.setMaxEntries(MAX_ENTRIES); + List results = new ArrayList<>(); + // Return a DOD site type. + when(mockNodeService.getType(RM_SITE_NODE)).thenReturn(TYPE_DOD_5015_SITE); + + // Call the method under test. + recordsManagementAuditServiceImpl.getAuditTrailImpl(params, results, mockWriter, JSON); + + // Check that two audit queries were performed (one for DOD events and one for standard events). + verify(mockAuditService, times(2)) + .auditQuery(any(AuditService.AuditQueryCallback.class), queryParamsCaptor.capture(), + eq(MAX_ENTRIES)); + Set apps = queryParamsCaptor.getAllValues().stream().map(AuditQueryParameters::getApplicationName) + .collect(Collectors.toSet()); + // We always need to make the standard query - regardless of the type of RM site (to get events like RM site created). + assertEquals("Expected the standard audit query and the DOD audit query.", + Sets.newHashSet(RM_AUDIT_APPLICATION_NAME, DOD5015_AUDIT_APPLICATION_NAME), apps); + // Check that the event of viewing the audit log was itself audited. + verify(mockAuditComponent).recordAuditValues(eq(RM_AUDIT_PATH_ROOT), any(Map.class)); + } +} From 77b5d35685579ead660e3175c044a023e43effd0 Mon Sep 17 00:00:00 2001 From: Tom Page Date: Thu, 4 Jan 2018 14:28:27 +0000 Subject: [PATCH 2/2] RM-5344 Handle case where site with id "rm" doesn't exist. This is not possible normally, but it does happen with the integration tests. Also fix some whitespace in the test class. --- .../RecordsManagementAuditServiceImpl.java | 11 +++++++--- ...RecordsManagementAuditServiceImplTest.java | 20 +++++++++---------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java index 11df111ad5..138d3394a4 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java @@ -76,6 +76,7 @@ import org.alfresco.service.cmr.repository.MLText; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; @@ -1104,10 +1105,14 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean } // Get audit entries - QName siteType = nodeService.getType(siteService.getSite(DEFAULT_SITE_NAME).getNodeRef()); - if (siteType.equals(TYPE_DOD_5015_SITE)) + SiteInfo siteInfo = siteService.getSite(DEFAULT_SITE_NAME); + if (siteInfo != null) { - auditService.auditQuery(callback, dod5015AuditQueryParams, maxEntries); + QName siteType = nodeService.getType(siteInfo.getNodeRef()); + if (siteType.equals(TYPE_DOD_5015_SITE)) + { + auditService.auditQuery(callback, dod5015AuditQueryParams, maxEntries); + } } // We always need to make the standard query - regardless of the type of RM site (to get events like RM site created). auditService.auditQuery(callback, auditQueryParams, maxEntries); diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementAuditServiceImplTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementAuditServiceImplTest.java index e718bc84c4..79ad3909f5 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementAuditServiceImplTest.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementAuditServiceImplTest.java @@ -78,18 +78,18 @@ public class RecordsManagementAuditServiceImplTest extends BaseRMTestCase @Override public Void run() throws Exception { - // test start time recorded - testStartTime = new Date(); + // test start time recorded + testStartTime = new Date(); - // Stop and clear the log - rmAuditService.stopAuditLog(filePlan); - rmAuditService.clearAuditLog(filePlan); - rmAuditService.startAuditLog(filePlan); + // Stop and clear the log + rmAuditService.stopAuditLog(filePlan); + rmAuditService.clearAuditLog(filePlan); + rmAuditService.startAuditLog(filePlan); - // check that audit service is started - assertTrue(rmAuditService.isAuditLogEnabled(filePlan)); + // check that audit service is started + assertTrue(rmAuditService.isAuditLogEnabled(filePlan)); - return null; + return null; } }); } @@ -198,7 +198,7 @@ public class RecordsManagementAuditServiceImplTest extends BaseRMTestCase } }); } - + /** * Test getAuditTrail method and parameter filters. */