From 8edfd4bdce509e015a4500d8712b3cb1f9af8c55 Mon Sep 17 00:00:00 2001
From: kcichonczyk <88378534+kcichonczyk@users.noreply.github.com>
Date: Mon, 9 May 2022 13:14:10 +0200
Subject: [PATCH] [ACS-2747] Prevent private action execution from the V1 HTTP
API. Added end-to-end test method. (#1108)
---
.../alfresco/rest/actions/ActionsTests.java | 25 ++++++++++++++++++-
.../alfresco/rest/api/impl/ActionsImpl.java | 21 ++++++++++++++++
.../alfresco/public-rest-context.xml | 1 +
3 files changed, 46 insertions(+), 1 deletion(-)
diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/actions/ActionsTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/actions/ActionsTests.java
index df822111de..2092acd8ae 100644
--- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/actions/ActionsTests.java
+++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/actions/ActionsTests.java
@@ -11,6 +11,7 @@ import org.alfresco.rest.model.RestActionDefinitionModelsCollection;
import org.alfresco.rest.model.RestNodeModel;
import org.alfresco.utility.Utility;
import org.alfresco.utility.model.FileModel;
+import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.TestGroup;
import org.alfresco.utility.model.UserModel;
@@ -21,10 +22,14 @@ import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import java.util.Properties;
+
public class ActionsTests extends RestTest
{
private UserModel adminUser;
private FileModel document;
+ private FolderModel folder;
+ private FileModel randomFile;
private SiteModel publicSite;
@BeforeClass(alwaysRun = true)
@@ -33,6 +38,8 @@ public class ActionsTests extends RestTest
adminUser = dataUser.getAdminUser();
publicSite = dataSite.createPublicRandomSite();
document = dataContent.usingSite(publicSite).usingUser(adminUser).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
+ folder = dataContent.usingUser(adminUser).createFolder();
+ randomFile = dataContent.usingUser(adminUser).usingResource(folder).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
}
@TestRail(section = { TestGroup.REST_API,TestGroup.ACTIONS }, executionType = ExecutionType.SANITY,
@@ -106,6 +113,22 @@ public class ActionsTests extends RestTest
});
}
+ @TestRail(section = { TestGroup.REST_API,TestGroup.ACTIONS }, executionType = ExecutionType.SANITY,
+ description = "Test for private action execution from POST api/-default-/public/alfresco/versions/1/action-executions, should not be possible to execute private action using V1 REST API")
+ @Test(groups = { TestGroup.REST_API, TestGroup.ACTIONS, TestGroup.SANITY})
+ public void executePrivateActionV1RESTAPI() throws Exception
+ {
+ // 'count-children' action is expected to be private (not exposed) by default
+ String actionDefinitionId = "count-children";
+ JSONObject response = restClient.authenticateUser(adminUser)
+ .withCoreAPI()
+ .usingActions()
+ .executeAction(actionDefinitionId, folder);
+
+ restClient.assertStatusCodeIs(HttpStatus.FORBIDDEN);
+ restClient.assertLastError().containsSummary("Action '" + actionDefinitionId + "' is not exposed within 'http-v1' execution source.");
+ }
+
@TestRail (section = { TestGroup.REST_API, TestGroup.ACTIONS }, executionType = ExecutionType.SANITY,
description = "Sanity test for POST /action-executions")
@Test (groups = { TestGroup.REST_API, TestGroup.ACTIONS, TestGroup.SANITY })
@@ -141,7 +164,7 @@ public class ActionsTests extends RestTest
withCoreAPI().
usingActions().
getActionDefinitionById("add-features");
-
+
restClient.assertStatusCodeIs(HttpStatus.OK);
assertFalse(restActionDefinition.getId().isEmpty());
restActionDefinition.getId().equals("add-features");
diff --git a/remote-api/src/main/java/org/alfresco/rest/api/impl/ActionsImpl.java b/remote-api/src/main/java/org/alfresco/rest/api/impl/ActionsImpl.java
index 8c89fb7a2e..391d7fb51c 100644
--- a/remote-api/src/main/java/org/alfresco/rest/api/impl/ActionsImpl.java
+++ b/remote-api/src/main/java/org/alfresco/rest/api/impl/ActionsImpl.java
@@ -26,11 +26,14 @@
package org.alfresco.rest.api.impl;
import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.repo.action.ActionExecutionContext;
+import org.alfresco.repo.action.RuntimeActionService;
import org.alfresco.rest.api.Actions;
import org.alfresco.rest.api.model.Action;
import org.alfresco.rest.api.model.ActionDefinition;
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.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rest.framework.resource.parameters.SortColumn;
@@ -72,6 +75,9 @@ public class ActionsImpl implements Actions
private NamespaceService namespaceService;
private NodeService nodeService;
private NamespacePrefixResolver prefixResolver;
+ private RuntimeActionService runtimeActionService;
+
+ private final String HTTP_V1_EXECUTION_SOURCE = "http-v1";
public void setActionService(ActionService actionService)
{
@@ -98,6 +104,11 @@ public class ActionsImpl implements Actions
this.prefixResolver = prefixResolver;
}
+ public void setRuntimeActionService(RuntimeActionService runtimeActionService)
+ {
+ this.runtimeActionService = runtimeActionService;
+ }
+
@Override
public ActionDefinition getActionDefinitionById(String actionDefinitionId)
{
@@ -263,6 +274,16 @@ public class ActionsImpl implements Actions
throw new EntityNotFoundException(action.getActionDefinitionId());
}
+ final ActionExecutionContext actionExecutionContext = ActionExecutionContext
+ .builder(actionDef.getName())
+ .withExecutionSource(HTTP_V1_EXECUTION_SOURCE)
+ .build();
+
+ if (!runtimeActionService.isExposed(actionExecutionContext))
+ {
+ throw new PermissionDeniedException("Action '" + actionDef.getName() + "' is not exposed within '" + HTTP_V1_EXECUTION_SOURCE + "' execution source.");
+ }
+
// targetId is optional, however, currently targetId must be a valid node ID.
NodeRef actionedUponNodeRef = null;
if (action.getTargetId() != null && !action.getTargetId().isEmpty())
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 11bc9ac49b..ac61c43666 100644
--- a/remote-api/src/main/resources/alfresco/public-rest-context.xml
+++ b/remote-api/src/main/resources/alfresco/public-rest-context.xml
@@ -566,6 +566,7 @@
+