diff --git a/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/NodeValidator.java b/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/NodeValidator.java index 13a57370bc..c012f231ee 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/NodeValidator.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/NodeValidator.java @@ -33,12 +33,15 @@ import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.repo.rule.RuleModel; import org.alfresco.rest.api.Nodes; +import org.alfresco.rest.api.model.Node; import org.alfresco.rest.api.model.rules.RuleSet; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; import org.alfresco.rest.framework.core.exceptions.RelationshipResourceNotFoundException; import org.alfresco.service.Experimental; +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.rule.RuleService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; @@ -52,6 +55,7 @@ public class NodeValidator private Nodes nodes; private RuleService ruleService; private PermissionService permissionService; + private NodeService nodeService; /** * Validates if folder node exists and the user has permission to use it. @@ -65,20 +69,7 @@ public class NodeValidator public NodeRef validateFolderNode(final String folderNodeId, boolean requireChangePermission) { final NodeRef nodeRef = nodes.validateOrLookupNode(folderNodeId, null); - if (requireChangePermission) - { - if (permissionService.hasPermission(nodeRef, CHANGE_PERMISSIONS) != ALLOWED) - { - throw new PermissionDeniedException("Insufficient permissions to manage rules."); - } - } - else - { - if (permissionService.hasReadPermission(nodeRef) != ALLOWED) - { - throw new PermissionDeniedException("Cannot read from this node!"); - } - } + validatePermission(requireChangePermission, nodeRef); verifyNodeType(nodeRef, ContentModel.TYPE_FOLDER, null); return nodeRef; @@ -114,6 +105,15 @@ public class NodeValidator return ruleSetNodeRef; } + public NodeRef validateRuleSetNode(String linkToNodeId, boolean requireChangePermission) + { + final Node ruleSetNode = nodes.getNode(linkToNodeId); + final ChildAssociationRef primaryParent = nodeService.getPrimaryParent(ruleSetNode.getNodeRef()); + final NodeRef parentNode = primaryParent.getParentRef(); + validatePermission(requireChangePermission, parentNode); + return parentNode; + } + /** * Validates if rule node exists and associated rule set node matches. @@ -142,6 +142,24 @@ public class NodeValidator return nodeRef; } + private void validatePermission(boolean requireChangePermission, NodeRef nodeRef) + { + if (requireChangePermission) + { + if (permissionService.hasPermission(nodeRef, CHANGE_PERMISSIONS) != ALLOWED) + { + throw new PermissionDeniedException("Insufficient permissions to manage rules."); + } + } + else + { + if (permissionService.hasReadPermission(nodeRef) != ALLOWED) + { + throw new PermissionDeniedException("Cannot read from this node!"); + } + } + } + private void verifyNodeType(final NodeRef nodeRef, final QName expectedType, final String expectedTypeName) { final Set expectedTypes = Set.of(expectedType); @@ -152,6 +170,16 @@ public class NodeValidator } } + public boolean isRuleSetNode(String nodeId) { + try + { + validateNode(nodeId, ContentModel.TYPE_SYSTEM_FOLDER, RULE_SET_EXPECTED_TYPE_NAME); + return true; + } catch (InvalidArgumentException e) { + return false; + } + } + public boolean isRuleSetNotNullAndShared(final NodeRef ruleSetNodeRef, final NodeRef folderNodeRef) { if (ruleSetNodeRef == null && folderNodeRef != null) @@ -184,4 +212,9 @@ public class NodeValidator { this.nodes = nodes; } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } } diff --git a/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/RuleSetsImpl.java b/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/RuleSetsImpl.java index b5f2a5d367..4492dd7ed4 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/RuleSetsImpl.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/RuleSetsImpl.java @@ -81,7 +81,10 @@ public class RuleSetsImpl implements RuleSets { final NodeRef folderNodeRef = validator.validateFolderNode(folderNodeId,true); - final NodeRef linkToNodeRef = validator.validateFolderNode(linkToNodeId, true); + final boolean isRuleSetNode = validator.isRuleSetNode(linkToNodeId); + final NodeRef linkToNodeRef = isRuleSetNode + ? validator.validateRuleSetNode(linkToNodeId, true) + : validator.validateFolderNode(linkToNodeId, true); //The target node should have pre-existing rules to link to if (!ruleService.hasRules(linkToNodeRef)) { diff --git a/remote-api/src/main/resources/alfresco/public-rest-context.xml b/remote-api/src/main/resources/alfresco/public-rest-context.xml index f00f9bf063..59fc8c936b 100644 --- a/remote-api/src/main/resources/alfresco/public-rest-context.xml +++ b/remote-api/src/main/resources/alfresco/public-rest-context.xml @@ -854,6 +854,7 @@ + diff --git a/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/NodeValidatorTest.java b/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/NodeValidatorTest.java index 770c9840f6..f71f32156e 100644 --- a/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/NodeValidatorTest.java +++ b/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/NodeValidatorTest.java @@ -39,18 +39,24 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.reset; import java.util.Set; +import org.alfresco.model.ContentModel; import org.alfresco.rest.api.Nodes; +import org.alfresco.rest.api.model.Node; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; import org.alfresco.rest.framework.core.exceptions.RelationshipResourceNotFoundException; +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.rule.RuleService; import org.alfresco.service.cmr.security.PermissionService; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; @@ -61,21 +67,33 @@ public class NodeValidatorTest { private static final String FOLDER_NODE_ID = "dummy-folder-node-id"; + private static final String LINK_TO_NODE_ID = "dummy-link-to-node-id"; private static final String RULE_SET_ID = "dummy-rule-set-id"; private static final String RULE_ID = "dummy-rule-id"; + private static final String PARENT_NODE_ID = "dummy-parent-node-id"; private static final NodeRef folderNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, FOLDER_NODE_ID); private static final NodeRef ruleSetNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, RULE_SET_ID); private static final NodeRef ruleNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, RULE_ID); + private static final NodeRef parentNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_NODE_ID); @Mock private Nodes nodesMock; + @Mock + private Node ruleSetNodeMock; + @Mock private PermissionService permissionServiceMock; @Mock private RuleService ruleServiceMock; + @Mock + private NodeService nodeServiceMock; + + @Mock + private ChildAssociationRef primaryParentMock; + @InjectMocks private NodeValidator nodeValidator; @@ -182,6 +200,20 @@ public class NodeValidatorTest assertThat(nodeRef).isNotNull().isEqualTo(ruleSetNodeRef); } + @Test + public void testValidateRuleSetNodeNoParentId() + { + given(nodesMock.getNode(any())).willReturn(ruleSetNodeMock); + given(nodeServiceMock.getPrimaryParent(any())).willReturn(primaryParentMock); + given(primaryParentMock.getParentRef()).willReturn(parentNodeRef); + + //when + final NodeRef nodeRef = nodeValidator.validateRuleSetNode(LINK_TO_NODE_ID,true); + + assertThat(nodeRef).isNotNull().isEqualTo(parentNodeRef); + + } + @Test public void validateRuleSetNode_defaultId() { @@ -329,6 +361,28 @@ public class NodeValidatorTest then(ruleServiceMock).shouldHaveNoMoreInteractions(); } + @Test + public void testIsRuleSetNode() + { + //resetting mock to bypass setup method + resetNodesMock(); + + boolean actual = nodeValidator.isRuleSetNode(RULE_SET_ID); + Assert.assertTrue(actual); + } + + + @Test + public void testIsNotRuleSetNode() + { + //resetting mock to bypass setup method + resetNodesMock(); + + //using an id that doesn't belong to a ruleset node + boolean actual = nodeValidator.isRuleSetNode(FOLDER_NODE_ID); + Assert.assertFalse(actual); + } + @Test public void testIsRuleSetNotNullAndShared() { @@ -376,4 +430,12 @@ public class NodeValidatorTest then(ruleServiceMock).should().isRuleSetShared(ruleSetNodeRef); then(ruleServiceMock).shouldHaveNoMoreInteractions(); } + + private void resetNodesMock() { + reset(nodesMock); + given(nodesMock.validateOrLookupNode(eq(FOLDER_NODE_ID), any())).willReturn(folderNodeRef); + given(nodesMock.validateNode(RULE_SET_ID)).willReturn(ruleSetNodeRef); + given(nodesMock.validateNode(RULE_ID)).willReturn(ruleNodeRef); + given(nodesMock.nodeMatches(ruleSetNodeRef, Set.of(ContentModel.TYPE_SYSTEM_FOLDER), null)).willReturn(true); + } } \ No newline at end of file diff --git a/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/RuleSetsImplTest.java b/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/RuleSetsImplTest.java index 16094b1aa1..d52f5f2d52 100644 --- a/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/RuleSetsImplTest.java +++ b/remote-api/src/test/java/org/alfresco/rest/api/impl/rules/RuleSetsImplTest.java @@ -96,6 +96,7 @@ public class RuleSetsImplTest extends TestCase MockitoAnnotations.openMocks(this); given(nodeValidatorMock.validateFolderNode(eq(LINK_TO_NODE_ID), anyBoolean())).willReturn(LINK_TO_NODE); + given(nodeValidatorMock.validateRuleSetNode(LINK_TO_NODE_ID,true)).willReturn(LINK_TO_NODE); given(nodeValidatorMock.validateFolderNode(eq(FOLDER_ID), anyBoolean())).willReturn(FOLDER_NODE); given(nodeValidatorMock.validateRuleSetNode(RULE_SET_ID, FOLDER_NODE)).willReturn(RULE_SET_NODE); @@ -153,7 +154,7 @@ public class RuleSetsImplTest extends TestCase } @Test - public void testLinkingToRuleSet() + public void testLinkToFolderRuleSet() { NodeRef childNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "dummy-child-id"); @@ -162,7 +163,7 @@ public class RuleSetsImplTest extends TestCase given(assocRef.getChildRef()).willReturn(childNodeRef); //when - assertEquals(ruleSets.linkToRuleSet(FOLDER_ID,LINK_TO_NODE_ID).getId(), childNodeRef.getId()); + String actual = ruleSets.linkToRuleSet(FOLDER_ID,LINK_TO_NODE_ID).getId(); then(ruleServiceMock).should().hasRules(LINK_TO_NODE); then(ruleServiceMock).should().hasRules(FOLDER_NODE); @@ -170,6 +171,35 @@ public class RuleSetsImplTest extends TestCase then(runtimeRuleServiceMock).shouldHaveNoMoreInteractions(); then(nodeServiceMock).should().addChild(FOLDER_NODE, childNodeRef, RuleModel.ASSOC_RULE_FOLDER, RuleModel.ASSOC_RULE_FOLDER); then(nodeServiceMock).shouldHaveNoMoreInteractions(); + + assertEquals(childNodeRef.getId(),actual); + } + + @Test + public void testLinkToRuleSet() + { + NodeRef childNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "dummy-child-id"); + + given(nodeValidatorMock.isRuleSetNode(any())).willReturn(true); + given(ruleServiceMock.hasRules(any(NodeRef.class))).willReturn(true, false); + given(runtimeRuleServiceMock.getSavedRuleFolderAssoc(any(NodeRef.class))).willReturn(assocRef); + given(assocRef.getChildRef()).willReturn(childNodeRef); + + //when + String actual = ruleSets.linkToRuleSet(FOLDER_ID,LINK_TO_NODE_ID).getId(); + + then(nodeValidatorMock).should().validateFolderNode(FOLDER_ID,true); + then(nodeValidatorMock).should().isRuleSetNode(LINK_TO_NODE_ID); + then(nodeValidatorMock).should().validateRuleSetNode(LINK_TO_NODE_ID,true); + then(nodeValidatorMock).shouldHaveNoMoreInteractions(); + then(ruleServiceMock).should().hasRules(LINK_TO_NODE); + then(ruleServiceMock).should().hasRules(FOLDER_NODE); + then(runtimeRuleServiceMock).should().getSavedRuleFolderAssoc(LINK_TO_NODE); + then(runtimeRuleServiceMock).shouldHaveNoMoreInteractions(); + then(nodeServiceMock).should().addChild(FOLDER_NODE, childNodeRef, RuleModel.ASSOC_RULE_FOLDER, RuleModel.ASSOC_RULE_FOLDER); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + + assertEquals(childNodeRef.getId(),actual); } @Test