diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/PageBulkAction.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/PageBulkAction.java index ac5fa157435..dcc1658a1ba 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/PageBulkAction.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/PageBulkAction.java @@ -17,11 +17,14 @@ package com.evolveum.midpoint.web.page.admin.configuration; import com.evolveum.midpoint.model.api.ScriptExecutionException; +import com.evolveum.midpoint.model.api.ScriptExecutionResult; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.result.OperationResultStatus; import com.evolveum.midpoint.security.api.AuthorizationConstants; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SecurityViolationException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.web.application.AuthorizationAction; @@ -119,19 +122,22 @@ private void startPerformed(AjaxRequestTarget target) { if (bulkActionDto.isAsync()) { try { getScriptingService().evaluateExpressionInBackground(expression, task, result); - } catch (SchemaException e) { - result.recordFatalError("Couldn't submit bulk action to execution because of schema exception", e); + result.recordStatus(OperationResultStatus.IN_PROGRESS, task.getName() + " has been successfully submitted to execution"); + } catch (SchemaException|SecurityViolationException e) { + result.recordFatalError("Couldn't submit bulk action to execution", e); } } else { try { - getScriptingService().evaluateExpression(expression, task, result); - } catch (ScriptExecutionException e) { + ScriptExecutionResult executionResult = getScriptingService().evaluateExpression(expression, task, result); + result.recordStatus(OperationResultStatus.SUCCESS, "Action executed. Returned " + executionResult.getDataOutput().size() + " item(s). Console and data output available via 'Export to XML' function."); + result.addReturn("console", executionResult.getConsoleOutput()); + result.addCollectionOfSerializablesAsReturn("data", executionResult.getDataOutput()); + } catch (ScriptExecutionException|SchemaException|SecurityViolationException e) { result.recordFatalError("Couldn't execute bulk action", e); } } } - result.computeStatusIfUnknown(); showResult(result); target.add(getFeedbackPanel()); } diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/result/OperationResult.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/result/OperationResult.java index f123f794d43..0b3e0fde1c7 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/result/OperationResult.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/result/OperationResult.java @@ -21,9 +21,6 @@ import javax.xml.bind.JAXBElement; -import com.evolveum.midpoint.prism.util.CloneUtil; - -import org.apache.commons.collections.map.HashedMap; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; import org.w3c.dom.Document; @@ -37,7 +34,6 @@ import com.evolveum.midpoint.util.DebugDumpable; import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.util.exception.CommonException; -import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.EntryType; @@ -674,6 +670,10 @@ public void addCollectionOfSerializablesAsParam(String paramName, Collection value) { + addReturn(name, value != null ? new ArrayList(value) : null); + } + public void addArbitraryCollectionAsParam(String paramName, Collection values) { if (values != null) { ArrayList valuesAsStrings = new ArrayList(); diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelAuthorizationAction.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelAuthorizationAction.java index 5bc81f5f67d..43711e45630 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelAuthorizationAction.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelAuthorizationAction.java @@ -30,7 +30,8 @@ public enum ModelAuthorizationAction implements DisplayableValue { DISCOVER_CONNECTORS("discoverConnectors", "Discover Connectors", "DISCOVER_CONNECTORS_HELP"), ASSIGN("assign", "Assign", "ASSIGN_HELP"), - UNASSIGN("unassign", "Unassign", "UNASSIGN_HELP"); + UNASSIGN("unassign", "Unassign", "UNASSIGN_HELP"), + EXECUTE_SCRIPT("executeScript", "Execute script", "EXECUTE_SCRIPT_HELP"); private String url; private String label; diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ScriptExecutionResult.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ScriptExecutionResult.java new file mode 100644 index 00000000000..aa22be4529c --- /dev/null +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ScriptExecutionResult.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010-2014 Evolveum + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.evolveum.midpoint.model.api; + +import com.evolveum.midpoint.prism.Item; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Result of a script execution. + * + * @author mederly + */ +public class ScriptExecutionResult { + + private String consoleOutput; + private List dataOutput; // unmodifiable + always non-null + + public ScriptExecutionResult(String consoleOutput, List dataOutput) { + this.consoleOutput = consoleOutput; + if (dataOutput == null) { + dataOutput = new ArrayList<>(); + } + this.dataOutput = Collections.unmodifiableList(dataOutput); + } + + public String getConsoleOutput() { + return consoleOutput; + } + + public List getDataOutput() { + return dataOutput; + } +} diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ScriptingService.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ScriptingService.java index 994b1ebaa4a..b4af75b8e4a 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ScriptingService.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ScriptingService.java @@ -20,9 +20,9 @@ import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SecurityViolationException; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ScriptingExpressionType; -import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; /** @@ -46,7 +46,7 @@ public interface ScriptingService { * * TODO consider removing this method (it was meant as a simplified version of the method below) */ - public void evaluateExpressionInBackground(QName objectType, ObjectFilter filter, String actionName, Task task, OperationResult parentResult) throws SchemaException; + public void evaluateExpressionInBackground(QName objectType, ObjectFilter filter, String actionName, Task task, OperationResult parentResult) throws SchemaException, SecurityViolationException; /** * Asynchronously executes any scripting expression. @@ -59,7 +59,7 @@ public interface ScriptingService { * @param parentResult * @throws SchemaException */ - public void evaluateExpressionInBackground(ScriptingExpressionType expression, Task task, OperationResult parentResult) throws SchemaException; + public void evaluateExpressionInBackground(ScriptingExpressionType expression, Task task, OperationResult parentResult) throws SchemaException, SecurityViolationException; /** * Synchronously executes any scripting expression (with no input data). @@ -72,5 +72,5 @@ public interface ScriptingService { * TODO return ExecutionContext (requires moving the context to model api) */ - public void evaluateExpression(ScriptingExpressionType expression, Task task, OperationResult result) throws ScriptExecutionException; + public ScriptExecutionResult evaluateExpression(ScriptingExpressionType expression, Task task, OperationResult result) throws ScriptExecutionException, SchemaException, SecurityViolationException; } \ No newline at end of file diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelWebService.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelWebService.java index 6134d4c3dc2..7ba692fe9ef 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelWebService.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelWebService.java @@ -17,6 +17,8 @@ import com.evolveum.midpoint.model.api.ModelExecuteOptions; import com.evolveum.midpoint.model.api.ModelPort; +import com.evolveum.midpoint.model.api.ScriptExecutionResult; +import com.evolveum.midpoint.model.api.ScriptingService; import com.evolveum.midpoint.model.common.util.AbstractModelWebService; import com.evolveum.midpoint.model.impl.controller.ModelController; import com.evolveum.midpoint.model.impl.scripting.Data; @@ -108,7 +110,7 @@ public class ModelWebService extends AbstractModelWebService implements ModelPor private ModelController modelController; @Autowired - private ScriptingExpressionEvaluator scriptingExpressionEvaluator; + private ScriptingService scriptingService; @Override public void getObject(QName objectType, String oid, SelectorQualifiedGetOptionsType optionsType, @@ -273,22 +275,22 @@ private ExecuteScriptsResponseType doExecuteScripts(List> scripts try { for (JAXBElement script : scriptsToExecute) { - ExecutionContext outputContext = scriptingExpressionEvaluator.evaluateExpression((ScriptingExpressionType) script.getValue(), task, result); + ScriptExecutionResult executionResult = scriptingService.evaluateExpression((ScriptingExpressionType) script.getValue(), task, result); SingleScriptOutputType output = new SingleScriptOutputType(); outputs.getOutput().add(output); - output.setTextOutput(outputContext.getConsoleOutput()); + output.setTextOutput(executionResult.getConsoleOutput()); if (options == null || options.getOutputFormat() == null || options.getOutputFormat() == OutputFormatType.XML) { - output.setXmlData(prepareXmlData(outputContext.getFinalOutput())); + output.setXmlData(prepareXmlData(executionResult.getDataOutput())); } else { // temporarily we send serialized XML in the case of MSL output - ItemListType jaxbOutput = prepareXmlData(outputContext.getFinalOutput()); + ItemListType jaxbOutput = prepareXmlData(executionResult.getDataOutput()); output.setMslData(prismContext.serializeAnyData(jaxbOutput, SchemaConstants.C_VALUE, PrismContext.LANG_XML)); } } result.computeStatusIfUnknown(); - } catch (ScriptExecutionException|JAXBException|SchemaException|RuntimeException e) { + } catch (ScriptExecutionException|JAXBException|SchemaException|RuntimeException|SecurityViolationException e) { result.recordFatalError(e.getMessage(), e); LoggingUtils.logException(LOGGER, "Exception while executing script", e); } @@ -297,10 +299,10 @@ private ExecuteScriptsResponseType doExecuteScripts(List> scripts return response; } - private ItemListType prepareXmlData(Data output) throws JAXBException, SchemaException { + private ItemListType prepareXmlData(List output) throws JAXBException, SchemaException { ItemListType itemListType = new ItemListType(); if (output != null) { - for (Item item : output.getData()) { + for (Item item : output) { RawType rawType = prismContext.toRawType(item); itemListType.getItem().add(rawType); } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java index 682e89e158c..c3fffbb6030 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java @@ -22,17 +22,17 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Set; -import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; import com.evolveum.midpoint.model.api.ScriptExecutionException; +import com.evolveum.midpoint.model.api.ScriptExecutionResult; import com.evolveum.midpoint.model.api.ScriptingService; import com.evolveum.midpoint.model.api.TaskService; import com.evolveum.midpoint.model.api.WorkflowService; import com.evolveum.midpoint.model.api.hooks.ReadHook; +import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; import com.evolveum.midpoint.model.impl.scripting.ScriptingExpressionEvaluator; import com.evolveum.midpoint.prism.parser.XNodeSerializer; import com.evolveum.midpoint.prism.polystring.PolyString; @@ -44,13 +44,8 @@ import org.apache.commons.lang.NotImplementedException; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; -import org.apache.cxf.phase.Phase; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import com.evolveum.midpoint.audit.api.AuditEventRecord; @@ -61,7 +56,6 @@ import com.evolveum.midpoint.common.crypto.CryptoUtil; import com.evolveum.midpoint.common.refinery.LayerRefinedAttributeDefinition; import com.evolveum.midpoint.common.refinery.LayerRefinedObjectClassDefinition; -import com.evolveum.midpoint.common.refinery.LayerRefinedResourceSchema; import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; import com.evolveum.midpoint.common.refinery.RefinedResourceSchema; import com.evolveum.midpoint.model.api.ModelAuthorizationAction; @@ -117,18 +111,14 @@ import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.result.OperationResultRunner; import com.evolveum.midpoint.schema.result.OperationResultStatus; -import com.evolveum.midpoint.schema.util.ShadowUtil; -import com.evolveum.midpoint.security.api.Authorization; import com.evolveum.midpoint.security.api.AuthorizationConstants; import com.evolveum.midpoint.security.api.ObjectSecurityConstraints; -import com.evolveum.midpoint.security.api.OwnerResolver; import com.evolveum.midpoint.security.api.SecurityEnforcer; import com.evolveum.midpoint.security.api.UserProfileService; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.task.api.TaskManager; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.DisplayableValue; -import com.evolveum.midpoint.util.exception.CommonException; import com.evolveum.midpoint.util.exception.CommunicationException; import com.evolveum.midpoint.util.exception.ConfigurationException; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; @@ -145,7 +135,6 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationPhaseType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorHostType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.LayerType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectSynchronizationType; @@ -1856,18 +1845,26 @@ public void releaseWorkItem(String workItemId, OperationResult parentResult) { //region Scripting (bulk actions) @Override - public void evaluateExpressionInBackground(QName objectType, ObjectFilter filter, String actionName, Task task, OperationResult parentResult) throws SchemaException { + public void evaluateExpressionInBackground(QName objectType, ObjectFilter filter, String actionName, Task task, OperationResult parentResult) throws SchemaException, SecurityViolationException { + checkScriptingAuthorization(parentResult); scriptingExpressionEvaluator.evaluateExpressionInBackground(objectType, filter, actionName, task, parentResult); } @Override - public void evaluateExpressionInBackground(ScriptingExpressionType expression, Task task, OperationResult parentResult) throws SchemaException { + public void evaluateExpressionInBackground(ScriptingExpressionType expression, Task task, OperationResult parentResult) throws SchemaException, SecurityViolationException { + checkScriptingAuthorization(parentResult); scriptingExpressionEvaluator.evaluateExpressionInBackground(expression, task, parentResult); } @Override - public void evaluateExpression(ScriptingExpressionType expression, Task task, OperationResult result) throws ScriptExecutionException { - scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + public ScriptExecutionResult evaluateExpression(ScriptingExpressionType expression, Task task, OperationResult result) throws ScriptExecutionException, SchemaException, SecurityViolationException { + checkScriptingAuthorization(result); + ExecutionContext executionContext = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + return executionContext.toExecutionResult(); + } + + private void checkScriptingAuthorization(OperationResult parentResult) throws SchemaException, SecurityViolationException { + securityEnforcer.authorize(ModelAuthorizationAction.EXECUTE_SCRIPT.getUrl(), null, null, null, null, null, parentResult); } //endregion diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ExecutionContext.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ExecutionContext.java index f567e7921ba..d4cd0024912 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ExecutionContext.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ExecutionContext.java @@ -16,12 +16,14 @@ package com.evolveum.midpoint.model.impl.scripting; +import com.evolveum.midpoint.model.api.ScriptExecutionResult; import com.evolveum.midpoint.prism.Item; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -80,4 +82,13 @@ public Data getFinalOutput() { public void setFinalOutput(Data finalOutput) { this.finalOutput = finalOutput; } + + public ScriptExecutionResult toExecutionResult() { + List items = null; + if (getFinalOutput() != null) { + items = getFinalOutput().getData(); + } + ScriptExecutionResult result = new ScriptExecutionResult(getConsoleOutput(), items); + return result; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ScriptExecutionTaskHandler.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ScriptExecutionTaskHandler.java index af6c9841a91..80d5501e971 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ScriptExecutionTaskHandler.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ScriptExecutionTaskHandler.java @@ -17,6 +17,8 @@ package com.evolveum.midpoint.model.impl.scripting; import com.evolveum.midpoint.model.api.ScriptExecutionException; +import com.evolveum.midpoint.model.api.ScriptExecutionResult; +import com.evolveum.midpoint.model.api.ScriptingService; import com.evolveum.midpoint.prism.PrismProperty; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; @@ -25,6 +27,8 @@ import com.evolveum.midpoint.task.api.TaskHandler; import com.evolveum.midpoint.task.api.TaskManager; import com.evolveum.midpoint.task.api.TaskRunResult; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SecurityViolationException; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; @@ -52,7 +56,7 @@ public class ScriptExecutionTaskHandler implements TaskHandler { private TaskManager taskManager; @Autowired - private ScriptingExpressionEvaluator scriptingExpressionEvaluator; + private ScriptingService scriptingService; @Override public TaskRunResult run(Task task) { @@ -61,16 +65,18 @@ public TaskRunResult run(Task task) { TaskRunResult runResult = new TaskRunResult(); PrismProperty executeScriptProperty = task.getExtensionProperty(SchemaConstants.SE_EXECUTE_SCRIPT); - if (executeScriptProperty == null) { + if (executeScriptProperty == null || executeScriptProperty.getValue().getValue() == null || + executeScriptProperty.getValue().getValue().getScriptingExpression() == null) { throw new IllegalStateException("There's no script to be run in task " + task + " (property " + SchemaConstants.SE_EXECUTE_SCRIPT + ")"); } try { - ExecutionContext resultingContext = scriptingExpressionEvaluator.evaluateExpression(executeScriptProperty.getRealValue(), task, result); - LOGGER.debug("Execution result:\n", resultingContext.getConsoleOutput()); + ScriptExecutionResult executionResult = scriptingService.evaluateExpression(executeScriptProperty.getValue().getValue().getScriptingExpression().getValue(), task, result); + LOGGER.debug("Execution output: {} item(s)", executionResult.getDataOutput().size()); + LOGGER.debug("Execution result:\n", executionResult.getConsoleOutput()); result.computeStatus(); runResult.setRunResultStatus(TaskRunResult.TaskRunResultStatus.FINISHED); - } catch (ScriptExecutionException e) { + } catch (ScriptExecutionException|SecurityViolationException|SchemaException e) { result.recordFatalError("Couldn't execute script: " + e.getMessage(), e); LoggingUtils.logException(LOGGER, "Couldn't execute script", e); runResult.setRunResultStatus(TaskRunResult.TaskRunResultStatus.PERMANENT_ERROR);