From 7158053a1934484bf075cce7ae3c9bf0b4548b6c Mon Sep 17 00:00:00 2001 From: Tatyana Valkevych Date: Mon, 26 Oct 2015 15:58:04 +0000 Subject: [PATCH] MNT-15068: Merged V2.2 to V2.2.1.x 109401: RM-2391 : The Audit Log GET requests have to verify first which user is logged in and to which data it has access. - Added capability checking to the AuditLog Get REST API. Added a unit test. Minor changes on the Share side to forward the forbidden status. 111064: RM-2391 : - also check for the access audit capability on each node from the report 114786: RM-2391 : - Implemented final fix and added a unit test. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/V2.2.1.x@115178 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../rm-service-context.xml | 1 + .../rm-webscript-context.xml | 2 + .../RecordsManagementAuditServiceImpl.java | 23 ++++++ .../script/AuditLogGet.java | 54 +++++++++++++- ...RecordsManagementAuditServiceImplTest.java | 49 +++++++++++++ .../legacy/webscript/AuditRestApiTest.java | 72 +++++++++++++++++++ .../test/util/BaseRMWebScriptTestCase.java | 7 +- 7 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/webscript/AuditRestApiTest.java diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml index 978d73d381..4057ea3e2d 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -921,6 +921,7 @@ + cm:lastThumbnailModification diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml index c815e3e411..637551ea30 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml @@ -536,6 +536,8 @@ + + diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java index b0b406217c..9e804bc72e 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java @@ -40,6 +40,7 @@ import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction; import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; 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.repo.audit.AuditComponent; import org.alfresco.repo.audit.model.AuditApplication; @@ -62,6 +63,7 @@ import org.alfresco.service.cmr.repository.ContentWriter; 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.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; @@ -98,6 +100,8 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean /** Logger */ private static Log logger = LogFactory.getLog(RecordsManagementAuditServiceImpl.class); + private static final String ACCESS_AUDIT_CAPABILITY = "AccessAudit"; + private static final String KEY_RM_AUDIT_NODE_RECORDS = "RMAUditNodeRecords"; protected static final String RM_AUDIT_EVENT_LOGIN_SUCCESS = "Login.Success"; @@ -179,6 +183,7 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean private RecordsManagementActionService rmActionService; private FilePlanService filePlanService; private NamespaceService namespaceService; + protected CapabilityService capabilityService; private boolean shutdown = false; @@ -270,6 +275,17 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean { this.namespaceService = namespaceService; } + + /** + * @param capabilityService capability service + */ + public void setCapabilityService(CapabilityService capabilityService) + { + this.capabilityService = capabilityService; + } + + + /** * @param ignoredAuditProperties @@ -904,6 +920,13 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean // Skip it return true; } + + if(nodeRef != null && nodeService.exists(nodeRef) && + !AccessStatus.ALLOWED.equals( + capabilityService.getCapabilityAccessState(nodeRef, ACCESS_AUDIT_CAPABILITY))) + { + return true; + } // TODO: Refactor this to use the builder pattern RecordsManagementAuditEntry entry = new RecordsManagementAuditEntry( diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogGet.java index 6cf968f99c..3acdbd1197 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogGet.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogGet.java @@ -21,9 +21,17 @@ package org.alfresco.module.org_alfresco_module_rm.script; import java.io.File; import java.io.IOException; +import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditQueryParameters; +import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService.ReportFormat; +import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; +import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; import org.alfresco.repo.web.scripts.content.ContentStreamer; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptResponse; @@ -39,9 +47,16 @@ public class AuditLogGet extends BaseAuditRetrievalWebScript private static Log logger = LogFactory.getLog(AuditLogGet.class); private static final String PARAM_EXPORT = "export"; + private static final String ACCESS_AUDIT_CAPABILITY = "AccessAudit"; /** Content Streamer */ protected ContentStreamer contentStreamer; + + /** Capability service */ + protected CapabilityService capabilityService; + + /** File plan service */ + protected FilePlanService filePlanService; /** * @param contentStreamer @@ -50,6 +65,24 @@ public class AuditLogGet extends BaseAuditRetrievalWebScript { this.contentStreamer = contentStreamer; } + + /** + * + * @param capabilityService Capability Service + */ + public void setCapabilityService(CapabilityService capabilityService) + { + this.capabilityService = capabilityService; + } + + /** + * + * @param capabilityService Capability Service + */ + public void setFilePlanService(FilePlanService filePlanService) + { + this.filePlanService = filePlanService; + } @Override public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException @@ -58,8 +91,16 @@ public class AuditLogGet extends BaseAuditRetrievalWebScript try { + + RecordsManagementAuditQueryParameters queryParams = parseQueryParameters(req); + ReportFormat reportFormat = parseReportFormat(req); + + if( !userCanAccessAudit(queryParams) ) + { + throw new WebScriptException(Status.STATUS_FORBIDDEN, "Access denied because the user does not have the Access Audit capability"); + } // parse the parameters and get a file containing the audit trail - auditTrail = this.rmAuditService.getAuditTrailFile(parseQueryParameters(req), parseReportFormat(req)); + auditTrail = this.rmAuditService.getAuditTrailFile(queryParams, reportFormat); if (logger.isDebugEnabled()) { @@ -101,4 +142,15 @@ public class AuditLogGet extends BaseAuditRetrievalWebScript } } } + + private boolean userCanAccessAudit(RecordsManagementAuditQueryParameters queryParams) + { + NodeRef targetNode = queryParams.getNodeRef(); + if( targetNode == null ) + { + targetNode = filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID); + } + return AccessStatus.ALLOWED.equals( + capabilityService.getCapabilityAccessState(targetNode, ACCESS_AUDIT_CAPABILITY)); + } } \ No newline at end of file diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementAuditServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementAuditServiceImplTest.java index 65b0cede07..1a9d8f469c 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementAuditServiceImplTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementAuditServiceImplTest.java @@ -141,6 +141,55 @@ public class RecordsManagementAuditServiceImplTest extends BaseRMTestCase }, ADMIN_USER); } + /** + * Test getAuditTrail method to check that deleted items always show in the audit. + * + * @see RM-2391 (last addressed isue) + */ + public void testGetAuditTrailForDeletedItem() + { + // We have only one entry for the event "audit.start": + List entries = getAuditTrail(1, ADMIN_USER); + + assertEquals(entries.get(0).getEvent(), "audit.start"); + + // Event "audit.view" was generated but will be visible on the next call to getAuditTrail(). + + // Make a change: + updateTitle(filePlan, ADMIN_USER); // event=Update RM Object + + // Show the audit has been updated; at this point we have three entries for the three events up to now: + // "audit.start", "audit.view" and "Update RM Object"; + entries = getAuditTrail(3, ADMIN_USER); + + assertEquals(entries.get(0).getEvent(), "audit.start"); + assertEquals(entries.get(1).getEvent(), "audit.view"); + assertEquals(entries.get(2).getEvent(), "Update RM Object"); + + // New "audit.view" event was generated - will be visible on next getAuditTrail(). + + doTestInTransaction(new Test() + { + @Override + public Void run() throws Exception + { + nodeService.deleteNode(record); + List entries = getAuditTrail(5, ADMIN_USER); + + assertEquals(entries.get(0).getEvent(), "audit.start"); + assertEquals(entries.get(1).getEvent(), "audit.view"); + assertEquals(entries.get(2).getEvent(), "Update RM Object"); + assertEquals(entries.get(3).getEvent(), "audit.view"); + + // Show the audit contains a reference to the deleted item: + assertEquals(entries.get(4).getEvent(), "Delete RM Object"); + assertEquals(entries.get(4).getNodeRef(), record); + + return null; + } + }); + } + /** * Test getAuditTrail method and parameter filters. */ diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/webscript/AuditRestApiTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/webscript/AuditRestApiTest.java new file mode 100644 index 0000000000..d28c62d790 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/webscript/AuditRestApiTest.java @@ -0,0 +1,72 @@ +package org.alfresco.module.org_alfresco_module_rm.test.legacy.webscript; + +import java.io.IOException; +import java.text.MessageFormat; + +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMWebScriptTestCase; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.util.GUID; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; + + +public class AuditRestApiTest extends BaseRMWebScriptTestCase +{ + /** URL for the REST APIs */ + protected static final String GET_NODE_AUDITLOG_URL_FORMAT = "/api/node/{0}/rmauditlog"; + + private static final String USER_WITHOUT_AUDIT_CAPABILITY = GUID.generate(); + + private NodeRef record; + + public void testAuditAccessCapability() throws IOException + { + + String recordAuditUrl = MessageFormat.format(GET_NODE_AUDITLOG_URL_FORMAT,record.toString().replace("://", "/")); + + sendRequest(new GetRequest(recordAuditUrl), Status.STATUS_OK, AuthenticationUtil.getAdminUserName() ); + + sendRequest(new GetRequest(recordAuditUrl), Status.STATUS_FORBIDDEN, USER_WITHOUT_AUDIT_CAPABILITY ); + } + + @Override + protected void setupTestData() + { + super.setupTestData(); + + retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Object execute() throws Throwable + { + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil + .getSystemUserName()); + + createUser(USER_WITHOUT_AUDIT_CAPABILITY); + + record = utils.createRecord(recordFolder, GUID.generate()); + + + return null; + } + }); + } + + @Override + protected void tearDownImpl() + { + super.tearDownImpl(); + + deleteUser(USER_WITHOUT_AUDIT_CAPABILITY); + } + + protected String getRMSiteId() + { + return filePlanService.DEFAULT_RM_SITE_ID; + } + + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMWebScriptTestCase.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMWebScriptTestCase.java index 53fed575c1..ac9c6f6798 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMWebScriptTestCase.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMWebScriptTestCase.java @@ -279,7 +279,7 @@ public class BaseRMWebScriptTestCase extends BaseWebScriptTest assertNotNull("Could not create base folder", folder); // Create the site - siteId = GUID.generate(); + siteId = getRMSiteId(); siteInfo = siteService.createSite("rm-site-dashboard", siteId, "title", "descrition", SiteVisibility.PUBLIC, RecordsManagementModel.TYPE_RM_SITE); filePlan = siteService.getContainer(siteId, RmSiteType.COMPONENT_DOCUMENT_LIBRARY); assertNotNull("Site document library container was not created successfully.", filePlan); @@ -383,4 +383,9 @@ public class BaseRMWebScriptTestCase extends BaseWebScriptTest authorityService.deleteAuthority(groupName, true); } } + + protected String getRMSiteId() + { + return GUID.generate(); + } }