diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/CreateRulesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/CreateRulesTests.java index c2a4123273..e0a529ad04 100644 --- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/CreateRulesTests.java +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/CreateRulesTests.java @@ -522,6 +522,49 @@ public class CreateRulesTests extends RestTest restClient.assertLastError().containsSummary("Missing action's mandatory parameter: destination-folder"); } + /** + * Check we get error when attempting to create a rule that copies files to a non-existent folder. + */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void createRuleThatUsesNonExistentNode() + { + RestRuleModel ruleModel = rulesUtils.createRuleModelWithDefaultValues(); + RestActionBodyExecTemplateModel invalidAction = new RestActionBodyExecTemplateModel(); + String actionDefinitionId = "copy"; + invalidAction.setActionDefinitionId(actionDefinitionId); + invalidAction.setParams(Map.of("destination-folder", "non-existent-node")); + ruleModel.setActions(List.of(invalidAction)); + + restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet() + .createSingleRule(ruleModel); + + restClient.assertStatusCodeIs(NOT_FOUND); + restClient.assertLastError().containsSummary("The entity with id: non-existent-node was not found"); + } + + /** + * Check we get error when attempting to create a rule that references a folder that the user does not have read permission for. + */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void createRuleThatUsesNodeWithoutReadPermission() + { + SiteModel privateSite = dataSite.usingAdmin().createPrivateRandomSite(); + FolderModel privateFolder = dataContent.usingAdmin().usingSite(privateSite).createFolder(); + + RestRuleModel ruleModel = rulesUtils.createRuleModelWithDefaultValues(); + RestActionBodyExecTemplateModel invalidAction = new RestActionBodyExecTemplateModel(); + String actionDefinitionId = "copy"; + invalidAction.setActionDefinitionId(actionDefinitionId); + invalidAction.setParams(Map.of("destination-folder", privateFolder.getNodeRef())); + ruleModel.setActions(List.of(invalidAction)); + + restClient.authenticateUser(user).withPrivateAPI().usingNode(ruleFolder).usingDefaultRuleSet() + .createSingleRule(ruleModel); + + restClient.assertStatusCodeIs(NOT_FOUND); + restClient.assertLastError().containsSummary("The entity with id: " + privateFolder.getNodeRef() + " was not found"); + } + /** * Check we can create a rule with multiple conditions */ diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/RulesTestsUtils.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/RulesTestsUtils.java index 8447946762..7399ef259f 100644 --- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/RulesTestsUtils.java +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/RulesTestsUtils.java @@ -93,6 +93,7 @@ public class RulesTestsUtils implements InitializingBean private FolderModel copyDestinationFolder; private FolderModel checkOutDestinationFolder; + /** * Initialise the util class. */ diff --git a/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/ActionParameterConverter.java b/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/ActionParameterConverter.java index e503495580..bed1fbc892 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/ActionParameterConverter.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/impl/rules/ActionParameterConverter.java @@ -26,12 +26,17 @@ package org.alfresco.rest.api.impl.rules; +import static org.alfresco.rest.framework.core.exceptions.NotFoundException.DEFAULT_MESSAGE_ID; +import static org.alfresco.service.cmr.security.AccessStatus.ALLOWED; + import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.alfresco.rest.api.Nodes; +import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; import org.alfresco.rest.framework.core.exceptions.NotFoundException; import org.alfresco.service.Experimental; @@ -42,8 +47,9 @@ import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryException; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.json.JSONArray; @@ -56,13 +62,19 @@ public class ActionParameterConverter private final DictionaryService dictionaryService; private final ActionService actionService; private final NamespaceService namespaceService; + private final NodeService nodeService; + private final PermissionService permissionService; + private final Nodes nodes; - public ActionParameterConverter(DictionaryService dictionaryService, ActionService actionService, - NamespaceService namespaceService) + public ActionParameterConverter(DictionaryService dictionaryService, ActionService actionService, NamespaceService namespaceService, + NodeService nodeService, PermissionService permissionService, Nodes nodes) { this.dictionaryService = dictionaryService; this.actionService = actionService; this.namespaceService = namespaceService; + this.nodeService = nodeService; + this.permissionService = permissionService; + this.nodes = nodes; } public Map getConvertedParams(Map params, String name) @@ -74,10 +86,12 @@ public class ActionParameterConverter definition = actionService.getActionDefinition(name); if (definition == null) { - throw new NotFoundException(NotFoundException.DEFAULT_MESSAGE_ID, new String[]{name}); + throw new NotFoundException(DEFAULT_MESSAGE_ID, new String[]{name}); } - } catch (NoSuchBeanDefinitionException e) { - throw new NotFoundException(NotFoundException.DEFAULT_MESSAGE_ID, new String[]{name}); + } + catch (NoSuchBeanDefinitionException e) + { + throw new NotFoundException(DEFAULT_MESSAGE_ID, new String[]{name}); } for (Map.Entry param : params.entrySet()) @@ -91,7 +105,8 @@ public class ActionParameterConverter { final QName typeQName = paramDef.getType(); parameters.put(param.getKey(), convertValue(typeQName, param.getValue())); - } else + } + else { parameters.put(param.getKey(), param.getValue().toString()); } @@ -105,7 +120,8 @@ public class ActionParameterConverter { return ((QName) param).toPrefixString(namespaceService); } - else if (param instanceof NodeRef) { + else if (param instanceof NodeRef) + { return ((NodeRef) param).getId(); } else @@ -121,7 +137,7 @@ public class ActionParameterConverter final DataTypeDefinition typeDef = dictionaryService.getDataType(typeQName); if (typeDef == null) { - throw new NotFoundException(NotFoundException.DEFAULT_MESSAGE_ID, new String[]{typeQName.toPrefixString()}); + throw new NotFoundException(DEFAULT_MESSAGE_ID, new String[]{typeQName.toPrefixString()}); } if (propertyValue instanceof JSONArray) @@ -130,7 +146,8 @@ public class ActionParameterConverter try { Class.forName(javaClassName); - } catch (ClassNotFoundException e) + } + catch (ClassNotFoundException e) { throw new DictionaryException("Java class " + javaClassName + " of property type " + typeDef.getName() + " is invalid", e); } @@ -151,7 +168,12 @@ public class ActionParameterConverter } else if (typeQName.isMatch(DataTypeDefinition.NODE_REF)) { - value = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, propertyValue.toString()); + NodeRef nodeRef = nodes.validateOrLookupNode(propertyValue.toString(), null); + if (permissionService.hasReadPermission(nodeRef) != ALLOWED) + { + throw new EntityNotFoundException(propertyValue.toString()); + } + value = nodeRef; } else { diff --git a/remote-api/src/main/java/org/alfresco/rest/framework/core/exceptions/EntityNotFoundException.java b/remote-api/src/main/java/org/alfresco/rest/framework/core/exceptions/EntityNotFoundException.java index 8ba193cff8..e053e1da13 100644 --- a/remote-api/src/main/java/org/alfresco/rest/framework/core/exceptions/EntityNotFoundException.java +++ b/remote-api/src/main/java/org/alfresco/rest/framework/core/exceptions/EntityNotFoundException.java @@ -34,7 +34,7 @@ public class EntityNotFoundException extends NotFoundException { private static final long serialVersionUID = -1198595000441207734L; public static String DEFAULT_MESSAGE_ID = "framework.exception.EntityNotFound"; - + /** * The entity id param will be shown in the default error message. * @param entityId String @@ -44,6 +44,17 @@ public class EntityNotFoundException extends NotFoundException super(DEFAULT_MESSAGE_ID, new String[] {entityId}); } + /** + * The entity id param will be shown in the default error message. + * + * @param msgId The message template. + * @param parameters The message template parameters. + */ + public EntityNotFoundException(String msgId, String[] parameters) + { + super(msgId, parameters); + } + public EntityNotFoundException(String msgId, Throwable cause) { super(msgId, cause); 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 1178e6cc04..720498da18 100644 --- a/remote-api/src/main/resources/alfresco/public-rest-context.xml +++ b/remote-api/src/main/resources/alfresco/public-rest-context.xml @@ -910,6 +910,9 @@ + + +