[ACS-2747] Prevent private action execution from the V1 HTTP API. Added end-to-end test method. (#1108)

This commit is contained in:
kcichonczyk
2022-05-09 13:14:10 +02:00
committed by GitHub
parent 810cd9f067
commit 8edfd4bdce
3 changed files with 46 additions and 1 deletions

View File

@@ -11,6 +11,7 @@ import org.alfresco.rest.model.RestActionDefinitionModelsCollection;
import org.alfresco.rest.model.RestNodeModel; import org.alfresco.rest.model.RestNodeModel;
import org.alfresco.utility.Utility; import org.alfresco.utility.Utility;
import org.alfresco.utility.model.FileModel; import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel; import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.TestGroup; import org.alfresco.utility.model.TestGroup;
import org.alfresco.utility.model.UserModel; import org.alfresco.utility.model.UserModel;
@@ -21,10 +22,14 @@ import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import java.util.Properties;
public class ActionsTests extends RestTest public class ActionsTests extends RestTest
{ {
private UserModel adminUser; private UserModel adminUser;
private FileModel document; private FileModel document;
private FolderModel folder;
private FileModel randomFile;
private SiteModel publicSite; private SiteModel publicSite;
@BeforeClass(alwaysRun = true) @BeforeClass(alwaysRun = true)
@@ -33,6 +38,8 @@ public class ActionsTests extends RestTest
adminUser = dataUser.getAdminUser(); adminUser = dataUser.getAdminUser();
publicSite = dataSite.createPublicRandomSite(); publicSite = dataSite.createPublicRandomSite();
document = dataContent.usingSite(publicSite).usingUser(adminUser).createContent(CMISUtil.DocumentType.TEXT_PLAIN); 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, @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, @TestRail (section = { TestGroup.REST_API, TestGroup.ACTIONS }, executionType = ExecutionType.SANITY,
description = "Sanity test for POST /action-executions") description = "Sanity test for POST /action-executions")
@Test (groups = { TestGroup.REST_API, TestGroup.ACTIONS, TestGroup.SANITY }) @Test (groups = { TestGroup.REST_API, TestGroup.ACTIONS, TestGroup.SANITY })
@@ -141,7 +164,7 @@ public class ActionsTests extends RestTest
withCoreAPI(). withCoreAPI().
usingActions(). usingActions().
getActionDefinitionById("add-features"); getActionDefinitionById("add-features");
restClient.assertStatusCodeIs(HttpStatus.OK); restClient.assertStatusCodeIs(HttpStatus.OK);
assertFalse(restActionDefinition.getId().isEmpty()); assertFalse(restActionDefinition.getId().isEmpty());
restActionDefinition.getId().equals("add-features"); restActionDefinition.getId().equals("add-features");

View File

@@ -26,11 +26,14 @@
package org.alfresco.rest.api.impl; package org.alfresco.rest.api.impl;
import org.alfresco.error.AlfrescoRuntimeException; 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.Actions;
import org.alfresco.rest.api.model.Action; import org.alfresco.rest.api.model.Action;
import org.alfresco.rest.api.model.ActionDefinition; import org.alfresco.rest.api.model.ActionDefinition;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; 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.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rest.framework.resource.parameters.SortColumn; import org.alfresco.rest.framework.resource.parameters.SortColumn;
@@ -72,6 +75,9 @@ public class ActionsImpl implements Actions
private NamespaceService namespaceService; private NamespaceService namespaceService;
private NodeService nodeService; private NodeService nodeService;
private NamespacePrefixResolver prefixResolver; private NamespacePrefixResolver prefixResolver;
private RuntimeActionService runtimeActionService;
private final String HTTP_V1_EXECUTION_SOURCE = "http-v1";
public void setActionService(ActionService actionService) public void setActionService(ActionService actionService)
{ {
@@ -98,6 +104,11 @@ public class ActionsImpl implements Actions
this.prefixResolver = prefixResolver; this.prefixResolver = prefixResolver;
} }
public void setRuntimeActionService(RuntimeActionService runtimeActionService)
{
this.runtimeActionService = runtimeActionService;
}
@Override @Override
public ActionDefinition getActionDefinitionById(String actionDefinitionId) public ActionDefinition getActionDefinitionById(String actionDefinitionId)
{ {
@@ -263,6 +274,16 @@ public class ActionsImpl implements Actions
throw new EntityNotFoundException(action.getActionDefinitionId()); 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. // targetId is optional, however, currently targetId must be a valid node ID.
NodeRef actionedUponNodeRef = null; NodeRef actionedUponNodeRef = null;
if (action.getTargetId() != null && !action.getTargetId().isEmpty()) if (action.getTargetId() != null && !action.getTargetId().isEmpty())

View File

@@ -566,6 +566,7 @@
<property name="namespaceService" ref="NamespaceService"/> <property name="namespaceService" ref="NamespaceService"/>
<property name="nodeService" ref="NodeService"/> <property name="nodeService" ref="NodeService"/>
<property name="prefixResolver" ref="namespaceService"/> <property name="prefixResolver" ref="namespaceService"/>
<property name="runtimeActionService" ref="actionService"/>
</bean> </bean>
<bean id="Actions" class="org.springframework.aop.framework.ProxyFactoryBean"> <bean id="Actions" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="org.alfresco.rest.api.Actions"/> <property name="proxyInterfaces" value="org.alfresco.rest.api.Actions"/>