diff --git a/config/alfresco/node-services-context.xml b/config/alfresco/node-services-context.xml index eb447dd042..498db53a55 100644 --- a/config/alfresco/node-services-context.xml +++ b/config/alfresco/node-services-context.xml @@ -203,7 +203,10 @@ {http://www.alfresco.org/model/content/1.0}source - + + + + diff --git a/source/java/org/alfresco/repo/node/integrity/IncompleteNodeTagger.java b/source/java/org/alfresco/repo/node/integrity/IncompleteNodeTagger.java index 334c3a2b0f..33fce96e8d 100644 --- a/source/java/org/alfresco/repo/node/integrity/IncompleteNodeTagger.java +++ b/source/java/org/alfresco/repo/node/integrity/IncompleteNodeTagger.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 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% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 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.repo.node.integrity; import java.io.Serializable; @@ -37,6 +37,7 @@ import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; @@ -85,6 +86,7 @@ public class IncompleteNodeTagger private NodeService nodeService; private List storesToIgnore = new ArrayList(0); private Set propertiesToIgnore = new HashSet(); + private BehaviourFilter behaviourFilter; public IncompleteNodeTagger() { @@ -136,6 +138,11 @@ public class IncompleteNodeTagger } } + public void setBehaviourFilter(BehaviourFilter behaviourFilter) + { + this.behaviourFilter = behaviourFilter; + } + /** * Registers the system-level policy behaviours */ @@ -145,6 +152,7 @@ public class IncompleteNodeTagger PropertyCheck.mandatory("IncompleteNodeTagger", "dictionaryService", dictionaryService); PropertyCheck.mandatory("IncompleteNodeTagger", "nodeService", nodeService); PropertyCheck.mandatory("IncompleteNodeTagger", "policyComponent", policyComponent); + PropertyCheck.mandatory("IncompleteNodeTagger", "behaviourFilter", behaviourFilter); // register behaviour policyComponent.bindClassBehaviour( @@ -591,7 +599,19 @@ public class IncompleteNodeTagger { if (addTag && !isTagged) { - nodeService.addAspect(nodeRef, ContentModel.ASPECT_INCOMPLETE, null); + // MNT-17239: Unexpected changes of cm:modified and cm:modifier + behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); + behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); + try + { + nodeService.addAspect(nodeRef, ContentModel.ASPECT_INCOMPLETE, null); + } + finally + { + behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); + behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); + } + // done if (logger.isDebugEnabled()) { @@ -600,7 +620,19 @@ public class IncompleteNodeTagger } else if (!addTag && isTagged) { - nodeService.removeAspect(nodeRef, ContentModel.ASPECT_INCOMPLETE); + // MNT-17239: Unexpected changes of cm:modified and cm:modifier + behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); + behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); + try + { + nodeService.removeAspect(nodeRef, ContentModel.ASPECT_INCOMPLETE); + } + finally + { + behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); + behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); + } + // done if (logger.isDebugEnabled()) { diff --git a/source/test-java/org/alfresco/repo/node/integrity/IncompleteNodeTaggerTest.java b/source/test-java/org/alfresco/repo/node/integrity/IncompleteNodeTaggerTest.java index d77e4d0112..8a59cb1c0a 100644 --- a/source/test-java/org/alfresco/repo/node/integrity/IncompleteNodeTaggerTest.java +++ b/source/test-java/org/alfresco/repo/node/integrity/IncompleteNodeTaggerTest.java @@ -1,30 +1,34 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 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% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 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.repo.node.integrity; import java.io.InputStream; +import java.io.Serializable; +import java.util.Map; +import java.util.Set; +import java.util.UUID; import javax.transaction.UserTransaction; @@ -43,7 +47,6 @@ import org.alfresco.service.cmr.lock.LockType; 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.AuthenticationService; import org.alfresco.service.cmr.security.MutableAuthenticationService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; @@ -240,4 +243,88 @@ public class IncompleteNodeTaggerTest extends TestCase // Tag checkTagging(nodeRef, true); } + + /** + * Test for MNT-17239: Unexpected changes of cm:modified and cm:modifier + */ + public void testUnexpectedAuditUpdate() throws Exception + { + NodeRef nodeRef = createNode("abc", IntegrityTest.TEST_TYPE_WITH_PROPERTIES, null); + + checkTagging(nodeRef, true); + + // Now remove the aspect. + nodeService.removeAspect(nodeRef, ContentModel.ASPECT_INCOMPLETE); + + // Assert the node is not auditable. + Set aspects = nodeService.getAspects(nodeRef); + assertFalse(aspects.contains(ContentModel.ASPECT_AUDITABLE)); + + // Add auditable capability. + nodeService.addAspect(nodeRef, ContentModel.ASPECT_AUDITABLE, null); + + // Assert the node is now auditable. + aspects = nodeService.getAspects(nodeRef); + assertTrue(aspects.contains(ContentModel.ASPECT_AUDITABLE)); + + final Map props = nodeService.getProperties(nodeRef); + assertNotNull(props.get(ContentModel.PROP_CREATED)); + assertNotNull(props.get(ContentModel.PROP_MODIFIED)); + assertNotNull(props.get(ContentModel.PROP_CREATOR)); + assertNotNull(props.get(ContentModel.PROP_MODIFIER)); + + // Authenticate as someone else - someone not able to do anything + final String user = "user-" + UUID.randomUUID(); + RunAsWork createUserWork = new RunAsWork() + { + public Void doWork() throws Exception + { + if (!authenticationService.authenticationExists(user)) + { + authenticationService.createAuthentication(user, user.toCharArray()); + } + return null; + } + }; + AuthenticationUtil.runAs(createUserWork, AuthenticationUtil.getSystemUserName()); + authenticationComponent.setCurrentUser(user); + + // Tag + checkTagging(nodeRef, true); + + // Check that audit behavior wasn't triggered. + checkAuditableProperties(nodeRef, props); + } + + private void assertAuditableProperties(NodeRef nodeRef, Map checkProps) + { + assertTrue("Auditable aspect not present", nodeService.hasAspect(nodeRef, ContentModel.ASPECT_AUDITABLE)); + + Map props = nodeService.getProperties(nodeRef); + assertNotNull(props.get(ContentModel.PROP_CREATED)); + assertNotNull(props.get(ContentModel.PROP_MODIFIED)); + assertNotNull(props.get(ContentModel.PROP_CREATOR)); + assertNotNull(props.get(ContentModel.PROP_MODIFIER)); + if (checkProps != null) + { + assertEquals("PROP_CREATED not correct", checkProps.get(ContentModel.PROP_CREATED), props.get(ContentModel.PROP_CREATED)); + assertEquals("PROP_MODIFIED not correct", checkProps.get(ContentModel.PROP_MODIFIED), props.get(ContentModel.PROP_MODIFIED)); + assertEquals("PROP_CREATOR not correct", checkProps.get(ContentModel.PROP_CREATOR), props.get(ContentModel.PROP_CREATOR)); + assertEquals("PROP_MODIFIER not correct", checkProps.get(ContentModel.PROP_MODIFIER), props.get(ContentModel.PROP_MODIFIER)); + } + } + + private void checkAuditableProperties(NodeRef nodeRef, Map checkProps) + { + tagger.beforeCommit(false); + RunAsWork checkWork = new RunAsWork() + { + public Void doWork() throws Exception + { + assertAuditableProperties(nodeRef, checkProps); + return null; + } + }; + AuthenticationUtil.runAs(checkWork, AuthenticationUtil.getSystemUserName()); + } }