mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
ACS-3657 Allow returning partial list of rule sets.
If a user does not have access to a rule set applied to a node then it will be excluded from the results, but the user will be able to see the list of other rule sets. Also add E2E tests for permissions when viewing rule sets.
This commit is contained in:
@@ -31,6 +31,7 @@ import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithDefault
|
|||||||
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithModifiedValues;
|
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithModifiedValues;
|
||||||
import static org.alfresco.utility.report.log.Step.STEP;
|
import static org.alfresco.utility.report.log.Step.STEP;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.springframework.http.HttpStatus.FORBIDDEN;
|
||||||
import static org.springframework.http.HttpStatus.NOT_FOUND;
|
import static org.springframework.http.HttpStatus.NOT_FOUND;
|
||||||
import static org.springframework.http.HttpStatus.OK;
|
import static org.springframework.http.HttpStatus.OK;
|
||||||
|
|
||||||
@@ -62,6 +63,8 @@ public class GetRuleSetsTests extends RestTest
|
|||||||
private UserModel user;
|
private UserModel user;
|
||||||
private SiteModel site;
|
private SiteModel site;
|
||||||
private FolderModel ruleFolder;
|
private FolderModel ruleFolder;
|
||||||
|
/** A folder with a rule in a private site owned by admin. */
|
||||||
|
private FolderModel privateFolder;
|
||||||
private FolderModel inheritingChildFolder;
|
private FolderModel inheritingChildFolder;
|
||||||
private FolderModel notInheritingChildFolder;
|
private FolderModel notInheritingChildFolder;
|
||||||
private RestRuleModel rule;
|
private RestRuleModel rule;
|
||||||
@@ -93,6 +96,11 @@ public class GetRuleSetsTests extends RestTest
|
|||||||
.getListOfRuleSets();
|
.getListOfRuleSets();
|
||||||
ruleSets.assertThat().entriesListCountIs(1);
|
ruleSets.assertThat().entriesListCountIs(1);
|
||||||
ruleSetId = ruleSets.getEntries().get(0).onModel().getId();
|
ruleSetId = ruleSets.getEntries().get(0).onModel().getId();
|
||||||
|
|
||||||
|
STEP("Use admin to create a private site containing a rule in a rule set that can be inherited.");
|
||||||
|
SiteModel privateSite = dataSite.usingAdmin().createPrivateRandomSite();
|
||||||
|
privateFolder = dataContent.usingAdmin().usingSite(privateSite).createFolder();
|
||||||
|
coreAPIForAdmin().usingNode(privateFolder).usingDefaultRuleSet().createSingleRule(createRuleModelWithModifiedValues());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check we can get an empty list of rule sets. */
|
/** Check we can get an empty list of rule sets. */
|
||||||
@@ -135,6 +143,48 @@ public class GetRuleSetsTests extends RestTest
|
|||||||
restClient.assertStatusCodeIs(NOT_FOUND);
|
restClient.assertStatusCodeIs(NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check that we get a 403 error when trying to get rule sets for a folder we don't have read access to. */
|
||||||
|
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
|
||||||
|
public void getRuleSetsWithoutPermission()
|
||||||
|
{
|
||||||
|
STEP("Check a user cannot list rule sets without read access.");
|
||||||
|
coreAPIForUser().usingNode(privateFolder).getListOfRuleSets();
|
||||||
|
restClient.assertStatusCodeIs(FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check that we can still list some rule sets if we don't have permission to view them all. */
|
||||||
|
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
|
||||||
|
public void permissionsAreRespectedWhenListingRuleSets()
|
||||||
|
{
|
||||||
|
STEP("Create a public site containing a parent and child folder with rule inheritance enabled.");
|
||||||
|
SiteModel publicSite = dataSite.usingUser(user).createPublicRandomSite();
|
||||||
|
FolderModel parentFolder = dataContent.usingUser(user).usingSite(publicSite).createFolder();
|
||||||
|
FolderModel childFolder = dataContent.usingUser(user).usingResource(parentFolder).createFolder();
|
||||||
|
RestRuleSettingsModel enabled = new RestRuleSettingsModel();
|
||||||
|
enabled.setValue(true);
|
||||||
|
coreAPIForUser().usingNode(parentFolder).usingRuleSetting(IS_INHERITANCE_ENABLED).updateSetting(enabled);
|
||||||
|
|
||||||
|
STEP("Link the parent folder to a private rule set.");
|
||||||
|
RestRuleSetLinkModel linkModel = new RestRuleSetLinkModel();
|
||||||
|
linkModel.setId(privateFolder.getNodeRef());
|
||||||
|
coreAPIForAdmin().usingNode(parentFolder).createRuleLink(linkModel);
|
||||||
|
|
||||||
|
STEP("Create a rule on the child folder.");
|
||||||
|
coreAPIForUser().usingNode(childFolder).usingDefaultRuleSet().createSingleRule(createRuleModelWithDefaultValues());
|
||||||
|
|
||||||
|
STEP("Check admin can view both rule sets.");
|
||||||
|
RestRuleSetModelsCollection adminViewOfRuleSets = coreAPIForAdmin().usingNode(childFolder).getListOfRuleSets();
|
||||||
|
restClient.assertStatusCodeIs(OK);
|
||||||
|
RestRuleSetModel parentRuleSet = adminViewOfRuleSets.getEntries().get(0).onModel();
|
||||||
|
RestRuleSetModel childRuleSet = adminViewOfRuleSets.getEntries().get(1).onModel();
|
||||||
|
|
||||||
|
STEP("Check the normal user can only view the child rule set.");
|
||||||
|
RestRuleSetModelsCollection userViewOfRuleSets = coreAPIForUser().usingNode(childFolder).getListOfRuleSets();
|
||||||
|
restClient.assertStatusCodeIs(OK);
|
||||||
|
userViewOfRuleSets.assertThat().entriesListContains("id", childRuleSet.getId())
|
||||||
|
.and().entriesListDoesNotContain("id", parentRuleSet.getId());
|
||||||
|
}
|
||||||
|
|
||||||
/** Check we can get the id of the folder that owns a list of rule sets. */
|
/** Check we can get the id of the folder that owns a list of rule sets. */
|
||||||
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
|
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
|
||||||
public void getRuleSetsAndOwningFolders()
|
public void getRuleSetsAndOwningFolders()
|
||||||
@@ -503,6 +553,38 @@ public class GetRuleSetsTests extends RestTest
|
|||||||
ruleSet.assertThat().field("isLinkedTo").is(false);
|
ruleSet.assertThat().field("isLinkedTo").is(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check that we can only view a rule set if have read permission. */
|
||||||
|
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
|
||||||
|
public void permissionsChecksForFolderWithPrivateAndPublicRuleSets()
|
||||||
|
{
|
||||||
|
STEP("Create a public site containing a parent and child folder with rule inheritance enabled.");
|
||||||
|
SiteModel publicSite = dataSite.usingUser(user).createPublicRandomSite();
|
||||||
|
FolderModel parentFolder = dataContent.usingUser(user).usingSite(publicSite).createFolder();
|
||||||
|
FolderModel childFolder = dataContent.usingUser(user).usingResource(parentFolder).createFolder();
|
||||||
|
RestRuleSettingsModel enabled = new RestRuleSettingsModel();
|
||||||
|
enabled.setValue(true);
|
||||||
|
coreAPIForUser().usingNode(parentFolder).usingRuleSetting(IS_INHERITANCE_ENABLED).updateSetting(enabled);
|
||||||
|
|
||||||
|
STEP("Link the parent folder to a private rule set.");
|
||||||
|
RestRuleSetLinkModel linkModel = new RestRuleSetLinkModel();
|
||||||
|
linkModel.setId(privateFolder.getNodeRef());
|
||||||
|
coreAPIForAdmin().usingNode(parentFolder).createRuleLink(linkModel);
|
||||||
|
|
||||||
|
STEP("Create a rule on the child folder.");
|
||||||
|
coreAPIForUser().usingNode(childFolder).usingDefaultRuleSet().createSingleRule(createRuleModelWithDefaultValues());
|
||||||
|
|
||||||
|
STEP("Use the admin user to get both rule sets.");
|
||||||
|
RestRuleSetModelsCollection adminViewOfRuleSets = coreAPIForAdmin().usingNode(childFolder).getListOfRuleSets();
|
||||||
|
RestRuleSetModel parentRuleSet = adminViewOfRuleSets.getEntries().get(0).onModel();
|
||||||
|
RestRuleSetModel childRuleSet = adminViewOfRuleSets.getEntries().get(1).onModel();
|
||||||
|
|
||||||
|
STEP("Check the normal user can only view the child rule set.");
|
||||||
|
coreAPIForUser().usingNode(childFolder).getRuleSet(parentRuleSet.getId());
|
||||||
|
restClient.assertStatusCodeIs(FORBIDDEN);
|
||||||
|
coreAPIForUser().usingNode(childFolder).getRuleSet(childRuleSet.getId());
|
||||||
|
restClient.assertStatusCodeIs(OK);
|
||||||
|
}
|
||||||
|
|
||||||
private RestCoreAPI coreAPIForUser()
|
private RestCoreAPI coreAPIForUser()
|
||||||
{
|
{
|
||||||
return restClient.authenticateUser(user).withCoreAPI();
|
return restClient.authenticateUser(user).withCoreAPI();
|
||||||
|
@@ -39,6 +39,7 @@ import java.util.stream.IntStream;
|
|||||||
|
|
||||||
import org.alfresco.repo.rule.RuleModel;
|
import org.alfresco.repo.rule.RuleModel;
|
||||||
import org.alfresco.repo.rule.RuntimeRuleService;
|
import org.alfresco.repo.rule.RuntimeRuleService;
|
||||||
|
import org.alfresco.repo.security.permissions.AccessDeniedException;
|
||||||
import org.alfresco.rest.api.RuleSets;
|
import org.alfresco.rest.api.RuleSets;
|
||||||
import org.alfresco.rest.api.model.rules.RuleSet;
|
import org.alfresco.rest.api.model.rules.RuleSet;
|
||||||
import org.alfresco.rest.api.model.rules.RuleSetLink;
|
import org.alfresco.rest.api.model.rules.RuleSetLink;
|
||||||
@@ -50,10 +51,14 @@ import org.alfresco.service.Experimental;
|
|||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
import org.alfresco.service.cmr.rule.RuleService;
|
import org.alfresco.service.cmr.rule.RuleService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@Experimental
|
@Experimental
|
||||||
public class RuleSetsImpl implements RuleSets
|
public class RuleSetsImpl implements RuleSets
|
||||||
{
|
{
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(RuleSetsImpl.class);
|
||||||
|
|
||||||
private RuleSetLoader ruleSetLoader;
|
private RuleSetLoader ruleSetLoader;
|
||||||
private RuleService ruleService;
|
private RuleService ruleService;
|
||||||
private NodeValidator validator;
|
private NodeValidator validator;
|
||||||
@@ -67,15 +72,42 @@ public class RuleSetsImpl implements RuleSets
|
|||||||
|
|
||||||
List<RuleSet> ruleSets = ruleService.getNodesSupplyingRuleSets(folderNode)
|
List<RuleSet> ruleSets = ruleService.getNodesSupplyingRuleSets(folderNode)
|
||||||
.stream()
|
.stream()
|
||||||
.map(ruleService::getRuleSetNode)
|
.map(supplyingNode -> loadRuleSet(supplyingNode, folderNode, includes))
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.map(nodeRef -> ruleSetLoader.loadRuleSet(nodeRef, folderNode, includes))
|
|
||||||
.distinct()
|
.distinct()
|
||||||
.collect(toList());
|
.collect(toList());
|
||||||
|
|
||||||
return ListPage.of(ruleSets, paging);
|
return ListPage.of(ruleSets, paging);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the specified rule set if the user has permission.
|
||||||
|
*
|
||||||
|
* @param supplyingNode The folder supplying a rule set.
|
||||||
|
* @param folderNode The folder being supplied with rule sets.
|
||||||
|
* @param includes The list of optional fields to include for each rule set in the response.
|
||||||
|
* @return The rule set from the DB or null if the folder has no rule set, or the current user does not have permission to view it.
|
||||||
|
*/
|
||||||
|
private RuleSet loadRuleSet(NodeRef supplyingNode, NodeRef folderNode, List<String> includes)
|
||||||
|
{
|
||||||
|
NodeRef ruleSetNode = ruleService.getRuleSetNode(supplyingNode);
|
||||||
|
// Check if the folder has no rule sets.
|
||||||
|
if (ruleSetNode == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ruleSetLoader.loadRuleSet(ruleSetNode, folderNode, includes);
|
||||||
|
}
|
||||||
|
catch (AccessDeniedException e)
|
||||||
|
{
|
||||||
|
LOGGER.debug("User does not have permission to view rule set with id {}.", ruleSetNode, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RuleSet getRuleSetById(String folderNodeId, String ruleSetId, List<String> includes)
|
public RuleSet getRuleSetById(String folderNodeId, String ruleSetId, List<String> includes)
|
||||||
{
|
{
|
||||||
|
@@ -42,6 +42,7 @@ import java.util.List;
|
|||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
import org.alfresco.repo.rule.RuleModel;
|
import org.alfresco.repo.rule.RuleModel;
|
||||||
import org.alfresco.repo.rule.RuntimeRuleService;
|
import org.alfresco.repo.rule.RuntimeRuleService;
|
||||||
|
import org.alfresco.repo.security.permissions.AccessDeniedException;
|
||||||
import org.alfresco.rest.api.model.rules.RuleSet;
|
import org.alfresco.rest.api.model.rules.RuleSet;
|
||||||
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
|
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
|
||||||
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
|
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
|
||||||
@@ -146,6 +147,34 @@ public class RuleSetsImplTest extends TestCase
|
|||||||
assertEquals(PAGING, actual.getPaging());
|
assertEquals(PAGING, actual.getPaging());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnlyGetPermittedRuleSets()
|
||||||
|
{
|
||||||
|
// Simulate a private folder with a rule set that the current user can't access.
|
||||||
|
NodeRef privateFolder = new NodeRef("private://folder/");
|
||||||
|
NodeRef privateRuleSetNode = new NodeRef("private://rule/set/node/");
|
||||||
|
given(ruleServiceMock.getRuleSetNode(privateFolder)).willReturn(privateRuleSetNode);
|
||||||
|
given(ruleServiceMock.getNodesSupplyingRuleSets(FOLDER_NODE)).willReturn(List.of(FOLDER_NODE, privateFolder));
|
||||||
|
given(ruleSetLoaderMock.loadRuleSet(eq(privateRuleSetNode), any(NodeRef.class), any(List.class)))
|
||||||
|
.willThrow(new AccessDeniedException("Cannot access private rule set."));
|
||||||
|
|
||||||
|
// Call the method under test.
|
||||||
|
CollectionWithPagingInfo<RuleSet> actual = ruleSets.getRuleSets(FOLDER_ID, INCLUDES, PAGING);
|
||||||
|
|
||||||
|
then(nodeValidatorMock).should().validateFolderNode(FOLDER_ID, false);
|
||||||
|
then(nodeValidatorMock).shouldHaveNoMoreInteractions();
|
||||||
|
|
||||||
|
then(ruleServiceMock).should().getNodesSupplyingRuleSets(FOLDER_NODE);
|
||||||
|
then(ruleServiceMock).should().getRuleSetNode(FOLDER_NODE);
|
||||||
|
then(ruleServiceMock).should().getRuleSetNode(privateFolder);
|
||||||
|
then(ruleServiceMock).shouldHaveNoMoreInteractions();
|
||||||
|
|
||||||
|
// Check we only get the accessible rule set back.
|
||||||
|
Collection<RuleSet> expected = List.of(ruleSetMock);
|
||||||
|
assertEquals(expected, actual.getCollection());
|
||||||
|
assertEquals(PAGING, actual.getPaging());
|
||||||
|
}
|
||||||
|
|
||||||
/** Check that a folder with a parent and grandparent can inherit rule sets from the grandparent, even if the parent has no rules. */
|
/** Check that a folder with a parent and grandparent can inherit rule sets from the grandparent, even if the parent has no rules. */
|
||||||
@Test
|
@Test
|
||||||
public void testGetInheritedRuleSets()
|
public void testGetInheritedRuleSets()
|
||||||
|
Reference in New Issue
Block a user