diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/hold-service.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/hold-service.properties index ae21a7ddb8..6e3cc58d4f 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/hold-service.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/hold-service.properties @@ -1,4 +1,8 @@ rm.hold.not-hold=The node {0} is not a hold. rm.hold.add-to-hold-invalid-type={0} is neither a record nor a record folder nor active content. Only records, record \ folders or active content can be added to a hold. -rm.hold.add-to-hold-archived-node=Archived nodes can't be added to hold. \ No newline at end of file +rm.hold.add-to-hold-archived-node=Archived nodes can't be added to hold. +rm.hold.delete-frozen-node=Frozen nodes can not be deleted. +rm.hold.delete-node-frozen-children=Can not delete node, because it contains a frozen child node. +rm.hold.move-frozen-node=Frozen nodes can not be moved. +rm.hold.update-frozen-node=Frozen nodes can not be updated. \ No newline at end of file diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml index a52089bd08..07adb5d9bf 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml @@ -168,7 +168,6 @@ - diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/hold/HoldServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/hold/HoldServiceImpl.java index 93ca463d99..c4da746aa2 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/hold/HoldServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/hold/HoldServiceImpl.java @@ -202,6 +202,8 @@ public class HoldServiceImpl extends ServiceBaseImpl List frozenNodes = getHeld(hold); for (NodeRef frozenNode : frozenNodes) { + //set in transaction cache in order not to trigger update policy when removing the child association + transactionalResourceHelper.getSet("frozen").add(frozenNode); removeFreezeAspect(frozenNode, 1); } @@ -228,6 +230,8 @@ public class HoldServiceImpl extends ServiceBaseImpl if (nodeService.hasAspect(nodeRef, ASPECT_FROZEN)) { // remove the freeze aspect from the node + //set in transaction cache in order not to trigger update policy when removing the aspect + transactionalResourceHelper.getSet("frozen").add(nodeRef); nodeService.removeAspect(nodeRef, ASPECT_FROZEN); } @@ -242,6 +246,8 @@ public class HoldServiceImpl extends ServiceBaseImpl if (recordsOtherHolds.size() == index) { // remove the freeze aspect from the node + //set in transaction cache in order not to trigger update policy when removing the aspect + transactionalResourceHelper.getSet("frozen").add(record); nodeService.removeAspect(record, ASPECT_FROZEN); } } @@ -636,6 +642,8 @@ public class HoldServiceImpl extends ServiceBaseImpl { if (!nodeService.hasAspect(nodeRef, ASPECT_FROZEN)) { + //set in transaction cache in order not to trigger update policy when adding the aspect + transactionalResourceHelper.getSet("frozen").add(nodeRef); // add freeze aspect nodeService.addAspect(nodeRef, ASPECT_FROZEN, props); @@ -714,33 +722,26 @@ public class HoldServiceImpl extends ServiceBaseImpl { // run as system so we don't run into further permission issues // we already know we have to have the correct capability to get here - authenticationUtil.runAsSystem(new RunAsWork() - { - @Override - public Void doWork() - { - // remove from hold - nodeService.removeChild(hold, nodeRef); + authenticationUtil.runAsSystem((RunAsWork) () -> { + // remove from hold + //set in transaction cache in order not to trigger update policy when removing the child association + transactionalResourceHelper.getSet("frozen").add(nodeRef); + nodeService.removeChild(hold, nodeRef); - // audit that the node has been remove from the hold - // TODO add details of the hold that the node was removed from - recordsManagementAuditService.auditEvent(nodeRef, AUDIT_REMOVE_FROM_HOLD); + // audit that the node has been remove from the hold + // TODO add details of the hold that the node was removed from + recordsManagementAuditService.auditEvent(nodeRef, AUDIT_REMOVE_FROM_HOLD); - return null; - } - }); + return null; + + }); } } // run as system as we can't be sure if have remove aspect rights on node - authenticationUtil.runAsSystem(new RunAsWork() - { - @Override - public Void doWork() - { - removeFreezeAspect(nodeRef, 0); - return null; - } + authenticationUtil.runAsSystem((RunAsWork) () -> { + removeFreezeAspect(nodeRef, 0); + return null; }); } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/FrozenAspect.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/FrozenAspect.java index c3dcc1f4fb..18cbd5f544 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/FrozenAspect.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/FrozenAspect.java @@ -36,7 +36,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; import org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService; import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; import org.alfresco.repo.node.NodeServicePolicies; @@ -46,10 +45,11 @@ import org.alfresco.repo.policy.annotation.BehaviourBean; import org.alfresco.repo.policy.annotation.BehaviourKind; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; -import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; +import org.springframework.extensions.surf.util.I18NUtil; /** * rma:frozen behaviour bean @@ -64,22 +64,13 @@ import org.alfresco.service.namespace.QName; public class FrozenAspect extends BaseBehaviourBean implements NodeServicePolicies.BeforeDeleteNodePolicy, NodeServicePolicies.OnAddAspectPolicy, - NodeServicePolicies.OnRemoveAspectPolicy + NodeServicePolicies.OnRemoveAspectPolicy, + NodeServicePolicies.OnUpdatePropertiesPolicy, + NodeServicePolicies.BeforeMoveNodePolicy { - /** file plan service */ - protected FilePlanService filePlanService; - /** freeze service */ protected FreezeService freezeService; - /** - * @param filePlanService file plan service - */ - public void setFilePlanService(FilePlanService filePlanService) - { - this.filePlanService = filePlanService; - } - /** * @param freezeService freeze service */ @@ -101,25 +92,16 @@ public class FrozenAspect extends BaseBehaviourBean ) public void beforeDeleteNode(final NodeRef nodeRef) { - AuthenticationUtil.runAsSystem(new RunAsWork() - { - @Override - public Void doWork() + AuthenticationUtil.runAsSystem((RunAsWork) () -> { + if (nodeService.exists(nodeRef) && freezeService.isFrozen(nodeRef)) { - if (nodeService.exists(nodeRef) && - filePlanService.isFilePlanComponent(nodeRef)) - { - if (freezeService.isFrozen(nodeRef)) - { - // never allowed to delete a frozen node - throw new AccessDeniedException("Frozen nodes can not be deleted."); - } - - // check children - checkChildren(nodeService.getChildAssocs(nodeRef)); - } - return null; + // never allow to delete a frozen node + throw new PermissionDeniedException(I18NUtil.getMessage("rm.hold.delete-frozen-node")); } + + // check children + checkChildren(nodeService.getChildAssocs(nodeRef)); + return null; }); } @@ -139,8 +121,8 @@ public class FrozenAspect extends BaseBehaviourBean NodeRef nodeRef = assoc.getChildRef(); if (freezeService.isFrozen(nodeRef)) { - // never allowed to delete a node with a frozen child - throw new AccessDeniedException("Can not delete node, because it contains a frozen child node."); + // never allow to delete a node with a frozen child + throw new PermissionDeniedException(I18NUtil.getMessage("rm.hold.delete-node-frozen-children")); } // check children @@ -192,33 +174,74 @@ public class FrozenAspect extends BaseBehaviourBean ) public void onRemoveAspect(final NodeRef record, QName aspectTypeQName) { - AuthenticationUtil.runAsSystem(new RunAsWork() - { - @Override - public Void doWork() - { - if (nodeService.exists(record) && + AuthenticationUtil.runAsSystem((RunAsWork) () -> { + if (nodeService.exists(record) && isRecord(record)) + { + // get the owning record folder + NodeRef recordFolder = nodeService.getPrimaryParent(record).getParentRef(); + + // check that the aspect has been added + if (nodeService.hasAspect(recordFolder, ASPECT_HELD_CHILDREN)) { - // get the owning record folder - NodeRef recordFolder = nodeService.getPrimaryParent(record).getParentRef(); - - // check that the aspect has been added - if (nodeService.hasAspect(recordFolder, ASPECT_HELD_CHILDREN)) + // decrement current count + int currentCount = (Integer) nodeService.getProperty(recordFolder, PROP_HELD_CHILDREN_COUNT); + if (currentCount > 0) { - // decrement current count - int currentCount = (Integer)nodeService.getProperty(recordFolder, PROP_HELD_CHILDREN_COUNT); - if (currentCount > 0) - { - currentCount = currentCount - 1; - nodeService.setProperty(recordFolder, PROP_HELD_CHILDREN_COUNT, currentCount); - } - } + currentCount = currentCount - 1; + nodeService.setProperty(recordFolder, PROP_HELD_CHILDREN_COUNT, currentCount); + } } - return null; } - }); + return null; + }); } + /** + * Behaviour associated with moving a frozen node + *

+ * Prevent frozen items being moved + */ + @Override + @Behaviour + ( + kind = BehaviourKind.CLASS, + notificationFrequency = NotificationFrequency.FIRST_EVENT + ) + public void beforeMoveNode(ChildAssociationRef oldChildAssocRef, NodeRef newParentRef) + { + AuthenticationUtil.runAsSystem((RunAsWork) () -> { + if (nodeService.exists(oldChildAssocRef.getChildRef()) && + freezeService.isFrozen(oldChildAssocRef.getChildRef())) + { + throw new PermissionDeniedException(I18NUtil.getMessage("rm.hold.move-frozen-node")); + } + return null; + }); + } + + /** + * Behaviour associated with updating properties + *

+ * Prevents frozen items being updated + */ + @Override + @Behaviour + ( + kind = BehaviourKind.CLASS, + notificationFrequency = NotificationFrequency.FIRST_EVENT + ) + public void onUpdateProperties(NodeRef nodeRef, Map before, Map after) + { + AuthenticationUtil.runAsSystem((RunAsWork) () -> { + // check to not throw exception when the aspect is being added + if (nodeService.exists(nodeRef) && freezeService.isFrozen(nodeRef) && + !transactionalResourceHelper.getSet("frozen").contains(nodeRef) ) + { + throw new PermissionDeniedException(I18NUtil.getMessage("rm.hold.update-frozen-node")); + } + return null; + }); + } } diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/hold/UpdateHeldActiveContentTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/hold/UpdateHeldActiveContentTest.java new file mode 100644 index 0000000000..a50c65a3b8 --- /dev/null +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/hold/UpdateHeldActiveContentTest.java @@ -0,0 +1,212 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.test.integration.hold; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; +import org.alfresco.service.cmr.model.FileNotFoundException; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.NodeRef; +import org.springframework.extensions.webscripts.GUID; + +/** + * Prevent Updating Held Active Content Integration Tests + * + * @author Claudia Agache + * @since 3.2 + */ +public class UpdateHeldActiveContentTest extends BaseRMTestCase +{ + @Override + protected boolean isCollaborationSiteTest() + { + return true; + } + + /** + * Given active content on hold + * When I try to delete the content + * Then I am not successful + */ + public void testDeleteHeldDocument() + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + public void given() + { + // create a hold + NodeRef hold = holdService.createHold(filePlan, GUID.generate(), GUID.generate(), GUID.generate()); + + // add the active content to hold + holdService.addToHold(hold, dmDocument); + } + + public void when() + { + try + { + fileFolderService.delete(dmDocument); + fail("Expected PermissionDeniedException to be thrown"); + } + catch (PermissionDeniedException pde) + { + assertTrue(pde.getMessage().contains("Frozen nodes can not be deleted.")); + } + } + }); + } + + /** + * Given active content on hold + * When I try to copy the content + * Then I am not successful + */ + public void testCopyHeldDocument() + { + doBehaviourDrivenTest(new BehaviourDrivenTest(AccessDeniedException.class) + { + public void given() + { + // create a hold + NodeRef hold = holdService.createHold(filePlan, GUID.generate(), GUID.generate(), GUID.generate()); + + // add the active content to hold + holdService.addToHold(hold, dmDocument); + } + + public void when() throws FileNotFoundException + { + fileFolderService.copy(dmDocument, dmFolder1, null); + } + }); + } + + /** + * Given active content on hold + * When I try to move the content + * Then I am not successful + */ + public void testMoveHeldDocument() + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + public void given() + { + // create a hold + NodeRef hold = holdService.createHold(filePlan, GUID.generate(), GUID.generate(), GUID.generate()); + + // add the active content to hold + holdService.addToHold(hold, dmDocument); + } + + public void when() throws FileNotFoundException + { + try + { + fileFolderService.move(dmDocument, dmFolder1, null); + fail("Expected PermissionDeniedException to be thrown"); + } + catch (PermissionDeniedException pde) + { + assertTrue(pde.getMessage().contains("Frozen nodes can not be moved.")); + } + } + }); + } + + /** + * Given active content on hold + * When I try to edit the properties + * Or perform an action that edits the properties + * Then I am not successful + */ + public void testUpdateHeldDocumentProperties() + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + public void given() + { + // create a hold + NodeRef hold = holdService.createHold(filePlan, GUID.generate(), GUID.generate(), GUID.generate()); + + // add the active content to hold + holdService.addToHold(hold, dmDocument); + } + + public void when() + { + try + { + nodeService.setProperty(dmDocument, ContentModel.PROP_DESCRIPTION, "description"); + fail("Expected PermissionDeniedException to be thrown"); + } + catch (PermissionDeniedException pde) + { + assertTrue(pde.getMessage().contains("Frozen nodes can not be updated.")); + } + } + }); + } + + /** + * Given active content on hold + * When I try to update the content + * Then I am not successful + */ + public void testUpdateHeldDocumentContent() + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + public void given() + { + // create a hold + NodeRef hold = holdService.createHold(filePlan, GUID.generate(), GUID.generate(), GUID.generate()); + + // add the active content to hold + holdService.addToHold(hold, dmDocument); + } + + public void when() + { + try + { + ContentData content = (ContentData) nodeService.getProperty(dmDocument, PROP_CONTENT); + nodeService.setProperty(dmDocument, PROP_CONTENT, ContentData.setMimetype(content, + MimetypeMap.MIMETYPE_TEXT_PLAIN)); + fail("Expected PermissionDeniedException to be thrown"); + } + catch (PermissionDeniedException pde) + { + assertTrue(pde.getMessage().contains("Frozen nodes can not be updated.")); + } + } + }); + } +}