ACS-3363 Support inheritedBy in GET rule sets. (#1387)

* ACS-3363 E2E test for inheritedBy.

* ACS-3363 Support optional inheritedBy field in GET rule sets.

* ACS-3363 Update to new version of TAS REST API.

* ACS-3363 Remove user from private site before calling method under test.
This commit is contained in:
Tom Page
2022-09-16 11:06:17 +01:00
committed by GitHub
parent e66263a5a8
commit ac1a77156e
9 changed files with 355 additions and 21 deletions

View File

@@ -25,17 +25,26 @@
*/
package org.alfresco.rest.rules;
import static org.alfresco.rest.requests.RuleSettings.IS_INHERITANCE_ENABLED;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModel;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithModifiedValues;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.junit.Assert.assertTrue;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.OK;
import java.util.List;
import com.google.common.collect.ImmutableMap;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestRuleModel;
import org.alfresco.rest.model.RestRuleSetLinkModel;
import org.alfresco.rest.model.RestRuleSetModel;
import org.alfresco.rest.model.RestRuleSetModelsCollection;
import org.alfresco.rest.model.RestRuleSettingsModel;
import org.alfresco.rest.requests.coreAPI.RestCoreAPI;
import org.alfresco.utility.constants.UserRole;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.TestGroup;
@@ -248,4 +257,78 @@ public class GetRuleSetsTests extends RestTest
ruleSet.assertThat().field("owningFolder").is(ruleFolder.getNodeRef())
.assertThat().field("id").is(ruleSetId);
}
/**
* Check we can find out the id of any folders that inherit a rule set.
* <p>
* The test checks several different situations:
* <pre>
* folder --[owns]-> rule set
* +- publicFolder --[inherits]-> rule set (user has access)
* +- privateFolder --[inherits]-> rule set (user does not have access)
* +- publicGrandchild --[inherits]-> rule set (user has access again)
* +- nonInheritingFolder (inheritance should be prevented)
* +- linkingFolder --[links]-> rule set (not inherited)
* +- descendantFolder --[inherits]-> rule set (inherited via link)
* </pre>
*/
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
public void getRuleSetAndInheritedBy()
{
STEP("Create a site owned by admin and add user as a contributor");
SiteModel siteModel = dataSite.usingAdmin().createPrivateRandomSite();
dataUser.addUserToSite(user, siteModel, UserRole.SiteContributor);
STEP("Create the folder structure");
FolderModel folder = dataContent.usingUser(user).usingSite(siteModel).createFolder();
FolderModel publicFolder = dataContent.usingUser(user).usingResource(folder).createFolder();
FolderModel privateFolder = dataContent.usingAdmin().usingResource(folder).createFolder();
dataContent.usingAdmin().usingResource(privateFolder).setInheritPermissions(false);
// Create the grandchild with user and use admin to move it under the private folder.
FolderModel publicGrandchild = dataContent.usingUser(user).usingSite(siteModel).createFolder();
coreAPIForAdmin().usingActions().executeAction("move", publicGrandchild, ImmutableMap.of("destination-folder", "workspace://SpacesStore/" + privateFolder.getNodeRef()));
// Create the non-inheriting folder.
FolderModel nonInheritingFolder = dataContent.usingUser(user).usingResource(folder).createFolder();
RestRuleSettingsModel nonInheriting = new RestRuleSettingsModel();
nonInheriting.setKey(IS_INHERITANCE_ENABLED);
nonInheriting.setValue(false);
coreAPIForUser().usingNode(nonInheritingFolder).usingIsInheritanceEnabledRuleSetting().updateSetting(nonInheriting);
// Create a child that will link to the rule and a child of that to inherit via the link.
FolderModel linkingFolder = dataContent.usingUser(user).usingResource(nonInheritingFolder).createFolder();
FolderModel descendantFolder = dataContent.usingUser(user).usingResource(linkingFolder).createFolder();
STEP("Create an inheritable rule in the folder and get the rule set id.");
RestRuleModel ruleModel = createRuleModelWithModifiedValues();
coreAPIForUser().usingNode(folder).usingDefaultRuleSet().createSingleRule(ruleModel);
RestRuleSetModelsCollection ruleSets = coreAPIForUser().usingNode(folder).getListOfRuleSets();
String ruleSetId = ruleSets.getEntries().get(0).onModel().getId();
STEP("Create the link to the rule from the linking folder");
RestRuleSetLinkModel ruleSetLink = new RestRuleSetLinkModel();
ruleSetLink.setId(folder.getNodeRef());
coreAPIForUser().usingNode(linkingFolder).createRuleLink(ruleSetLink);
STEP("Remove the user from the site");
dataUser.removeUserFromSite(user, siteModel);
STEP("Get the rule set and inheriting folders");
RestRuleSetModel ruleSet = coreAPIForUser().usingNode(folder)
.include("inheritedBy")
.getRuleSet(ruleSetId);
restClient.assertStatusCodeIs(OK);
List<String> expectedInheritors = List.of(publicFolder.getNodeRef(), descendantFolder.getNodeRef(), publicGrandchild.getNodeRef());
ruleSet.assertThat().field("inheritedBy").is(expectedInheritors)
.assertThat().field("id").is(ruleSetId);
}
private RestCoreAPI coreAPIForUser()
{
return restClient.authenticateUser(user).withCoreAPI();
}
private RestCoreAPI coreAPIForAdmin()
{
return restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI();
}
}

View File

@@ -123,7 +123,7 @@
<dependency.mariadb.version>2.7.4</dependency.mariadb.version>
<dependency.tas-utility.version>3.0.49</dependency.tas-utility.version>
<dependency.rest-assured.version>5.1.1</dependency.rest-assured.version>
<dependency.tas-restapi.version>1.115</dependency.tas-restapi.version>
<dependency.tas-restapi.version>1.117</dependency.tas-restapi.version>
<dependency.tas-cmis.version>1.32</dependency.tas-cmis.version>
<dependency.tas-email.version>1.9</dependency.tas-email.version>
<dependency.tas-webdav.version>1.7</dependency.tas-webdav.version>

View File

@@ -36,6 +36,7 @@ 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;
/** Responsible for converting a NodeRef into a {@link RuleSet} object. */
@Experimental
@@ -43,7 +44,10 @@ public class RuleSetLoader
{
protected static final String OWNING_FOLDER = "owningFolder";
protected static final String INCLUSION_TYPE = "inclusionType";
protected static final String INHERITED_BY = "inheritedBy";
public static final int MAX_INHERITED_BY_SIZE = 100;
private NodeService nodeService;
private RuleService ruleService;
/**
* Load a rule set for the given node ref.
@@ -79,12 +83,26 @@ public class RuleSetLoader
ruleSet.setInclusionType(linked ? LINKED : INHERITED);
}
}
if (includes.contains(INHERITED_BY))
{
ruleSet.setInheritedBy(loadInheritedBy(ruleSetNodeRef));
}
}
return ruleSet;
}
private List<NodeRef> loadInheritedBy(NodeRef ruleSetNodeRef)
{
return ruleService.getFoldersInheritingRuleSet(ruleSetNodeRef, MAX_INHERITED_BY_SIZE);
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setRuleService(RuleService ruleService)
{
this.ruleService = ruleService;
}
}

View File

@@ -26,6 +26,7 @@
package org.alfresco.rest.api.model.rules;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
@@ -40,6 +41,7 @@ public class RuleSet
private String id;
private NodeRef owningFolder;
private InclusionType inclusionType;
private List<NodeRef> inheritedBy;
public static RuleSet of(String id)
{
@@ -86,6 +88,16 @@ public class RuleSet
this.inclusionType = inclusionType;
}
public List<NodeRef> getInheritedBy()
{
return inheritedBy;
}
public void setInheritedBy(List<NodeRef> inheritedBy)
{
this.inheritedBy = inheritedBy;
}
@Override
public String toString()
{
@@ -94,6 +106,7 @@ public class RuleSet
.add("id='" + id + "'")
.add("owningFolder='" + owningFolder + "'")
.add("inclusionType='" + inclusionType + "'")
.add("inheritedBy='" + inheritedBy + "'")
.toString()
+ '}';
}
@@ -108,13 +121,14 @@ public class RuleSet
RuleSet ruleSet = (RuleSet) o;
return Objects.equals(id, ruleSet.id)
&& Objects.equals(owningFolder, ruleSet.owningFolder)
&& inclusionType == ruleSet.inclusionType;
&& inclusionType == ruleSet.inclusionType
&& Objects.equals(inheritedBy, ruleSet.inheritedBy);
}
@Override
public int hashCode()
{
return Objects.hash(id, owningFolder, inclusionType);
return Objects.hash(id, owningFolder, inclusionType, inheritedBy);
}
public static Builder builder()
@@ -127,6 +141,7 @@ public class RuleSet
private String id;
private NodeRef owningFolder;
private InclusionType inclusionType;
private List<NodeRef> inheritedBy;
public Builder id(String id)
{
@@ -146,12 +161,19 @@ public class RuleSet
return this;
}
public Builder inheritedBy(List<NodeRef> inheritedBy)
{
this.inheritedBy = inheritedBy;
return this;
}
public RuleSet create()
{
final RuleSet ruleSet = new RuleSet();
ruleSet.setId(id);
ruleSet.setOwningFolder(owningFolder);
ruleSet.setInclusionType(inclusionType);
ruleSet.setInheritedBy(inheritedBy);
return ruleSet;
}
}

View File

@@ -863,6 +863,7 @@
<bean id="ruleSetLoader" class="org.alfresco.rest.api.impl.rules.RuleSetLoader">
<property name="nodeService" ref="NodeService" />
<property name="ruleService" ref="RuleService" />
</bean>
<bean id="ruleSets" class="org.alfresco.rest.api.impl.rules.RuleSetsImpl">

View File

@@ -26,11 +26,14 @@
package org.alfresco.rest.api.impl.rules;
import static org.alfresco.rest.api.impl.rules.RuleSetLoader.INCLUSION_TYPE;
import static org.alfresco.rest.api.impl.rules.RuleSetLoader.INHERITED_BY;
import static org.alfresco.rest.api.impl.rules.RuleSetLoader.OWNING_FOLDER;
import static org.alfresco.rest.api.model.rules.InclusionType.INHERITED;
import static org.alfresco.rest.api.model.rules.InclusionType.LINKED;
import static org.alfresco.rest.api.model.rules.InclusionType.OWNED;
import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import java.util.List;
@@ -41,6 +44,7 @@ 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.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,6 +70,8 @@ public class RuleSetLoaderTest extends TestCase
@Mock
private NodeService nodeServiceMock;
@Mock
private RuleService ruleServiceMock;
@Mock
private ChildAssociationRef ruleSetAssociationMock;
@Mock
private ChildAssociationRef linkAssociationMock;
@@ -79,6 +85,8 @@ public class RuleSetLoaderTest extends TestCase
given(linkAssociationMock.getParentRef()).willReturn(LINKING_FOLDER);
given(nodeServiceMock.getParentAssocs(RULE_SET_NODE)).willReturn(List.of(ruleSetAssociationMock, linkAssociationMock));
given(ruleServiceMock.getFoldersInheritingRuleSet(eq(RULE_SET_NODE), anyInt())).willReturn(List.of(INHERITING_FOLDER));
}
@Test
@@ -130,4 +138,14 @@ public class RuleSetLoaderTest extends TestCase
RuleSet expected = RuleSet.builder().id(RULE_SET_ID).inclusionType(INHERITED).create();
assertEquals(expected, actual);
}
@Test
public void testLoadRuleSet_inheritedBy()
{
// Call the method under test.
RuleSet actual = ruleSetLoader.loadRuleSet(RULE_SET_NODE, INHERITING_FOLDER, List.of(INHERITED_BY));
RuleSet expected = RuleSet.builder().id(RULE_SET_ID).inheritedBy(List.of(INHERITING_FOLDER)).create();
assertEquals(expected, actual);
}
}

View File

@@ -25,14 +25,19 @@
*/
package org.alfresco.repo.rule;
import static org.alfresco.model.ContentModel.ASSOC_CONTAINS;
import static org.alfresco.repo.rule.RuleModel.ASPECT_IGNORE_INHERITED_RULES;
import static org.alfresco.repo.rule.RuleModel.ASSOC_RULE_FOLDER;
import static org.alfresco.service.cmr.security.AccessStatus.ALLOWED;
import static org.alfresco.service.namespace.RegexQNamePattern.MATCH_ALL;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -70,7 +75,6 @@ import org.alfresco.service.cmr.rule.Rule;
import org.alfresco.service.cmr.rule.RuleService;
import org.alfresco.service.cmr.rule.RuleServiceException;
import org.alfresco.service.cmr.rule.RuleType;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.QNamePattern;
@@ -520,7 +524,7 @@ public class RuleServiceImpl
// https://issues.alfresco.com/browse/ETWOTWO-438
if (!runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) ||
permissionService.hasPermission(nodeRef, PermissionService.READ) != AccessStatus.ALLOWED)
permissionService.hasPermission(nodeRef, PermissionService.READ) != ALLOWED)
{
// Doesn't have the aspect or the user doesn't have access
return Collections.emptyList();
@@ -538,7 +542,7 @@ public class RuleServiceImpl
{
// Get the rules for this node
List<ChildAssociationRef> ruleChildAssocRefs =
this.runtimeNodeService.getChildAssocs(ruleFolder, RegexQNamePattern.MATCH_ALL, ASSOC_NAME_RULES_REGEX);
this.runtimeNodeService.getChildAssocs(ruleFolder, MATCH_ALL, ASSOC_NAME_RULES_REGEX);
for (ChildAssociationRef ruleChildAssocRef : ruleChildAssocRefs)
{
// Create the rule and add to the list
@@ -567,7 +571,7 @@ public class RuleServiceImpl
{
// Get the rules for this node
List<ChildAssociationRef> ruleChildAssocRefs =
this.runtimeNodeService.getChildAssocs(ruleFolder, RegexQNamePattern.MATCH_ALL, ASSOC_NAME_RULES_REGEX);
this.runtimeNodeService.getChildAssocs(ruleFolder, MATCH_ALL, ASSOC_NAME_RULES_REGEX);
ruleCount = ruleChildAssocRefs.size();
}
@@ -647,6 +651,44 @@ public class RuleServiceImpl
return returnList;
}
/** {@inheritDoc} */
@Override
@Experimental
public List<NodeRef> getFoldersInheritingRuleSet(NodeRef ruleSet, int maxFoldersToReturn)
{
// Seed stack with all folders owning or linking to the rule set.
Deque<NodeRef> stack = new LinkedList<>();
for (ChildAssociationRef parentAssociation : runtimeNodeService.getParentAssocs(ruleSet))
{
stack.add(parentAssociation.getParentRef());
}
// Process child folders to find all that inherit the rules.
List<NodeRef> inheritors = new ArrayList<>();
while (!stack.isEmpty() && inheritors.size() < maxFoldersToReturn)
{
NodeRef folder = stack.pop();
runtimeNodeService.getChildAssocs(folder, ASSOC_CONTAINS, MATCH_ALL).stream().map(ChildAssociationRef::getChildRef).forEach(childNode -> {
QName childType = runtimeNodeService.getType(childNode);
if (dictionaryService.isSubClass(childType, ContentModel.TYPE_FOLDER)
&& !runtimeNodeService.hasAspect(childNode, ASPECT_IGNORE_INHERITED_RULES))
{
stack.add(childNode);
// Only return nodes that the user has permission to view.
if (permissionService.hasReadPermission(childNode) == ALLOWED)
{
inheritors.add(childNode);
if (inheritors.size() == maxFoldersToReturn)
{
// Return once we've hit the limit.
return;
}
}
}
});
}
return inheritors;
}
/**
* Gets the inherited rules for a given node reference
*
@@ -798,7 +840,7 @@ public class RuleServiceImpl
{
checkForLinkedRules(nodeRef);
if (this.permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) != AccessStatus.ALLOWED)
if (this.permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) != ALLOWED)
{
throw new RuleServiceException("Insufficient permissions to save a rule.");
}
@@ -823,7 +865,7 @@ public class RuleServiceImpl
// Create the action node
ruleNodeRef = this.nodeService.createNode(
getSavedRuleFolderRef(nodeRef),
ContentModel.ASSOC_CONTAINS,
ASSOC_CONTAINS,
QName.createQName(RuleModel.RULE_MODEL_URI, ASSOC_NAME_RULES_PREFIX + GUID.generate()),
RuleModel.TYPE_RULE).getChildRef();
@@ -866,7 +908,7 @@ public class RuleServiceImpl
NodeRef ruleFolder = getSavedRuleFolderRef(nodeRef);
if (ruleFolder != null)
{
List<ChildAssociationRef> assocs = this.runtimeNodeService.getChildAssocs(ruleFolder, RegexQNamePattern.MATCH_ALL, ASSOC_NAME_RULES_REGEX);
List<ChildAssociationRef> assocs = this.runtimeNodeService.getChildAssocs(ruleFolder, MATCH_ALL, ASSOC_NAME_RULES_REGEX);
List<ChildAssociationRef> orderedAssocs = new ArrayList<ChildAssociationRef>(assocs.size());
ChildAssociationRef movedAssoc = null;
for (ChildAssociationRef assoc : assocs)
@@ -948,7 +990,7 @@ public class RuleServiceImpl
{
checkForLinkedRules(nodeRef);
if (permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED)
if (permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) == ALLOWED)
{
if (nodeService.exists(nodeRef) && nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES))
{
@@ -1011,7 +1053,7 @@ public class RuleServiceImpl
{
checkForLinkedRules(nodeRef);
if (this.permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED)
if (this.permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) == ALLOWED)
{
if (this.nodeService.exists(nodeRef) == true &&
this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true)
@@ -1021,7 +1063,7 @@ public class RuleServiceImpl
{
List<ChildAssociationRef> ruleChildAssocs = this.nodeService.getChildAssocs(
folder,
RegexQNamePattern.MATCH_ALL, ASSOC_NAME_RULES_REGEX);
MATCH_ALL, ASSOC_NAME_RULES_REGEX);
for (ChildAssociationRef ruleChildAssoc : ruleChildAssocs)
{
this.nodeService.removeChild(folder, ruleChildAssoc.getChildRef());
@@ -1371,7 +1413,7 @@ public class RuleServiceImpl
{
boolean result = true;
if (this.nodeService.exists(actionedUponNodeRef)
&& this.permissionService.hasPermission(actionedUponNodeRef, PermissionService.READ).equals(AccessStatus.ALLOWED))
&& this.permissionService.hasPermission(actionedUponNodeRef, PermissionService.READ).equals(ALLOWED))
{
NodeRef copiedFrom = copyService.getOriginal(actionedUponNodeRef);
if (logger.isDebugEnabled() == true)

View File

@@ -227,6 +227,18 @@ public interface RuleService
@Experimental
List<NodeRef> getNodesSupplyingRuleSets(NodeRef nodeRef);
/**
* Get a list of folders inheriting the specified rule set.
*
* @param ruleSet The rule set node.
* @param maxFoldersToReturn A limit on the number of folders to return (since otherwise this could traverse a very large proportion of
* the repository.
* @return The list of the specified
*/
@Auditable (parameters = { "ruleSet", "maxFoldersToReturn" })
@Experimental
List<NodeRef> getFoldersInheritingRuleSet(NodeRef ruleSet, int maxFoldersToReturn);
/**
* Get the rule given its node reference
*

View File

@@ -25,17 +25,21 @@
*/
package org.alfresco.repo.rule;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static org.alfresco.model.ContentModel.ASSOC_CONTAINS;
import static org.alfresco.model.ContentModel.ASSOC_MEMBER;
import static org.alfresco.model.ContentModel.TYPE_CONTENT;
import static org.alfresco.model.ContentModel.TYPE_FOLDER;
import static org.alfresco.repo.rule.RuleModel.ASPECT_IGNORE_INHERITED_RULES;
import static org.alfresco.repo.rule.RuleModel.ASSOC_ACTION;
import static org.alfresco.repo.rule.RuleModel.ASSOC_RULE_FOLDER;
import static org.alfresco.repo.rule.RuleModel.TYPE_RULE;
import static org.alfresco.service.cmr.security.AccessStatus.ALLOWED;
import static org.alfresco.service.cmr.security.AccessStatus.DENIED;
import static org.alfresco.service.namespace.RegexQNamePattern.MATCH_ALL;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.junit.Assert.assertEquals;
@@ -52,18 +56,19 @@ import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.openMocks;
import java.io.Serializable;
import java.util.Collections;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.RuntimeActionService;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionServiceException;
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;
@@ -98,6 +103,8 @@ public class RuleServiceImplUnitTest
@Mock
private RuntimeActionService runtimeActionService;
@Mock
private DictionaryService dictionaryService;
@Mock
private Rule mockRule;
@Mock
private Action mockAction;
@@ -106,6 +113,10 @@ public class RuleServiceImplUnitTest
public void setUp()
{
openMocks(this);
when(dictionaryService.isSubClass(TYPE_FOLDER, TYPE_FOLDER)).thenReturn(true);
when(dictionaryService.isSubClass(TYPE_CONTENT, TYPE_FOLDER)).thenReturn(false);
when(permissionService.hasReadPermission(any())).thenReturn(ALLOWED);
}
@Test
@@ -220,7 +231,7 @@ public class RuleServiceImplUnitTest
@Test
public void testGetRuleSetNode_emptyAssociation()
{
given(runtimeNodeService.getChildAssocs(any(), any(), any())).willReturn(Collections.emptyList());
given(runtimeNodeService.getChildAssocs(any(), any(), any())).willReturn(emptyList());
// when
final NodeRef actualNode = ruleService.getRuleSetNode(FOLDER_NODE);
@@ -264,7 +275,7 @@ public class RuleServiceImplUnitTest
@Test
public void testIsRuleSetAssociatedWithFolder_emptyAssociation()
{
given(runtimeNodeService.getParentAssocs(any(), any(), any())).willReturn(Collections.emptyList());
given(runtimeNodeService.getParentAssocs(any(), any(), any())).willReturn(emptyList());
// when
boolean associated = ruleService.isRuleSetAssociatedWithFolder(RULE_SET_NODE, FOLDER_NODE);
@@ -343,7 +354,7 @@ public class RuleServiceImplUnitTest
@Test
public void testIsRuleAssociatedWithRuleSet_emptyAssociation()
{
given(runtimeNodeService.getParentAssocs(any())).willReturn(Collections.emptyList());
given(runtimeNodeService.getParentAssocs(any())).willReturn(emptyList());
// when
boolean associated = ruleService.isRuleAssociatedWithRuleSet(RULE_NODE, RULE_SET_NODE);
@@ -456,7 +467,7 @@ public class RuleServiceImplUnitTest
{
Map<String, NodeRef> nodes = createParentChildHierarchy("A,B", "B,C", "C,D", "D,E");
// Replace the B,C association with a user group membership association.
ChildAssociationRef memberAssoc = new ChildAssociationRef(ASSOC_MEMBER, nodes.get("B"), ContentModel.TYPE_FOLDER, nodes.get("C"));
ChildAssociationRef memberAssoc = new ChildAssociationRef(ASSOC_MEMBER, nodes.get("B"), TYPE_FOLDER, nodes.get("C"));
given(runtimeNodeService.getParentAssocs(nodes.get("C"))).willReturn(List.of(memberAssoc));
List<NodeRef> actual = ruleService.getNodesSupplyingRuleSets(nodes.get("E"));
@@ -570,10 +581,137 @@ public class RuleServiceImplUnitTest
.filter(assoc -> assoc.endsWith(nodeName))
.map(assoc -> assoc.split(",")[0])
.map(nodeRefMap::get)
.map(parentRef -> new ChildAssociationRef(ASSOC_CONTAINS, parentRef, ContentModel.TYPE_FOLDER, nodeRef))
.map(parentRef -> new ChildAssociationRef(ASSOC_CONTAINS, parentRef, TYPE_FOLDER, nodeRef))
.collect(toList());
given(runtimeNodeService.getParentAssocs(nodeRef)).willReturn(parentAssocs);
});
return nodeRefMap;
}
/** Check that getFoldersInheritingRuleSet returns a child folder. */
@Test
public void testGetFoldersInheritingRuleSet()
{
NodeRef parent = new NodeRef("parent://node/");
NodeRef ruleSetNode = new NodeRef("rule://set/");
ChildAssociationRef ruleSetAssociation = mock(ChildAssociationRef.class);
given(runtimeNodeService.getParentAssocs(ruleSetNode)).willReturn(List.of(ruleSetAssociation));
given(ruleSetAssociation.getParentRef()).willReturn(parent);
NodeRef child = new NodeRef("child://node/");
ChildAssociationRef childAssocMock = mock(ChildAssociationRef.class);
given(runtimeNodeService.getChildAssocs(parent, ASSOC_CONTAINS, MATCH_ALL)).willReturn(List.of(childAssocMock));
given(childAssocMock.getChildRef()).willReturn(child);
given(runtimeNodeService.getType(child)).willReturn(TYPE_FOLDER);
List<NodeRef> actual = ruleService.getFoldersInheritingRuleSet(ruleSetNode, 100);
assertEquals("Unexpected list of inheriting folders.", List.of(child), actual);
}
/** Check that getFoldersInheritingRuleSet omits a child folder if IGNORE_INHERITED_RULES is applied. */
@Test
public void testGetFoldersInheritingRuleSet_ignoreInheritedRules()
{
NodeRef parent = new NodeRef("parent://node/");
NodeRef ruleSetNode = new NodeRef("rule://set/");
ChildAssociationRef ruleSetAssociation = mock(ChildAssociationRef.class);
given(runtimeNodeService.getParentAssocs(ruleSetNode)).willReturn(List.of(ruleSetAssociation));
given(ruleSetAssociation.getParentRef()).willReturn(parent);
NodeRef child = new NodeRef("child://node/");
ChildAssociationRef childAssocMock = mock(ChildAssociationRef.class);
given(runtimeNodeService.getChildAssocs(parent, ASSOC_CONTAINS, MATCH_ALL)).willReturn(List.of(childAssocMock));
given(childAssocMock.getChildRef()).willReturn(child);
given(runtimeNodeService.getType(child)).willReturn(TYPE_FOLDER);
given(runtimeNodeService.hasAspect(child, ASPECT_IGNORE_INHERITED_RULES)).willReturn(true);
List<NodeRef> actual = ruleService.getFoldersInheritingRuleSet(ruleSetNode, 100);
assertEquals("Unexpected list of inheriting folders.", emptyList(), actual);
}
/** Check that getFoldersInheritingRuleSet only returns at most the requested number of folders. */
@Test
public void testGetFoldersExceedsLimit()
{
NodeRef root = new NodeRef("root://node/");
NodeRef ruleSetNode = new NodeRef("rule://set/");
ChildAssociationRef ruleSetAssociation = mock(ChildAssociationRef.class);
given(runtimeNodeService.getParentAssocs(ruleSetNode)).willReturn(List.of(ruleSetAssociation));
given(ruleSetAssociation.getParentRef()).willReturn(root);
// Create a chain of ancestors starting from the root that is 10 folders deep.
List<NodeRef> nodeChain = new ArrayList<>();
nodeChain.add(root);
IntStream.range(0, 10).forEach(index -> {
NodeRef parent = nodeChain.get(nodeChain.size() - 1);
NodeRef child = new NodeRef("chain://node/" + index);
nodeChain.add(child);
ChildAssociationRef childAssocMock = mock(ChildAssociationRef.class);
given(runtimeNodeService.getChildAssocs(parent, ASSOC_CONTAINS, MATCH_ALL)).willReturn(List.of(childAssocMock));
given(childAssocMock.getChildRef()).willReturn(child);
given(runtimeNodeService.getType(child)).willReturn(TYPE_FOLDER);
});
// Request at most 9 folders inheriting the rule.
List<NodeRef> actual = ruleService.getFoldersInheritingRuleSet(ruleSetNode, 9);
// Check we don't get the root node or the final descendant folder.
assertEquals("Unexpected list of inheriting folders.", nodeChain.subList(1, 10), actual);
}
/** Check that getFoldersInheritingRuleSet doesn't include documents. */
@Test
public void testGetFoldersInheritingRuleSet_ignoreFiles()
{
NodeRef parent = new NodeRef("parent://node/");
NodeRef ruleSetNode = new NodeRef("rule://set/");
ChildAssociationRef ruleSetAssociation = mock(ChildAssociationRef.class);
given(runtimeNodeService.getParentAssocs(ruleSetNode)).willReturn(List.of(ruleSetAssociation));
given(ruleSetAssociation.getParentRef()).willReturn(parent);
NodeRef childDocument = new NodeRef("child://document/");
ChildAssociationRef childAssocMock = mock(ChildAssociationRef.class);
given(runtimeNodeService.getChildAssocs(parent, ASSOC_CONTAINS, MATCH_ALL)).willReturn(List.of(childAssocMock));
given(childAssocMock.getChildRef()).willReturn(childDocument);
given(runtimeNodeService.getType(childDocument)).willReturn(TYPE_CONTENT);
List<NodeRef> actual = ruleService.getFoldersInheritingRuleSet(ruleSetNode, 100);
assertEquals("Unexpected list of inheriting folders.", emptyList(), actual);
}
/**
* Check that getFoldersInheritingRuleSet does not include folders that the user doesn't have access to.
* <p>
* This test uses a chain of three folders:
* <pre>
* grandparent - owns the rule set
* parent - user does not have read access
* child - user _does_ have read access
* </pre>
*/
@Test
public void testGetFoldersInheritingRuleSet_omitFoldersWithoutReadPermission()
{
NodeRef grandparent = new NodeRef("grandparent://node/");
NodeRef ruleSetNode = new NodeRef("rule://set/");
ChildAssociationRef ruleSetAssociation = mock(ChildAssociationRef.class);
given(runtimeNodeService.getParentAssocs(ruleSetNode)).willReturn(List.of(ruleSetAssociation));
given(ruleSetAssociation.getParentRef()).willReturn(grandparent);
NodeRef parent = new NodeRef("parent://node/");
ChildAssociationRef parentAssocMock = mock(ChildAssociationRef.class);
given(runtimeNodeService.getChildAssocs(grandparent, ASSOC_CONTAINS, MATCH_ALL)).willReturn(List.of(parentAssocMock));
given(parentAssocMock.getChildRef()).willReturn(parent);
given(runtimeNodeService.getType(parent)).willReturn(TYPE_FOLDER);
NodeRef child = new NodeRef("child://node/");
ChildAssociationRef childAssocMock = mock(ChildAssociationRef.class);
given(runtimeNodeService.getChildAssocs(grandparent, ASSOC_CONTAINS, MATCH_ALL)).willReturn(List.of(childAssocMock));
given(childAssocMock.getChildRef()).willReturn(child);
given(runtimeNodeService.getType(child)).willReturn(TYPE_FOLDER);
// The current user doesn't have permission to view the parent node.
given(permissionService.hasReadPermission(parent)).willReturn(DENIED);
List<NodeRef> actual = ruleService.getFoldersInheritingRuleSet(ruleSetNode, 100);
assertEquals("Unexpected list of inheriting folders.", List.of(child), actual);
}
}