diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/workflow/deployments/GetDeploymentsSanityTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/workflow/deployments/GetDeploymentsSanityTests.java index 9d1a4025f5..f08adacf13 100644 --- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/workflow/deployments/GetDeploymentsSanityTests.java +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/workflow/deployments/GetDeploymentsSanityTests.java @@ -35,10 +35,11 @@ public class GetDeploymentsSanityTests extends RestTest restClient.assertStatusCodeIs(HttpStatus.OK); deployments.assertThat().entriesListIsNotEmpty(); deployments.getOneRandomEntry().onModel().assertThat() - .fieldsCount().is(3).and() + .fieldsCount().is(4).and() .field("id").isNotEmpty().and() .field("deployedAt").isNotEmpty().and() - .field("name").isNotEmpty(); + .field("name").isNotEmpty().and() + .field("category").isNotEmpty(); } } diff --git a/remote-api/src/test/java/org/alfresco/rest/workflow/api/tests/DeploymentWorkflowApiTest.java b/remote-api/src/test/java/org/alfresco/rest/workflow/api/tests/DeploymentWorkflowApiTest.java index b74af43523..48b767529e 100644 --- a/remote-api/src/test/java/org/alfresco/rest/workflow/api/tests/DeploymentWorkflowApiTest.java +++ b/remote-api/src/test/java/org/alfresco/rest/workflow/api/tests/DeploymentWorkflowApiTest.java @@ -38,6 +38,7 @@ import java.util.Map; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.workflow.WorkflowDeployer; import org.alfresco.rest.api.tests.AbstractTestFixture; import org.alfresco.rest.api.tests.RepoService.TestNetwork; import org.alfresco.rest.api.tests.client.PublicApiClient.ListResponse; @@ -128,7 +129,7 @@ public class DeploymentWorkflowApiTest extends EnterpriseWorkflowTestApi Deployment adhocDeployment = deploymentMap.get("adhoc.bpmn20.xml"); assertEquals(activitiDeployment.getId(), adhocDeployment.getId()); - assertEquals(activitiDeployment.getCategory(), adhocDeployment.getCategory()); + assertEquals(activitiDeployment.getCategory(), WorkflowDeployer.CATEGORY_FULL_ACCESS); assertEquals(activitiDeployment.getName(), adhocDeployment.getName()); assertEquals(activitiDeployment.getDeploymentTime(), adhocDeployment.getDeployedAt()); @@ -251,7 +252,7 @@ public class DeploymentWorkflowApiTest extends EnterpriseWorkflowTestApi assertNotNull(deployment); assertEquals(activitiDeployment.getId(), deployment.getId()); - assertEquals(activitiDeployment.getCategory(), deployment.getCategory()); + assertEquals(activitiDeployment.getCategory(), WorkflowDeployer.CATEGORY_FULL_ACCESS); assertEquals(activitiDeployment.getName(), deployment.getName()); assertEquals(activitiDeployment.getDeploymentTime(), deployment.getDeployedAt()); diff --git a/repository/src/main/java/org/alfresco/repo/jscript/RhinoScriptProcessor.java b/repository/src/main/java/org/alfresco/repo/jscript/RhinoScriptProcessor.java index 7f06ac4f95..dee9b5cffc 100644 --- a/repository/src/main/java/org/alfresco/repo/jscript/RhinoScriptProcessor.java +++ b/repository/src/main/java/org/alfresco/repo/jscript/RhinoScriptProcessor.java @@ -270,25 +270,33 @@ public class RhinoScriptProcessor extends BaseProcessor implements ScriptProcess */ public Object executeString(String source, Map model) { - try - { - // compile the script based on the node content - Script script; - Context cx = Context.enter(); - try - { - script = cx.compileString(resolveScriptImports(source), "AlfrescoJS", 1, null); - } - finally - { - Context.exit(); - } - return executeScriptImpl(script, model, true, "string script"); - } - catch (Throwable err) - { - throw new ScriptException("Failed to execute supplied script: " + err.getMessage(), err); - } + return executeString(source, model, false); + } + + /** + * @see org.alfresco.service.cmr.repository.ScriptProcessor#executeString(java.lang.String, java.util.Map, boolean) + */ + public Object executeString(String source, Map model, boolean secure) + { + try + { + // compile the script based on the node content + Script script; + Context cx = Context.enter(); + try + { + script = cx.compileString(resolveScriptImports(source), "AlfrescoJS", 1, null); + } + finally + { + Context.exit(); + } + return executeScriptImpl(script, model, secure, "string script"); + } + catch (Throwable err) + { + throw new ScriptException("Failed to execute supplied script: " + err.getMessage(), err); + } } /** diff --git a/repository/src/main/java/org/alfresco/repo/processor/ScriptServiceImpl.java b/repository/src/main/java/org/alfresco/repo/processor/ScriptServiceImpl.java index cfced0f9a4..a517bb7c81 100644 --- a/repository/src/main/java/org/alfresco/repo/processor/ScriptServiceImpl.java +++ b/repository/src/main/java/org/alfresco/repo/processor/ScriptServiceImpl.java @@ -188,6 +188,15 @@ public class ScriptServiceImpl implements ScriptService throws ScriptException { return executeScriptString(this.defaultScriptProcessor, script, model); + } + + /** + * @see org.alfresco.service.cmr.repository.ScriptService#executeScriptString(java.lang.String, java.util.Map, boolean) + */ + public Object executeScriptString(String script, Map model, boolean secure) + throws ScriptException + { + return executeScriptString(this.defaultScriptProcessor, script, model, secure); } /** @@ -197,12 +206,23 @@ public class ScriptServiceImpl implements ScriptService throws ScriptException { ScriptProcessor scriptProcessor = lookupScriptProcessor(engine); - return executeString(scriptProcessor, script, model); - } + return executeString(scriptProcessor, script, model, false); + } + + /** + * @see org.alfresco.service.cmr.repository.ScriptService#executeScriptString(java.lang.String, java.util.Map, boolean) + */ + public Object executeScriptString(String engine, String script, Map model, boolean secure) + throws ScriptException + { + ScriptProcessor scriptProcessor = lookupScriptProcessor(engine); + return executeString(scriptProcessor, script, model, secure); + } /** * Execute script - * + * + * @param processor the script processor that will be responsible for supplied script execution * @param location the location of the script * @param model context model * @return Object the result of the script @@ -227,6 +247,7 @@ public class ScriptServiceImpl implements ScriptService /** * Execute script * + * @param processor the script processor that will be responsible for supplied script execution * @param scriptRef the script node reference * @param contentProp the content property of the script * @param model the context model @@ -251,7 +272,8 @@ public class ScriptServiceImpl implements ScriptService /** * Execute script - * + * + * @param processor the script processor that will be responsible for supplied script execution * @param location the classpath string locating the script * @param model the context model * @return Object the result of the script @@ -275,12 +297,15 @@ public class ScriptServiceImpl implements ScriptService /** * Execute script string - * + * + * @param processor the script processor that will be responsible for supplied script execution * @param script the script string - * @param model the context model + * @param model the context model + * @param secure the flag indicating if string script is considered secure (e.g., if it comes from classpath) + * if true it will have access to the full execution context, if false the script will be executed in a sandbox context * @return Object the result of the script */ - protected Object executeString(ScriptProcessor processor, String script, Map model) + protected Object executeString(ScriptProcessor processor, String script, Map model, boolean secure) { ParameterCheck.mandatoryString("script", script); @@ -290,7 +315,7 @@ public class ScriptServiceImpl implements ScriptService } try { - return processor.executeString(script, model); + return processor.executeString(script, model, secure); } catch (Throwable err) { diff --git a/repository/src/main/java/org/alfresco/repo/workflow/WorkflowComponent.java b/repository/src/main/java/org/alfresco/repo/workflow/WorkflowComponent.java index d2db428450..d2622035d5 100644 --- a/repository/src/main/java/org/alfresco/repo/workflow/WorkflowComponent.java +++ b/repository/src/main/java/org/alfresco/repo/workflow/WorkflowComponent.java @@ -74,6 +74,18 @@ public interface WorkflowComponent * @since 4.0 */ public WorkflowDeployment deployDefinition(InputStream workflowDefinition, String mimetype, String name); + + /** + * Deploy a Workflow Definition + * + * @param workflowDefinition the content object containing the definition + * @param mimetype (optional) the mime type of the workflow definition + * @param name (optional) a name to represent the deployment + * @param fullAccess true if category should be defined in order to consider the deployment secure + * @return workflow deployment descriptor + * @since 4.0 + */ + public WorkflowDeployment deployDefinition(InputStream workflowDefinition, String mimetype, String name, boolean fullAccess); /** * Is the specified Workflow Definition already deployed? @@ -86,7 +98,14 @@ public interface WorkflowComponent * @return true => already deployed */ public boolean isDefinitionDeployed(InputStream workflowDefinition, String mimetype); - + + /** + * Sets the deployment category if applicable to allow the workflow to have full access + * + * @param workflowDefinition the definition to check + */ + public void checkDeploymentCategory(InputStream workflowDefinition); + /** * Undeploy an exisiting Workflow Definition * diff --git a/repository/src/main/java/org/alfresco/repo/workflow/WorkflowDeployer.java b/repository/src/main/java/org/alfresco/repo/workflow/WorkflowDeployer.java index 2fd42c0356..e99e943836 100644 --- a/repository/src/main/java/org/alfresco/repo/workflow/WorkflowDeployer.java +++ b/repository/src/main/java/org/alfresco/repo/workflow/WorkflowDeployer.java @@ -85,6 +85,7 @@ public class WorkflowDeployer extends AbstractLifecycleBean public static final String REDEPLOY = "redeploy"; public static final String CATEGORY_ALFRESCO_INTERNAL = "http://alfresco.org/workflows/internal"; + public static final String CATEGORY_FULL_ACCESS = "http://alfresco.org/workflows/fullAccess"; // Dependencies private TransactionService transactionService; @@ -306,12 +307,14 @@ public class WorkflowDeployer extends AbstractLifecycleBean if (!redeploy && workflowService.isDefinitionDeployed(engineId, workflowResource.getInputStream(), mimetype)) { if (logger.isDebugEnabled()) - logger.debug("Workflow deployer: Definition '" + location + "' already deployed"); + { + logger.debug("Workflow deployer: Definition '" + location + "' already deployed. Checking deploymentcategory..."); + } + workflowService.checkDeploymentCategory(engineId, workflowResource.getInputStream()); } else { - WorkflowDeployment deployment = workflowService.deployDefinition(engineId, workflowResource.getInputStream(), - mimetype, workflowResource.getFilename()); + WorkflowDeployment deployment = workflowService.deployDefinition(engineId, workflowResource.getInputStream(), mimetype, workflowResource.getFilename(), true); logDeployment(location, deployment); } } diff --git a/repository/src/main/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java b/repository/src/main/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java index 85fbcab889..86d1116db7 100644 --- a/repository/src/main/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java +++ b/repository/src/main/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java @@ -232,9 +232,20 @@ public class WorkflowServiceImpl implements WorkflowService * .lang.String, java.io.InputStream, java.lang.String, java.lang.String) */ public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype, String name) + { + return deployDefinition(engineId, workflowDefinition, mimetype, name, false); + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.service.cmr.workflow.WorkflowService#deployDefinition(java + * .lang.String, java.io.InputStream, java.lang.String, java.lang.String, boolean) + */ + public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype, String name, boolean fullAccess) { WorkflowComponent component = getWorkflowComponent(engineId); - WorkflowDeployment deployment = component.deployDefinition(workflowDefinition, mimetype, name); + WorkflowDeployment deployment = component.deployDefinition(workflowDefinition, mimetype, name, fullAccess); if (logger.isDebugEnabled() && deployment.getProblems().length > 0) { @@ -277,6 +288,18 @@ public class WorkflowServiceImpl implements WorkflowService return component.isDefinitionDeployed(workflowDefinition, mimetype); } + /* + * (non-Javadoc) + * @see + * org.alfresco.service.cmr.workflow.WorkflowService#checkDeploymentCategory + * (java.lang.String, java.io.InputStream) + */ + public void checkDeploymentCategory(String engineId, InputStream workflowDefinition) + { + WorkflowComponent component = getWorkflowComponent(engineId); + component.checkDeploymentCategory(workflowDefinition); + } + /* * (non-Javadoc) * @see diff --git a/repository/src/main/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java b/repository/src/main/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java index 106b5b679c..9166a5de5e 100644 --- a/repository/src/main/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java +++ b/repository/src/main/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.workflow.activiti; @@ -242,45 +242,45 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine /** * {@inheritDoc} */ - public WorkflowInstance cancelWorkflow(String workflowId) - { - String localId = createLocalId(workflowId); - try - { - ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(localId).singleResult(); - if (processInstance == null) - { - throw new WorkflowException(messageService.getMessage(ERR_CANCEL_UNEXISTING_WORKFLOW)); - } - - // TODO: Cancel VS delete? - // Delete the process instance - runtimeService.deleteProcessInstance(processInstance.getId(), WorkflowConstants.PROP_CANCELLED); - - // Convert historic process instance - HistoricProcessInstance deletedInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstance.getId()) - .singleResult(); - WorkflowInstance result = typeConverter.convert(deletedInstance); - - // Delete the historic process instance - // MNT-15498 - if (!activitiUtil.isRetentionHistoricProcessInstanceEnabled()) - { - historyService.deleteHistoricProcessInstance(deletedInstance.getId()); - } - - return result; - } - catch (ActivitiException ae) - { - String msg = messageService.getMessage(ERR_CANCEL_WORKFLOW); - if (logger.isDebugEnabled()) - { - logger.debug(msg, ae); - } - throw new WorkflowException(msg, ae); - } - } + public WorkflowInstance cancelWorkflow(String workflowId) + { + String localId = createLocalId(workflowId); + try + { + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(localId).singleResult(); + if (processInstance == null) + { + throw new WorkflowException(messageService.getMessage(ERR_CANCEL_UNEXISTING_WORKFLOW)); + } + + // TODO: Cancel VS delete? + // Delete the process instance + runtimeService.deleteProcessInstance(processInstance.getId(), WorkflowConstants.PROP_CANCELLED); + + // Convert historic process instance + HistoricProcessInstance deletedInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstance.getId()) + .singleResult(); + WorkflowInstance result = typeConverter.convert(deletedInstance); + + // Delete the historic process instance + // MNT-15498 + if (!activitiUtil.isRetentionHistoricProcessInstanceEnabled()) + { + historyService.deleteHistoricProcessInstance(deletedInstance.getId()); + } + + return result; + } + catch (ActivitiException ae) + { + String msg = messageService.getMessage(ERR_CANCEL_WORKFLOW); + if (logger.isDebugEnabled()) + { + logger.debug(msg, ae); + } + throw new WorkflowException(msg, ae); + } + } /** * {@inheritDoc} @@ -336,6 +336,14 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine * {@inheritDoc} */ public WorkflowDeployment deployDefinition(InputStream workflowDefinition, String mimetype, String name) + { + return deployDefinition(workflowDefinition, mimetype, name, false); + } + + /** + * {@inheritDoc} + */ + public WorkflowDeployment deployDefinition(InputStream workflowDefinition, String mimetype, String name, boolean fullAccess) { try { @@ -363,6 +371,10 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine { repoService.setDeploymentCategory(deployment.getId(), WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL); } + else if (fullAccess) + { + repoService.setDeploymentCategory(deployment.getId(), WorkflowDeployer.CATEGORY_FULL_ACCESS); + } } // No problems can be added to the WorkflowDeployment, warnings are @@ -1005,6 +1017,46 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine } } + /** + * {@inheritDoc} + */ + public void checkDeploymentCategory(InputStream workflowDefinition) + { + try + { + String key = getProcessKey(workflowDefinition); + ProcessDefinition pd = activitiUtil.getProcessDefinitionByKey(key); + String deploymentId = pd.getDeploymentId(); + + List definitionList = repoService.createProcessDefinitionQuery().deploymentId(deploymentId).list(); + if (definitionList != null && definitionList.size() > 0) + { + boolean internalCategory = true; + for (ProcessDefinition processDefinition : definitionList) + { + if (WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL.equals(processDefinition.getCategory()) == false) + { + internalCategory = false; + break; + } + } + + if (!internalCategory) + { + repoService.setDeploymentCategory(deploymentId, WorkflowDeployer.CATEGORY_FULL_ACCESS); + } + } + + } + catch (Exception e) + { + if (logger.isDebugEnabled()) + { + logger.debug("Category was not set: " + e.getMessage(), e); + } + } + } + private String getProcessKey(InputStream workflowDefinition) throws Exception { try diff --git a/repository/src/main/java/org/alfresco/repo/workflow/activiti/script/ActivitiScriptBase.java b/repository/src/main/java/org/alfresco/repo/workflow/activiti/script/ActivitiScriptBase.java index cdb8f19c28..1dfee2d75f 100644 --- a/repository/src/main/java/org/alfresco/repo/workflow/activiti/script/ActivitiScriptBase.java +++ b/repository/src/main/java/org/alfresco/repo/workflow/activiti/script/ActivitiScriptBase.java @@ -32,8 +32,10 @@ import org.activiti.engine.delegate.VariableScope; import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.activiti.engine.impl.context.Context; import org.activiti.engine.impl.el.Expression; +import org.activiti.engine.impl.persistence.entity.DeploymentEntity; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.workflow.WorkflowDeployer; import org.alfresco.repo.workflow.activiti.ActivitiConstants; import org.alfresco.repo.workflow.activiti.ActivitiScriptNode; import org.alfresco.service.ServiceRegistry; @@ -102,14 +104,18 @@ public class ActivitiScriptBase { // Execute the script using the appropriate processor Object scriptResult = null; + + // Checks if current workflow is secure + boolean secure = isSecure(); + if (scriptProcessorName != null) { - scriptResult = getServiceRegistry().getScriptService().executeScriptString(scriptProcessorName, theScript, model); + scriptResult = getServiceRegistry().getScriptService().executeScriptString(scriptProcessorName, theScript, model, secure); } else { // Use default script-processor - scriptResult = getServiceRegistry().getScriptService().executeScriptString(theScript, model); + scriptResult = getServiceRegistry().getScriptService().executeScriptString(theScript, model, secure); } return scriptResult; @@ -142,6 +148,32 @@ public class ActivitiScriptBase throw new IllegalStateException("No ProcessEngineCOnfiguration found in active context"); } + /** + * Checks whether the workflow must be considered secure or not - based on {@link DeploymentEntity} category. + * If it is not considered secure, the workflow will be executed in sandbox context with more restrictions + * + * @return true if workflow is considered secure, false otherwise + */ + private boolean isSecure() + { + String category = null; + + try + { + if (Context.isExecutionContextActive()) + { + category = Context.getExecutionContext().getDeployment().getCategory(); + } + } + catch (Exception e) + { + // No action required + } + + // If the workflow is considered secure, the deployment entity category matches the condition (either internal or full access) + return category != null && (WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL.equals(category) || WorkflowDeployer.CATEGORY_FULL_ACCESS.equals(category)); + } + /** * Checks that the specified 'runAs' field * specifies a valid username. diff --git a/repository/src/main/java/org/alfresco/service/cmr/repository/ScriptProcessor.java b/repository/src/main/java/org/alfresco/service/cmr/repository/ScriptProcessor.java index d7475e9b98..bbd9d72d7c 100644 --- a/repository/src/main/java/org/alfresco/service/cmr/repository/ScriptProcessor.java +++ b/repository/src/main/java/org/alfresco/service/cmr/repository/ScriptProcessor.java @@ -70,10 +70,21 @@ public interface ScriptProcessor extends Processor * * @param script the script string * @param model the context model - * @return Obejct the result of the script + * @return Object the result of the script */ public Object executeString(String script, Map model); - + + /** + * Execute script string + * + * @param script the script string + * @param model the context model + * @param secure the flag that indicates if string is considered secure to be executed, i.e., it will have + * access to the full execution context instead of being executed in a sandbox context + * @return Object the result of the script + */ + public Object executeString(String script, Map model, boolean secure); + /** * Reset the processor - such as clearing any internal caches etc. */ diff --git a/repository/src/main/java/org/alfresco/service/cmr/repository/ScriptService.java b/repository/src/main/java/org/alfresco/service/cmr/repository/ScriptService.java index fc2b807432..e231c8433e 100644 --- a/repository/src/main/java/org/alfresco/service/cmr/repository/ScriptService.java +++ b/repository/src/main/java/org/alfresco/service/cmr/repository/ScriptService.java @@ -160,7 +160,22 @@ public interface ScriptService @Auditable(parameters = {"script", "model"}) public Object executeScriptString(String script, Map model) throws ScriptException; - + + /** + * Process a script against the supplied data model. Uses the default script engine. + * + * @param script Script content as a String. + * @param model Object model to process script against + * @param secure A flag indicating if string script is considered secure (e.g., if it comes from the classpath) + * If true it will have access to the full execution context, if false the script will be executed in a sandbox context (more restricted) + * @return output of the script (may be null or any valid wrapped JavaScript object) + * + * @throws ScriptException + */ + @Auditable(parameters = {"script", "model", "secure"}) + public Object executeScriptString(String script, Map model, boolean secure) + throws ScriptException; + /** * Process a script against the supplied data model. * @@ -175,7 +190,23 @@ public interface ScriptService @Auditable(parameters = {"engine", "script", "model"}) public Object executeScriptString(String engine, String script, Map model) throws ScriptException; - + + /** + * Process a script against the supplied data model. + * + * @param engine the script engine to use + * @param script Script content as a String. + * @param model Object model to process script against + * @param secure A flag indicating if string script is considered secure + * + * @return output of the script (may be null or any valid wrapped JavaScript object) + * + * @throws ScriptException + */ + @Auditable(parameters = {"engine", "script", "model", "secure"}) + public Object executeScriptString(String engine, String script, Map model, boolean secure) + throws ScriptException; + /** * Registers a script processor with the script service * diff --git a/repository/src/main/java/org/alfresco/service/cmr/workflow/WorkflowService.java b/repository/src/main/java/org/alfresco/service/cmr/workflow/WorkflowService.java index ffb27965e4..891f8cb21a 100644 --- a/repository/src/main/java/org/alfresco/service/cmr/workflow/WorkflowService.java +++ b/repository/src/main/java/org/alfresco/service/cmr/workflow/WorkflowService.java @@ -77,7 +77,24 @@ public interface WorkflowService parameters = {"engineId", "workflowDefinition", "mimetype", "name"}, recordable = {true, false, true, true}) public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype, String name); - + + /** + * Deploy a Workflow Definition to the Alfresco Repository + * + * @param engineId the bpm engine id + * @param workflowDefinition the workflow definition + * @param mimetype the mimetype of the workflow definition + * @param name a name representing the deployment + * @parm fullAccess true if workflow should be considered secure (e.g., if it is deployed in classpath) and should have full access to the execution context, + * false if it should be executed in a sandbox context (more restricted) + * @return workflow deployment descriptor + * @since 4.0 + */ + @Auditable( + parameters = {"engineId", "workflowDefinition", "mimetype", "name", "fullAccess"}, + recordable = {true, false, true, true, true}) + public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype, String name, boolean fullAccess); + /** * Deploy a Workflow Definition to the Alfresco Repository * @@ -117,6 +134,17 @@ public interface WorkflowService parameters = {"engineId", "workflowDefinition", "mimetype"}, recordable = {true, false, true}) public boolean isDefinitionDeployed(String engineId, InputStream workflowDefinition, String mimetype); + + /** + * Checks if the deployment for supplied workflow definition has the proper category + * + * @param engineId the bpm engine id + * @param workflowDefinition the definition to check + */ + @Auditable( + parameters = {"engineId", "workflowDefinition"}, + recordable = {true, false}) + public void checkDeploymentCategory(String engineId, InputStream workflowDefinition); /** * Undeploy an exisiting Workflow Definition diff --git a/repository/src/test/java/org/alfresco/repo/jscript/RhinoScriptTest.java b/repository/src/test/java/org/alfresco/repo/jscript/RhinoScriptTest.java index 8d7fb6f282..d4a3ecf407 100644 --- a/repository/src/test/java/org/alfresco/repo/jscript/RhinoScriptTest.java +++ b/repository/src/test/java/org/alfresco/repo/jscript/RhinoScriptTest.java @@ -25,42 +25,39 @@ */ package org.alfresco.repo.jscript; -import static org.junit.Assert.fail; - -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import junit.framework.TestCase; +import java.io.InputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.dictionary.DictionaryComponent; -import org.alfresco.repo.dictionary.DictionaryDAO; -import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.dictionary.DictionaryComponent; +import org.alfresco.repo.dictionary.DictionaryDAO; +import org.alfresco.repo.dictionary.M2Model; import org.alfresco.repo.node.BaseNodeServiceTest; import org.alfresco.repo.security.authentication.AuthenticationComponent; -import org.alfresco.repo.security.permissions.AccessDeniedException; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.ScriptService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.transaction.TransactionService; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.ScriptProcessor; +import org.alfresco.service.cmr.repository.ScriptService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; import org.alfresco.test_category.OwnJVMTestsCategory; import org.alfresco.util.ApplicationContextHelper; -import org.junit.experimental.categories.Category; -import org.mozilla.javascript.Context; -import org.mozilla.javascript.Scriptable; -import org.mozilla.javascript.ScriptableObject; +import org.junit.experimental.categories.Category; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; import org.springframework.context.ApplicationContext; + +import junit.framework.TestCase; /** @@ -434,7 +431,51 @@ public class RhinoScriptTest extends TestCase }); } - + + // MNT-21638 + public void testSecureScriptString() + { + boolean executed = executeSecureScriptString(TESTSCRIPT2, false); + assertFalse("Script shouldn't have been executed (secure = false)", executed); + + executed = executeSecureScriptString(TESTSCRIPT2, null); + assertFalse("Script shouldn't have been executed (secure = null)", executed); + + executed = executeSecureScriptString(TESTSCRIPT2, true); + assertTrue("Script should have been executed (secure = true)", executed); + } + + private boolean executeSecureScriptString(String script, Boolean secure) + { + return transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + public Boolean execute() throws Exception + { + StoreRef store = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "rhino_" + System.currentTimeMillis()); + NodeRef root = nodeService.getRootNode(store); + BaseNodeServiceTest.buildNodeGraph(nodeService, root); + + try + { + Map model = new HashMap(); + model.put("out", System.out); + + ScriptNode rootNode = new ScriptNode(root, serviceRegistry, null); + model.put("root", rootNode); + + // test executing a script directly as string + scriptService.executeScriptString("javascript", script, model, secure); + } + catch (Exception e) + { + return false; + } + + return true; + } + }); + } + private static final String TESTSCRIPT_CLASSPATH1 = "org/alfresco/repo/jscript/test_script1.js"; private static final String TESTSCRIPT_CLASSPATH2 = "org/alfresco/repo/jscript/test_script2.js"; private static final String TESTSCRIPT_CLASSPATH3 = "org/alfresco/repo/jscript/test_script3.js"; @@ -453,7 +494,12 @@ public class RhinoScriptTest extends TestCase "logger.log(\"child by name path: \" + childByNameNode.name);\r\n" + "var xpathResults = root.childrenByXPath(\"/*\");\r\n" + "logger.log(\"children of root from xpath: \" + xpathResults.length);\r\n"; - + + private static final String TESTSCRIPT2 = "var exec = new org.alfresco.util.exec.RuntimeExec();\r\n" + + "exec.setCommand([\"/bin/ls\"]);\r\n" + + "var res = exec.execute();\r\n" + + "java.lang.System.err.println(res.getStdOut());\r\n"; + private static final String BASIC_JAVA = "var list = com.google.common.collect.Lists.newArrayList();\n" + "root.nodeRef.getClass().forName(\"java.lang.ProcessBuilder\")"; diff --git a/repository/src/test/java/org/alfresco/repo/workflow/AbstractWorkflowServiceIntegrationTest.java b/repository/src/test/java/org/alfresco/repo/workflow/AbstractWorkflowServiceIntegrationTest.java index 29869837a5..e4de626df8 100644 --- a/repository/src/test/java/org/alfresco/repo/workflow/AbstractWorkflowServiceIntegrationTest.java +++ b/repository/src/test/java/org/alfresco/repo/workflow/AbstractWorkflowServiceIntegrationTest.java @@ -1247,7 +1247,15 @@ public abstract class AbstractWorkflowServiceIntegrationTest extends BaseSpringT WorkflowDefinition definition = deployment.getDefinition(); return definition; } - + + protected WorkflowDefinition deployDefinition(String resource, String name, boolean fullAccess) + { + InputStream input = getInputStream(resource); + WorkflowDeployment deployment = workflowService.deployDefinition(getEngine(), input, XML, name, fullAccess); + WorkflowDefinition definition = deployment.getDefinition(); + return definition; + } + protected abstract QName getAdhocProcessName(); diff --git a/repository/src/test/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowServiceIntegrationTest.java b/repository/src/test/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowServiceIntegrationTest.java index de77392398..b531b725ac 100644 --- a/repository/src/test/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowServiceIntegrationTest.java +++ b/repository/src/test/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowServiceIntegrationTest.java @@ -790,6 +790,45 @@ public class ActivitiWorkflowServiceIntegrationTest extends AbstractWorkflowServ assertNull("Workflow should not be deployed", workflowDef); } + /** + * + */ + @Test + public void testMNT21638_1() + { + WorkflowDefinition definition = deployDefinition("activiti/test-MNT21638-1.bpmn20.xml"); + + personManager.setUser(USER1); + + // Start the Workflow + try + { + WorkflowPath path = workflowService.startWorkflow(definition.getId(), null); + fail("Workflow should not have been executed"); + } + catch (Exception e) + { + // Do nothing + } + } + + /** + * + */ + @Test + public void testMNT21638_2() + { + WorkflowDefinition definition = deployDefinition("activiti/test-MNT21638-2.bpmn20.xml", "MNT21638", true); + + personManager.setUser(USER1); + + // Start the Workflow + WorkflowPath path = workflowService.startWorkflow(definition.getId(), null); + String instanceId = path.getInstance().getId(); + + assertNotNull(instanceId); + } + private NodeRef findWorkflowParent() { RepositoryLocation workflowLocation = (RepositoryLocation) diff --git a/repository/src/test/resources/activiti/test-MNT21638-1.bpmn20.xml b/repository/src/test/resources/activiti/test-MNT21638-1.bpmn20.xml new file mode 100644 index 0000000000..a76f556af2 --- /dev/null +++ b/repository/src/test/resources/activiti/test-MNT21638-1.bpmn20.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + var exec = new org.alfresco.util.exec.RuntimeExec(); + exec.setCommand(["/bin/ls"]); + var res = exec.execute(); + java.lang.System.err.println(res.getStdOut()); + + + + + + + admin + + + + + + + + + + \ No newline at end of file diff --git a/repository/src/test/resources/activiti/test-MNT21638-2.bpmn20.xml b/repository/src/test/resources/activiti/test-MNT21638-2.bpmn20.xml new file mode 100644 index 0000000000..8032641314 --- /dev/null +++ b/repository/src/test/resources/activiti/test-MNT21638-2.bpmn20.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + var exec = new org.alfresco.util.exec.RuntimeExec(); + exec.setCommand(["/bin/ls"]); + var res = exec.execute(); + java.lang.System.err.println(res.getStdOut()); + + + + + + + admin + + + + + + + + + + \ No newline at end of file