From 09648f9829edc77259592c0bd345c1b0dc40ef2c Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Tue, 1 Apr 2014 23:53:16 +0000 Subject: [PATCH] RM-1315: Manage Permissions is not working for folder inside Holds/Unfiled Records * unit tests to show unfiled and holds permissions working correctly * fixed issues exposed by above * minor refactor for FilePlanPermissionService implementation to reduce complexity * added unit test execution to local build target git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@66102 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- build.xml | 4 +- rm-server/build.xml | 8 + .../rm-service-context.xml | 3 - .../fileplan/FilePlanService.java | 2 + .../fileplan/FilePlanServiceImpl.java | 112 +------- .../FilePlanPermissionServiceImpl.java | 100 ++----- .../util/ServiceBaseImpl.java | 71 ++++- ...FilePlanPermissionServiceImplUnitTest.java | 263 ++++++++++++++++++ .../test/AllUnitTestSuite.java | 4 +- .../test/util/BaseUnitTest.java | 91 +++++- 10 files changed, 478 insertions(+), 180 deletions(-) create mode 100644 rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImplUnitTest.java diff --git a/build.xml b/build.xml index 5bf95091b2..b66671e98d 100644 --- a/build.xml +++ b/build.xml @@ -18,7 +18,7 @@ - + @@ -39,7 +39,7 @@ - + diff --git a/rm-server/build.xml b/rm-server/build.xml index 85e3ad8c55..905f6c3229 100644 --- a/rm-server/build.xml +++ b/rm-server/build.xml @@ -25,4 +25,12 @@ + + + + + + + + \ No newline at end of file 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 43c6458641..ab4d930ada 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 @@ -489,9 +489,6 @@ init-method="init"> - - - diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanService.java index 1d44198a43..96fb691229 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanService.java @@ -139,6 +139,8 @@ public interface FilePlanService NodeRef createUnfiledContainer(NodeRef filePlan); /** + * Gets the hold container for a given file plan. Returns + * null if none. * * @param filePlan * @return diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanServiceImpl.java index e411d87d1c..c8c74bd658 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanServiceImpl.java @@ -30,8 +30,6 @@ import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; -import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; -import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService; import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService; import org.alfresco.module.org_alfresco_module_rm.security.ExtendedReaderDynamicAuthority; @@ -41,7 +39,6 @@ import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl; import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.site.SiteInfo; @@ -51,9 +48,6 @@ import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.util.Pair; import org.alfresco.util.ParameterCheck; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; import org.springframework.extensions.surf.util.I18NUtil; /** @@ -63,9 +57,7 @@ import org.springframework.extensions.surf.util.I18NUtil; * @since 2.1 */ public class FilePlanServiceImpl extends ServiceBaseImpl - implements FilePlanService, - RecordsManagementModel, - ApplicationContextAware + implements FilePlanService { /** I18N */ private final static String MSG_DUP_ROOT = "rm.service.dup-root"; @@ -85,18 +77,6 @@ public class FilePlanServiceImpl extends ServiceBaseImpl /** RM site file plan container */ private static final String FILE_PLAN_CONTAINER = "documentLibrary"; - /** Application context */ - private ApplicationContext applicationContext; - - /** - * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) - */ - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException - { - this.applicationContext = applicationContext; - } - /** * NOTE: for some reason spring couldn't cope with the circular references between these two * beans so we need to grab this one manually. @@ -124,14 +104,6 @@ public class FilePlanServiceImpl extends ServiceBaseImpl return (NodeDAO)applicationContext.getBean("nodeDAO"); } - /** - * @return internal node service - */ - protected NodeService getInternalNodeService() - { - return (NodeService)applicationContext.getBean("nodeService"); - } - /** * @return site service */ @@ -140,14 +112,6 @@ public class FilePlanServiceImpl extends ServiceBaseImpl return (SiteService)applicationContext.getBean("SiteService"); } - /** - * @return record service - */ - protected RecordService getRecordService() - { - return (RecordService)applicationContext.getBean("RecordService"); - } - /** * @return record folder service */ @@ -164,20 +128,6 @@ public class FilePlanServiceImpl extends ServiceBaseImpl return (TransferService)applicationContext.getBean("RmTransferService"); } - /** - * @see org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService#isFilePlanComponent(org.alfresco.service.cmr.repository.NodeRef) - */ - public boolean isFilePlanComponent(NodeRef nodeRef) - { - boolean result = false; - if (getInternalNodeService().exists(nodeRef) && - getInternalNodeService().hasAspect(nodeRef, ASPECT_FILE_PLAN_COMPONENT)) - { - result = true; - } - return result; - } - /** * @see org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService#getFilePlanComponentKind(org.alfresco.service.cmr.repository.NodeRef) */ @@ -201,7 +151,7 @@ public class FilePlanServiceImpl extends ServiceBaseImpl { result = FilePlanComponentKind.RECORD_FOLDER; } - else if (getRecordService().isRecord(nodeRef)) + else if (isRecord(nodeRef)) { result = FilePlanComponentKind.RECORD; } @@ -277,15 +227,7 @@ public class FilePlanServiceImpl extends ServiceBaseImpl } return result; - } - - /** - * @see org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService#isFilePlan(org.alfresco.service.cmr.repository.NodeRef) - */ - public boolean isFilePlan(NodeRef nodeRef) - { - return instanceOf(nodeRef, TYPE_FILE_PLAN); - } + } /** * @see org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService#getFilePlans() @@ -470,25 +412,18 @@ public class FilePlanServiceImpl extends ServiceBaseImpl containerType, properties).getChildRef(); - - // if (!inheritPermissions) - // { - // set inheritance to false - getPermissionService().setInheritParentPermissions(container, false); - getPermissionService().setPermission(container, allRoles, RMPermissionModel.READ_RECORDS, true); - getPermissionService().setPermission(container, ExtendedReaderDynamicAuthority.EXTENDED_READER, RMPermissionModel.READ_RECORDS, true); - getPermissionService().setPermission(container, ExtendedWriterDynamicAuthority.EXTENDED_WRITER, RMPermissionModel.FILING, true); - getPermissionService().setPermission(container, "Administrator", RMPermissionModel.FILING, true); - - // TODO set the admin users to have filing permissions on the unfiled container!!! - // TODO we will need to be able to get a list of the admin roles from the service - // } - // else - // { - // just inherit eveything - // TODO will change this when we are able to set permissions on holds and transfers! - // getPermissionService().setInheritParentPermissions(container, true); - // } + // set inheritance to false + getPermissionService().setInheritParentPermissions(container, false); + + // give all roles read permissions on the container by default + getPermissionService().setPermission(container, allRoles, RMPermissionModel.READ_RECORDS, true); + + // setup the extended reader permissions + getPermissionService().setPermission(container, ExtendedReaderDynamicAuthority.EXTENDED_READER, RMPermissionModel.READ_RECORDS, true); + getPermissionService().setPermission(container, ExtendedWriterDynamicAuthority.EXTENDED_WRITER, RMPermissionModel.FILING, true); + + // setup the administrator permissions + getPermissionService().setPermission(container, "Administrator", RMPermissionModel.FILING, true); return container; } @@ -605,23 +540,6 @@ public class FilePlanServiceImpl extends ServiceBaseImpl } } - /** - * @see org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService#isFilePlanContainer(org.alfresco.service.cmr.repository.NodeRef) - */ - @Override - public boolean isFilePlanContainer(NodeRef nodeRef) - { - return instanceOf(nodeRef, TYPE_RECORDS_MANAGEMENT_CONTAINER); - } - - /** - * @see org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService#isRecordCategory(org.alfresco.service.cmr.repository.NodeRef) - */ - public boolean isRecordCategory(NodeRef nodeRef) - { - return instanceOf(nodeRef, TYPE_RECORD_CATEGORY); - } - /** * @see org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService#createRecordCategory(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.namespace.QName, java.util.Map) */ diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImpl.java index d2ababc027..44c40dbd56 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImpl.java @@ -25,10 +25,6 @@ import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; -import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; -import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; -import org.alfresco.module.org_alfresco_module_rm.record.RecordService; -import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService; import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; @@ -37,7 +33,6 @@ import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AccessPermission; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.PermissionService; @@ -54,8 +49,7 @@ import org.apache.commons.logging.LogFactory; * @since 2.1 */ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl - implements FilePlanPermissionService, - RecordsManagementModel + implements FilePlanPermissionService { /** Permission service */ protected PermissionService permissionService; @@ -63,15 +57,6 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl /** Policy component */ protected PolicyComponent policyComponent; - /** File plan service */ - protected FilePlanService filePlanService; - - /** Record service */ - protected RecordService recordService; - - /** Record folder service */ - protected RecordFolderService recordFolderService; - /** Logger */ protected static Log logger = LogFactory.getLog(FilePlanPermissionServiceImpl.class); @@ -110,14 +95,6 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl this.permissionService = permissionService; } - /** - * @param nodeService node service - */ - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - /** * @param policyComponent policy component */ @@ -126,30 +103,6 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl this.policyComponent = policyComponent; } - /** - * @param filePlanService file plan service - */ - public void setFilePlanService(FilePlanService filePlanService) - { - this.filePlanService = filePlanService; - } - - /** - * @param recordService record service - */ - public void setRecordService(RecordService recordService) - { - this.recordService = recordService; - } - - /** - * @param recordFolderService record folder service - */ - public void setRecordFolderService(RecordFolderService recordFolderService) - { - this.recordFolderService = recordFolderService; - } - /** * @see org.alfresco.module.org_alfresco_module_rm.security.FilePlanPermissionService#setupRecordCategoryPermissions(org.alfresco.service.cmr.repository.NodeRef) */ @@ -175,7 +128,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl { public Object doWork() { - boolean fillingOnly = filePlanService.isFilePlan(parentNodeRef); + boolean fillingOnly = isFilePlan(parentNodeRef); // since this is not a root category, inherit from parent Set perms = permissionService.getAllSetPermissions(parentNodeRef); @@ -258,7 +211,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl */ public void onAddRecord(final NodeRef record, final QName aspectTypeQName) { - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + runAs(new AuthenticationUtil.RunAsWork() { public Object doWork() { @@ -412,7 +365,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl { if (nodeService.exists(nodeRef)) { - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + runAs(new AuthenticationUtil.RunAsWork() { public Object doWork() { @@ -438,17 +391,18 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl ParameterCheck.mandatory("authority", authority); ParameterCheck.mandatory("permission", permission); - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + runAsSystem(new AuthenticationUtil.RunAsWork() { public Boolean doWork() throws Exception { - if (filePlanService.isFilePlan(nodeRef)) + if (isFilePlan(nodeRef)) { setPermissionDown(nodeRef, authority, permission); } - else if (filePlanService.isFilePlanContainer(nodeRef) || - recordFolderService.isRecordFolder(nodeRef) || - recordService.isRecord(nodeRef)) + else if (isFilePlanContainer(nodeRef) || + isRecordFolder(nodeRef) || + isRecord(nodeRef) || + isHold(nodeRef)) { setReadPermissionUp(nodeRef, authority); setPermissionDown(nodeRef, authority, permission); @@ -463,7 +417,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl return null; } - }, AuthenticationUtil.getSystemUserName()); + }); } /** @@ -475,7 +429,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl private void setReadPermissionUp(NodeRef nodeRef, String authority) { NodeRef parent = nodeService.getPrimaryParent(nodeRef).getParentRef(); - if (parent != null && filePlanService.isFilePlanComponent(parent)) + if (parent != null && isFilePlanComponent(parent)) { setReadPermissionUpImpl(parent, authority); } @@ -492,7 +446,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl setPermissionImpl(nodeRef, authority, RMPermissionModel.READ_RECORDS); NodeRef parent = nodeService.getPrimaryParent(nodeRef).getParentRef(); - if (parent != null && filePlanService.isFilePlanComponent(parent)) + if (parent != null && isFilePlanComponent(parent)) { setReadPermissionUpImpl(parent, authority); } @@ -513,17 +467,17 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl // set permissions setPermissionImpl(nodeRef, authority, permission); - if (filePlanService.isFilePlanContainer(nodeRef) || - recordFolderService.isRecordFolder(nodeRef)) + if (isFilePlanContainer(nodeRef) || + isRecordFolder(nodeRef)) { List assocs = nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); for (ChildAssociationRef assoc : assocs) { NodeRef child = assoc.getChildRef(); - if (filePlanService.isFilePlanContainer(child) || - recordFolderService.isRecordFolder(child) || - recordService.isRecord(child) || - instanceOf(child, TYPE_HOLD) || + if (isFilePlanContainer(child) || + isRecordFolder(child) || + isRecord(child) || + isHold(child) || instanceOf(child, TYPE_TRANSFER)) { setPermissionDown(child, authority, permission); @@ -556,7 +510,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl */ public void deletePermission(final NodeRef nodeRef, final String authority, final String permission) { - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + runAsSystem(new AuthenticationUtil.RunAsWork() { public Boolean doWork() throws Exception { @@ -566,17 +520,17 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl // Delete permission on this node permissionService.deletePermission(nodeRef, authority, permission); - if (filePlanService.isFilePlanContainer(nodeRef) || - recordFolderService.isRecordFolder(nodeRef)) + if (isFilePlanContainer(nodeRef) || + isRecordFolder(nodeRef)) { List assocs = nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); for (ChildAssociationRef assoc : assocs) { NodeRef child = assoc.getChildRef(); - if (filePlanService.isFilePlanContainer(child) || - recordFolderService.isRecordFolder(child) || - recordService.isRecord(child)|| - instanceOf(child, TYPE_HOLD) || + if (isFilePlanContainer(child) || + isRecordFolder(child) || + isRecord(child)|| + isHold(child) || instanceOf(child, TYPE_TRANSFER)) { deletePermission(child, authority, permission); @@ -587,6 +541,6 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl return null; } - }, AuthenticationUtil.getSystemUserName()); + }); } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java index ddd5eeb0e6..cca1d1f49f 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java @@ -31,6 +31,9 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.QName; import org.alfresco.util.ParameterCheck; import org.alfresco.util.PropertyMap; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; /** * Helper base class for service implementations. @@ -38,13 +41,25 @@ import org.alfresco.util.PropertyMap; * @author Roy Wetherall * @since 2.1 */ -public class ServiceBaseImpl implements RecordsManagementModel +public class ServiceBaseImpl implements RecordsManagementModel, ApplicationContextAware { /** Node service */ protected NodeService nodeService; /** Dictionary service */ protected DictionaryService dictionaryService; + + /** Application context */ + protected ApplicationContext applicationContext; + + /** + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) + */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + this.applicationContext = applicationContext; + } /** * @param nodeService node service @@ -61,6 +76,60 @@ public class ServiceBaseImpl implements RecordsManagementModel { this.dictionaryService = dictionaryService; } + + /** + * Indicates whether the given node is a file plan component or not. + *

+ * Exposed in the FilePlan service. + * + * @see org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService#isFilePlanComponent(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isFilePlanComponent(NodeRef nodeRef) + { + boolean result = false; + + // use the internal node service to prevent redirection of security checking. + NodeService myNodeService = (NodeService)applicationContext.getBean("nodeService"); + + if (myNodeService.exists(nodeRef) && + myNodeService.hasAspect(nodeRef, ASPECT_FILE_PLAN_COMPONENT)) + { + result = true; + } + return result; + } + + /** + * Indicates whether the given node is a file plan or not. + *

+ * Exposed in the FilePlan service. + * + * @see org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService#isFilePlan(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isFilePlan(NodeRef nodeRef) + { + return instanceOf(nodeRef, TYPE_FILE_PLAN); + } + + /** + * Indicates whether the given node is a file plan container or not. + * + * @see org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService#isFilePlanContainer(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isFilePlanContainer(NodeRef nodeRef) + { + return instanceOf(nodeRef, TYPE_RECORDS_MANAGEMENT_CONTAINER); + } + + /** + * Indicates whether the given node is a record category or not. + * + * @see org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService#isRecordCategory(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isRecordCategory(NodeRef nodeRef) + { + return instanceOf(nodeRef, TYPE_RECORD_CATEGORY); + } /** * Indicates whether the given node is a record folder or not. diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImplUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImplUnitTest.java new file mode 100644 index 0000000000..66f4adea3f --- /dev/null +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImplUnitTest.java @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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 . + */ +package org.alfresco.module.org_alfresco_module_rm.security; + +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Spy; + +/** + * File plan permission service implementation unit test. + *

+ * Primarily tests the file plan permission service interaction with the + * permission service. + * + * @author Roy Wetherall + * @since 2.2 + */ +public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest +{ + /** test authority */ + protected static final String AUTHORITY = "anAuthority"; + + /** unfiled nodes */ + protected NodeRef unfiledRecordContainer; + protected NodeRef unfiledRecordFolder; + protected NodeRef unfiledRecordFolderChild; + protected NodeRef unfiledRecord; + + /** held nodes */ + protected NodeRef holdContainer; + protected NodeRef hold; + protected NodeRef heldRecord; + + /** file plan permission service implementation */ + @Spy @InjectMocks FilePlanPermissionServiceImpl filePlanPermissionService; + + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest#before() + */ + @Override + public void before() + { + super.before(); + + // mock up run as methods + mockRunAsMethods(filePlanPermissionService); + + // initialize node's + unfiledRecordContainer = generateContainerNodeRef(TYPE_UNFILED_RECORD_CONTAINER); + unfiledRecordFolder = generateContainerNodeRef(TYPE_UNFILED_RECORD_FOLDER); + unfiledRecordFolderChild = generateContainerNodeRef(TYPE_UNFILED_RECORD_FOLDER); + unfiledRecord = generateRecord(); + holdContainer = generateContainerNodeRef(TYPE_HOLD_CONTAINER); + hold = generateHoldNodeRef("my test hold"); + heldRecord = generateRecord(); + + // setup parent hierarchy + makePrimaryParentOf(filePlan, generateNodeRef(ContentModel.TYPE_FOLDER)); + + makePrimaryParentOf(unfiledRecordFolder, unfiledRecordContainer); + makePrimaryParentOf(unfiledRecordContainer, filePlan); + + makePrimaryParentOf(hold, holdContainer); + makePrimaryParentOf(holdContainer, filePlan); + + // setup child hierarchy + makeChildrenOf(unfiledRecordFolder, unfiledRecordFolderChild); + makeChildrenOf(unfiledRecordFolderChild, unfiledRecord); + + makeChildrenOf(hold, heldRecord); + } + + /** + * Helper method to generate a container node ref of a perticular type. + * + * @param type type of node reference + * @return {@link NodeRef} node reference that behaves like a container of the type given. + */ + private NodeRef generateContainerNodeRef(QName type) + { + NodeRef nodeRef = generateNodeRef(type); + setupAsFilePlanComponent(nodeRef); + doReturn(true).when(filePlanPermissionService).isFilePlanContainer(nodeRef); + return nodeRef; + } + + /** + * Set read permission on unfiled record folder. + */ + @Test + public void setReadPermissionOnUnfiledRecordFolder() + { + // set read permission on unfiled record folder + filePlanPermissionService.setPermission(unfiledRecordFolder, AUTHORITY, RMPermissionModel.READ_RECORDS); + + // verify permission set on target node + verify(mockedPermissionService, times(1)).setPermission(unfiledRecordFolder, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + + // verify READ permission set up hierarchy + verify(mockedPermissionService, times(1)).setPermission(unfiledRecordContainer, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + verify(mockedPermissionService, times(1)).setPermission(filePlan, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + + // verify READ permission set down hierarchy + verify(mockedPermissionService, times(1)).setPermission(unfiledRecordFolderChild, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + verify(mockedPermissionService, times(1)).setPermission(unfiledRecord, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + + } + + /** + * Set filling permission on unfiled record folder + */ + @Test + public void setReadAndFilePermissionOnUnfileRecordFolder() + { + // set read permission on unfiled record folder + filePlanPermissionService.setPermission(unfiledRecordFolder, AUTHORITY, RMPermissionModel.FILING); + + // verify permission set on target node + verify(mockedPermissionService, times(1)).setPermission(unfiledRecordFolder, AUTHORITY, RMPermissionModel.FILING, true); + + // verify READ permission set up hierarchy + verify(mockedPermissionService, times(1)).setPermission(unfiledRecordContainer, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + verify(mockedPermissionService, times(1)).setPermission(filePlan, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + + // verify FILING permission set down hierarchy + verify(mockedPermissionService, times(1)).setPermission(unfiledRecordFolderChild, AUTHORITY, RMPermissionModel.FILING, true); + verify(mockedPermissionService, times(1)).setPermission(unfiledRecord, AUTHORITY, RMPermissionModel.FILING, true); + } + + /** + * Remove permission from unfiled record folders. + */ + @Test + public void deletePermissionFromUnfiledRecordFolder() + { + // delete read permission from unfiled record folder + filePlanPermissionService.deletePermission(unfiledRecordFolder, AUTHORITY, RMPermissionModel.READ_RECORDS); + + // verify permission deleted on target node + verify(mockedPermissionService, times(1)).deletePermission(unfiledRecordFolder, AUTHORITY, RMPermissionModel.READ_RECORDS); + + // verify no permissions deleted up the hierarchy + verify(mockedPermissionService, never()).deletePermission(eq(unfiledRecordContainer), eq(AUTHORITY), anyString()); + verify(mockedPermissionService, never()).deletePermission(eq(filePlan), eq(AUTHORITY), anyString()); + + // verify READ permission removed down hierarchy + verify(mockedPermissionService, times(1)).deletePermission(unfiledRecordFolderChild, AUTHORITY, RMPermissionModel.READ_RECORDS); + verify(mockedPermissionService, times(1)).deletePermission(unfiledRecord, AUTHORITY, RMPermissionModel.READ_RECORDS); + } + + /** + * Set read permission on hold container + */ + public void setReadPermissionOnHoldContainer() + { + // set read permission on hold + filePlanPermissionService.setPermission(holdContainer, AUTHORITY, RMPermissionModel.READ_RECORDS); + + // verify permission set on target node + verify(mockedPermissionService, times(1)).setPermission(holdContainer, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + + // verify READ permission set up hierarchy + verify(mockedPermissionService, times(1)).setPermission(filePlan, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + + // verify READ permission set on hold + verify(mockedPermissionService, times(1)).setPermission(hold, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + + // verify permission not set on child of hold + verify(mockedPermissionService, never()).setPermission(eq(heldRecord), eq(AUTHORITY), anyString(), eq(true)); + + } + + /** + * Set filing permission on hold container + */ + public void setFilingPermissionOnHoldContainer() + { + // set read permission on hold + filePlanPermissionService.setPermission(holdContainer, AUTHORITY, RMPermissionModel.FILING); + + // verify permission set on target node + verify(mockedPermissionService, times(1)).setPermission(holdContainer, AUTHORITY, RMPermissionModel.FILING, true); + + // verify READ permission set up hierarchy + verify(mockedPermissionService, times(1)).setPermission(filePlan, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + + // verify FILING permission set on hold + verify(mockedPermissionService, times(1)).setPermission(hold, AUTHORITY, RMPermissionModel.FILING, true); + + // verify permission not set on child of hold + verify(mockedPermissionService, never()).setPermission(eq(heldRecord), eq(AUTHORITY), anyString(), eq(true)); + + } + + /** + * Set read permission on hold. + */ + @Test + public void setReadPermissionOnHold() + { + // set read permission on hold + filePlanPermissionService.setPermission(hold, AUTHORITY, RMPermissionModel.READ_RECORDS); + + // verify permission set on target node + verify(mockedPermissionService, times(1)).setPermission(hold, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + + // verify READ permission set up hierarchy + verify(mockedPermissionService, times(1)).setPermission(holdContainer, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + verify(mockedPermissionService, times(1)).setPermission(filePlan, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + + // verify permission not set on child of hold + verify(mockedPermissionService, never()).setPermission(eq(heldRecord), eq(AUTHORITY), anyString(), eq(true)); + } + + /** + * Set filing permission on hold. + */ + @Test + public void setFilingPermissionOnHold() + { + // set filing permission on hold + filePlanPermissionService.setPermission(hold, AUTHORITY, RMPermissionModel.FILING); + + // verify permission set on target node + verify(mockedPermissionService, times(1)).setPermission(hold, AUTHORITY, RMPermissionModel.FILING, true); + + // verify READ permission set up hierarchy + verify(mockedPermissionService, times(1)).setPermission(holdContainer, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + verify(mockedPermissionService, times(1)).setPermission(filePlan, AUTHORITY, RMPermissionModel.READ_RECORDS, true); + + // verify permission not set on child of hold + verify(mockedPermissionService, never()).setPermission(eq(heldRecord), eq(AUTHORITY), anyString(), eq(true)); + } + +} diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/AllUnitTestSuite.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/AllUnitTestSuite.java index ca1eb72d62..43b9af7306 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/AllUnitTestSuite.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/AllUnitTestSuite.java @@ -26,6 +26,7 @@ import org.alfresco.module.org_alfresco_module_rm.record.RecordServiceImplUnitTe import org.alfresco.module.org_alfresco_module_rm.script.hold.HoldPostUnitTest; import org.alfresco.module.org_alfresco_module_rm.script.hold.HoldPutUnitTest; import org.alfresco.module.org_alfresco_module_rm.script.hold.HoldsGetUnitTest; +import org.alfresco.module.org_alfresco_module_rm.security.FilePlanPermissionServiceImplUnitTest; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @@ -46,7 +47,8 @@ import org.junit.runners.Suite.SuiteClasses; TransferEvaluatorUnitTest.class, HoldsGetUnitTest.class, HoldPostUnitTest.class, - HoldPutUnitTest.class + HoldPutUnitTest.class, + FilePlanPermissionServiceImplUnitTest.class }) public class AllUnitTestSuite { diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseUnitTest.java index 43d52c48a0..6325c296a1 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseUnitTest.java @@ -19,7 +19,9 @@ package org.alfresco.module.org_alfresco_module_rm.test.util; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; @@ -34,14 +36,18 @@ import org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService; +import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QNamePattern; +import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.util.GUID; import org.alfresco.util.collections.CollectionUtils; import org.junit.Before; @@ -49,6 +55,9 @@ import org.junit.Rule; import org.junit.rules.ExpectedException; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.springframework.context.ApplicationContext; /** * Base unit test. @@ -72,12 +81,17 @@ public class BaseUnitTest implements RecordsManagementModel @Mock(name="dictionaryService") protected DictionaryService mockedDictionaryService; @Mock(name="namespaceService") protected NamespaceService mockedNamespaceService; @Mock(name="identifierService") protected IdentifierService mockedIdentifierService; + @Mock(name="permissionService") protected PermissionService mockedPermissionService; + /** rm service mocks */ @Mock(name="filePlanService") protected FilePlanService mockedFilePlanService; @Mock(name="recordFolderService") protected RecordFolderService mockedRecordFolderService; @Mock(name="recordService") protected RecordService mockedRecordService; @Mock(name="holdService") protected HoldService mockedHoldService; + /** application context mock */ + @Mock(name="applicationContext") protected ApplicationContext mockedApplicationContext; + /** expected exception rule */ @Rule public ExpectedException exception = ExpectedException.none(); @@ -89,9 +103,13 @@ public class BaseUnitTest implements RecordsManagementModel public void before() { MockitoAnnotations.initMocks(this); + + // setup application context + doReturn(mockedNodeService).when(mockedApplicationContext).getBean("nodeService"); // setup file plan filePlan = generateNodeRef(TYPE_FILE_PLAN); + setupAsFilePlanComponent(filePlan); doReturn(true).when(mockedFilePlanService).isFilePlan(filePlan); // setup basic file plan component @@ -112,8 +130,7 @@ public class BaseUnitTest implements RecordsManagementModel doReturn(result).when(mockedNodeService).getChildAssocs(eq(recordFolder), eq(ContentModel.ASSOC_CONTAINS), any(QNamePattern.class)); doReturn(result).when(mockedNodeService).getParentAssocs(record); doReturn(Collections.singletonList(recordFolder)).when(mockedRecordFolderService).getRecordFolders(record); - doReturn(Collections.singletonList(record)).when(mockedRecordService).getRecords(recordFolder); - + doReturn(Collections.singletonList(record)).when(mockedRecordService).getRecords(recordFolder); } /** @@ -179,7 +196,7 @@ public class BaseUnitTest implements RecordsManagementModel doReturn(filePlan).when(mockedFilePlanService).getFilePlan(nodeRef); doReturn(filePlan).when(mockedNodeService).getProperty(nodeRef, PROP_ROOT_NODEREF); } - + /** * Helper method to generate a node reference. * @@ -207,4 +224,72 @@ public class BaseUnitTest implements RecordsManagementModel } return nodeRef; } + + /** + * Helper method to make one node the primary parent of the other. + *

+ * Assumes the cm:contains assoc type. + * + * @param child + * @param parent + */ + protected void makePrimaryParentOf(NodeRef child, NodeRef parent) + { + doReturn(new ChildAssociationRef(ContentModel.ASSOC_CONTAINS, parent, generateQName(), child)) + .when(mockedNodeService) + .getPrimaryParent(child); + } + + /** + * Helper method to make a number of nodes children of another. + *

+ * Assumes the cm:contains assoc type. + * + * @param parent + * @param children + */ + protected void makeChildrenOf(NodeRef parent, NodeRef ... children) + { + List assocs = new ArrayList(children.length); + for (NodeRef child : children) + { + assocs.add(new ChildAssociationRef(ContentModel.ASSOC_CONTAINS, parent, generateQName(), child)); + } + + doReturn(assocs).when(mockedNodeService).getChildAssocs(parent, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + } + + /** + * Helper method to mock up calls to 'run as' methods + * on base service implementation. + * + * @param service + */ + @SuppressWarnings("unchecked") + protected void mockRunAsMethods(ServiceBaseImpl service) + { + doAnswer(new Answer() + { + @SuppressWarnings("rawtypes") + @Override + public Object answer(InvocationOnMock invocation) throws Throwable + { + RunAsWork work = (RunAsWork)invocation.getArguments()[0]; + return work.doWork(); + } + + }).when(service).runAsSystem(any(RunAsWork.class)); + + doAnswer(new Answer() + { + @SuppressWarnings("rawtypes") + @Override + public Object answer(InvocationOnMock invocation) throws Throwable + { + RunAsWork work = (RunAsWork)invocation.getArguments()[0]; + return work.doWork(); + } + + }).when(service).runAs(any(RunAsWork.class), anyString()); + } }