diff --git a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java index 93ab3f6f131..0f852a5c649 100644 --- a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java +++ b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java @@ -1158,14 +1158,12 @@ public Object runScriptOnConnector(ScriptContext request, OperationOptions optio public Object runScriptOnResource(ScriptContext request, OperationOptions options) { try { - resource.runScript(request.getScriptLanguage(), request.getScriptText(), request.getScriptArguments()); + return resource.runScript(request.getScriptLanguage(), request.getScriptText(), request.getScriptArguments()); } catch (IllegalArgumentException e) { throw new ConnectorException(e.getMessage(), e); } catch (FileNotFoundException e) { throw new ConnectorIOException(e.getMessage(), e); } - - return null; } /** diff --git a/icf-connectors/dummy-resource/src/main/java/com/evolveum/icf/dummy/resource/DummyResource.java b/icf-connectors/dummy-resource/src/main/java/com/evolveum/icf/dummy/resource/DummyResource.java index e27cf326409..10d4f59c5a6 100644 --- a/icf-connectors/dummy-resource/src/main/java/com/evolveum/icf/dummy/resource/DummyResource.java +++ b/icf-connectors/dummy-resource/src/main/java/com/evolveum/icf/dummy/resource/DummyResource.java @@ -118,6 +118,8 @@ public class DummyResource implements DebugDumpable { public static final String VALUE_COOKIE = "cookie"; public static final String SCRIPT_LANGUAGE_POWERFAIL = "powerfail"; + public static final String SCRIPT_LANGUAGE_PARROT = "parrot"; + public static final String POWERFAIL_ARG_ERROR = "error"; public static final String POWERFAIL_ARG_ERROR_GENERIC = "generic"; public static final String POWERFAIL_ARG_ERROR_RUNTIME = "runtime"; @@ -819,7 +821,7 @@ public void purgeScriptHistory() { * * @param scriptCode code of the script */ - public void runScript(String language, String scriptCode, Map params) throws FileNotFoundException { + public String runScript(String language, String scriptCode, Map params) throws FileNotFoundException { scriptHistory.add(new ScriptHistoryEntry(language, scriptCode, params)); if (SCRIPT_LANGUAGE_POWERFAIL.equals(language)) { Object errorArg = params.get(POWERFAIL_ARG_ERROR); @@ -832,7 +834,10 @@ public void runScript(String language, String scriptCode, Map pa } else if (POWERFAIL_ARG_ERROR_IO.equals(errorArg)) { throw new FileNotFoundException("Booom! PowerFail script failed (IO)"); } + } else if (SCRIPT_LANGUAGE_PARROT.equals(language)) { + return scriptCode.toUpperCase(); } + return null; } /** diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd index bbd7220f309..f1d2351e069 100755 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd @@ -16076,6 +16076,7 @@ + diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd index 5f2fdaa159b..8120dd5a675 100644 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd @@ -1931,11 +1931,11 @@ - +

- Identifier (URI) of the state. This is the value that is used in the + Identifier of the state. This is the value that is used in the lifecycleState property.

@@ -2023,11 +2023,11 @@
- +

- Identifier (URI) of the state that is the target of the transition. + Identifier of the state that is the target of the transition.

diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/annotation/Experimental.java b/infra/util/src/main/java/com/evolveum/midpoint/util/annotation/Experimental.java new file mode 100644 index 00000000000..80f0cb7861d --- /dev/null +++ b/infra/util/src/main/java/com/evolveum/midpoint/util/annotation/Experimental.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2018 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.util.annotation; + +/** + * Marks experimental code. + * The code marked with this annotation is not officially supported. + * Use at your own risk. + * + * @author Radovan Semancik + */ +public @interface Experimental { + +} diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/expr/MidpointFunctions.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/expr/MidpointFunctions.java index a89a7315174..c90f9a8230b 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/expr/MidpointFunctions.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/expr/MidpointFunctions.java @@ -1105,5 +1105,15 @@ TaskType submitTaskFromTemplate(String templateTaskOid, Map exten ModelElementContext getFocusContext(); ModelProjectionContext getProjectionContext(); + + Object executeAdHocProvisioningScript(ResourceType resource, String language, String code) + throws SchemaException, ObjectNotFoundException, + ExpressionEvaluationException, CommunicationException, ConfigurationException, + SecurityViolationException, ObjectAlreadyExistsException; + + Object executeAdHocProvisioningScript(String resourceOid, String language, String code) + throws SchemaException, ObjectNotFoundException, + ExpressionEvaluationException, CommunicationException, ConfigurationException, + SecurityViolationException, ObjectAlreadyExistsException; } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/MidpointFunctionsImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/MidpointFunctionsImpl.java index 725199e4298..3595a18fbae 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/MidpointFunctionsImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/MidpointFunctionsImpl.java @@ -33,6 +33,7 @@ import com.evolveum.midpoint.model.common.ConstantsManager; import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionEvaluationContext; import com.evolveum.midpoint.model.impl.ModelObjectResolver; +import com.evolveum.midpoint.model.impl.lens.EvaluatedAssignmentImpl; import com.evolveum.midpoint.model.impl.lens.LensContext; import com.evolveum.midpoint.model.impl.lens.LensFocusContext; import com.evolveum.midpoint.model.impl.lens.LensProjectionContext; @@ -41,6 +42,7 @@ import com.evolveum.midpoint.prism.crypto.EncryptionException; import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.prism.delta.ChangeType; +import com.evolveum.midpoint.prism.delta.DeltaSetTriple; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.marshaller.ItemPathHolder; @@ -62,6 +64,7 @@ import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.Holder; import com.evolveum.midpoint.util.LocalizableMessage; +import com.evolveum.midpoint.util.annotation.Experimental; import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; @@ -415,6 +418,34 @@ public boolean isDirectlyAssigned(ObjectType target) { return isDirectlyAssigned(target.getOid()); } + // EXPERIMENTAL!! + @Experimental + public boolean hasActiveAssignmentTargetSubtype(String roleSubtype) { + LensContext lensContext = ModelExpressionThreadLocalHolder.getLensContext(); + if (lensContext == null) { + throw new UnsupportedOperationException("hasActiveAssignmentRoleSubtype works only with model context"); + } + DeltaSetTriple> evaluatedAssignmentTriple = lensContext.getEvaluatedAssignmentTriple(); + if (evaluatedAssignmentTriple == null) { + throw new UnsupportedOperationException("hasActiveAssignmentRoleSubtype works only with evaluatedAssignmentTriple"); + } + Collection> nonNegativeEvaluatedAssignments = evaluatedAssignmentTriple.getNonNegativeValues(); + if (nonNegativeEvaluatedAssignments == null) { + return false; + } + for (EvaluatedAssignmentImpl nonNegativeEvaluatedAssignment : nonNegativeEvaluatedAssignments) { + PrismObject target = nonNegativeEvaluatedAssignment.getTarget(); + if (target == null) { + continue; + } + Collection targetSubtypes = ObjectTypeUtil.getSubtypeValues((PrismObject) target); + if (targetSubtypes.contains(roleSubtype)) { + return true; + } + } + return false; + } + @Override public ShadowType getLinkedShadow(FocusType focus, ResourceType resource) throws SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { @@ -1586,4 +1617,25 @@ public String translate(LocalizableMessage message) { public String translate(LocalizableMessageType message) { return localizationService.translate(LocalizationUtil.toLocalizableMessage(message), Locale.getDefault()); } + + @Override + public Object executeAdHocProvisioningScript(ResourceType resource, String language, String code) + throws SchemaException, ObjectNotFoundException, + ExpressionEvaluationException, CommunicationException, ConfigurationException, + SecurityViolationException, ObjectAlreadyExistsException { + return executeAdHocProvisioningScript(resource.getOid(), language, code); + } + + @Override + public Object executeAdHocProvisioningScript(String resourceOid, String language, String code) + throws SchemaException, ObjectNotFoundException, + ExpressionEvaluationException, CommunicationException, ConfigurationException, + SecurityViolationException, ObjectAlreadyExistsException { + OperationProvisioningScriptType script = new OperationProvisioningScriptType(); + script.setCode(code); + script.setLanguage(language); + script.setHost(ProvisioningScriptHostType.RESOURCE); + + return provisioningService.executeScript(resourceOid, script, getCurrentTask(), getCurrentResult()); + } } \ No newline at end of file diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/ModelExpressionThreadLocalHolder.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/ModelExpressionThreadLocalHolder.java index eca5837ba64..926ed0cf40f 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/ModelExpressionThreadLocalHolder.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/ModelExpressionThreadLocalHolder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017 Evolveum + * Copyright (c) 2013-2018 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -113,36 +113,36 @@ public static PrismValueDeltaSetTriple evaluateAnyExpressionInContext(Express } public static PrismValueDeltaSetTriple> evaluateExpressionInContext(Expression, - PrismPropertyDefinition> expression, ExpressionEvaluationContext context, Task task, OperationResult result) + PrismPropertyDefinition> expression, ExpressionEvaluationContext eeContext, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { ModelExpressionThreadLocalHolder.pushExpressionEnvironment(new ExpressionEnvironment<>(task, result)); try { - return expression.evaluate(context); + return expression.evaluate(eeContext); } finally { ModelExpressionThreadLocalHolder.popExpressionEnvironment(); } } public static PrismValueDeltaSetTriple evaluateRefExpressionInContext(Expression expression, ExpressionEvaluationContext context, Task task, OperationResult result) + PrismReferenceDefinition> expression, ExpressionEvaluationContext eeContext, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { ModelExpressionThreadLocalHolder.pushExpressionEnvironment(new ExpressionEnvironment<>(task, result)); try { - return expression.evaluate(context); + return expression.evaluate(eeContext); } finally { ModelExpressionThreadLocalHolder.popExpressionEnvironment(); } } - public static PrismValueDeltaSetTriple> evaluateExpressionInContext(Expression, - PrismPropertyDefinition> expression, ExpressionEvaluationContext context, - LensContext lensContext, LensProjectionContext projectionContext, Task task, OperationResult result) + public static PrismValueDeltaSetTriple> evaluateExpressionInContext( + Expression,PrismPropertyDefinition> expression, + ExpressionEvaluationContext eeContext, + ExpressionEnvironment env) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { - ExpressionEnvironment env = new ExpressionEnvironment<>(lensContext, projectionContext, task, result); ModelExpressionThreadLocalHolder.pushExpressionEnvironment(env); PrismValueDeltaSetTriple> exprResultTriple; try { - exprResultTriple = expression.evaluate(context); + exprResultTriple = expression.evaluate(eeContext); } finally { ModelExpressionThreadLocalHolder.popExpressionEnvironment(); } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java index 3aeb88d0532..e2feac16bd9 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java @@ -1575,11 +1575,15 @@ private OperationProvisioningScriptsType evaluateScript(OperationProvisioningScr continue; } } - if (!script.getOperation().contains(operation)) { - continue; + if (operation != null) { + if (!script.getOperation().contains(operation)) { + continue; + } } - if (order != null && order != script.getOrder()) { - continue; + if (order != null) { + if (order != null && order != script.getOrder()) { + continue; + } } // Let's do the most expensive evaluation last if (!evaluateScriptCondition(script, variables, task, result)){ @@ -1630,9 +1634,10 @@ private void evaluateScriptArgument(ProvisioningScriptArgumentType argument, ExpressionEvaluationContext params = new ExpressionEvaluationContext(null, variables, shortDesc, task, result); + ExpressionEnvironment env = new ExpressionEnvironment<>(context, + objectContext instanceof LensProjectionContext ? (LensProjectionContext) objectContext : null, task, result); PrismValueDeltaSetTriple> outputTriple = ModelExpressionThreadLocalHolder - .evaluateExpressionInContext(expression, params, context, - objectContext instanceof LensProjectionContext ? (LensProjectionContext) objectContext : null, task, result); + .evaluateExpressionInContext(expression, params, env); Collection> nonNegativeValues = null; if (outputTriple != null) { @@ -1684,6 +1689,21 @@ private void executeReconciliationS if (resourceScripts == null) { return; } + + executeProvisioningScripts(context, projContext, resourceScripts, ProvisioningOperationTypeType.RECONCILE, order, task, parentResult); + } + + private Object executeProvisioningScripts(LensContext context, LensProjectionContext projContext, + OperationProvisioningScriptsType scripts, ProvisioningOperationTypeType operation, BeforeAfterType order, Task task, OperationResult parentResult) + throws SchemaException, ObjectNotFoundException, + ExpressionEvaluationException, CommunicationException, ConfigurationException, + SecurityViolationException, ObjectAlreadyExistsException { + + ResourceType resource = projContext.getResource(); + if (resource == null) { + LOGGER.warn("Resource does not exist. Skipping processing reconciliation scripts."); + return null; + } PrismObject user = null; PrismObject shadow = null; @@ -1708,26 +1728,29 @@ private void executeReconciliationS } else if (order == BeforeAfterType.AFTER) { shadow = (PrismObject) projContext.getObjectNew(); } else { - throw new IllegalArgumentException("Unknown order " + order); + shadow = (PrismObject) projContext.getObjectCurrent(); } ExpressionVariables variables = Utils.getDefaultExpressionVariables(user, shadow, projContext.getResourceShadowDiscriminator(), resource.asPrismObject(), context.getSystemConfiguration(), projContext); + Object scriptResult = null; ModelExpressionThreadLocalHolder.pushExpressionEnvironment(new ExpressionEnvironment<>(context, projContext, task, parentResult)); try { - OperationProvisioningScriptsType evaluatedScript = evaluateScript(resourceScripts, - projContext.getResourceShadowDiscriminator(), ProvisioningOperationTypeType.RECONCILE, order, - variables, context, projContext, task, parentResult); - for (OperationProvisioningScriptType script : evaluatedScript.getScript()) { - Utils.setRequestee(task, context); - provisioning.executeScript(resource.getOid(), script, task, parentResult); - Utils.clearRequestee(task); - } + OperationProvisioningScriptsType evaluatedScript = evaluateScript(scripts, + projContext.getResourceShadowDiscriminator(), operation, order, + variables, context, projContext, task, parentResult); + for (OperationProvisioningScriptType script : evaluatedScript.getScript()) { + Utils.setRequestee(task, context); + scriptResult = provisioning.executeScript(resource.getOid(), script, task, parentResult); + Utils.clearRequestee(task); + } } finally { ModelExpressionThreadLocalHolder.popExpressionEnvironment(); } + return scriptResult; } + } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java index 0d79d8aab7c..d100bbdb828 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum + * Copyright (c) 2010-2018 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,6 +52,7 @@ import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; import com.evolveum.midpoint.common.refinery.RefinedResourceSchema; import com.evolveum.midpoint.model.common.mapping.Mapping; +import com.evolveum.midpoint.model.impl.expr.ExpressionEnvironment; import com.evolveum.midpoint.model.impl.expr.ModelExpressionThreadLocalHolder; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.delta.ItemDelta; @@ -64,6 +65,7 @@ import com.evolveum.midpoint.repo.common.expression.Expression; import com.evolveum.midpoint.repo.common.expression.ExpressionEvaluationContext; import com.evolveum.midpoint.repo.common.expression.ExpressionFactory; +import com.evolveum.midpoint.repo.common.expression.ExpressionUtil; import com.evolveum.midpoint.repo.common.expression.ExpressionVariables; import com.evolveum.midpoint.repo.common.expression.ItemDeltaItem; import com.evolveum.midpoint.repo.common.expression.Source; @@ -530,16 +532,15 @@ public static boolean evaluateIterationCondition(LensCont if (expressionType == null) { return true; } - PrismPropertyDefinition outputDefinition = new PrismPropertyDefinitionImpl<>( - ExpressionConstants.OUTPUT_ELEMENT_NAME, - DOMUtil.XSD_BOOLEAN, context.getPrismContext()); - Expression,PrismPropertyDefinition> expression = expressionFactory.makeExpression(expressionType, outputDefinition , desc, task, result); + Expression,PrismPropertyDefinition> expression = expressionFactory.makeExpression( + expressionType, ExpressionUtil.createConditionOutputDefinition(context.getPrismContext()) , desc, task, result); variables.addVariableDefinition(ExpressionConstants.VAR_ITERATION, iteration); variables.addVariableDefinition(ExpressionConstants.VAR_ITERATION_TOKEN, iterationToken); ExpressionEvaluationContext expressionContext = new ExpressionEvaluationContext(null , variables, desc, task, result); - PrismValueDeltaSetTriple> outputTriple = ModelExpressionThreadLocalHolder.evaluateExpressionInContext(expression, expressionContext, context, null, task, result); + ExpressionEnvironment env = new ExpressionEnvironment<>(context, null, task, result); + PrismValueDeltaSetTriple> outputTriple = ModelExpressionThreadLocalHolder.evaluateExpressionInContext(expression, expressionContext, env); Collection> outputValues = outputTriple.getNonNegativeValues(); if (outputValues.isEmpty()) { return false; @@ -1047,10 +1048,10 @@ public static T evaluateExpressionSingle(ExpressionType expressionBean, Expr new QName(SchemaConstants.NS_C, "result"), typeName, prismContext); Expression,PrismPropertyDefinition> expression = expressionFactory.makeExpression(expressionBean, resultDef, contextDescription, task, result); - ExpressionEvaluationContext context = new ExpressionEvaluationContext(null, expressionVariables, contextDescription, task, result); - context.setAdditionalConvertor(additionalConvertor); + ExpressionEvaluationContext eeContext = new ExpressionEvaluationContext(null, expressionVariables, contextDescription, task, result); + eeContext.setAdditionalConvertor(additionalConvertor); PrismValueDeltaSetTriple> exprResultTriple = ModelExpressionThreadLocalHolder - .evaluateExpressionInContext(expression, context, task, result); + .evaluateExpressionInContext(expression, eeContext, task, result); List results = exprResultTriple.getZeroSet().stream() .map(ppv -> (T) ppv.getRealValue()) .collect(Collectors.toList()); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusLifecycleProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusLifecycleProcessor.java new file mode 100644 index 00000000000..2ad88a60d52 --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusLifecycleProcessor.java @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2010-2018 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.impl.lens.projector.focus; + +import java.util.*; +import java.util.Map.Entry; +import java.util.Objects; + +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.namespace.QName; + +import com.evolveum.midpoint.model.impl.lens.projector.ComplexConstructionConsumer; +import com.evolveum.midpoint.model.impl.lens.projector.ConstructionProcessor; +import com.evolveum.midpoint.model.impl.lens.projector.MappingEvaluator; +import com.evolveum.midpoint.model.impl.lens.projector.policy.PolicyRuleProcessor; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; + +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +import com.evolveum.midpoint.common.ActivationComputer; +import com.evolveum.midpoint.repo.common.expression.Expression; +import com.evolveum.midpoint.repo.common.expression.ExpressionEvaluationContext; +import com.evolveum.midpoint.repo.common.expression.ExpressionFactory; +import com.evolveum.midpoint.repo.common.expression.ExpressionUtil; +import com.evolveum.midpoint.repo.common.expression.ExpressionVariables; +import com.evolveum.midpoint.repo.common.expression.ObjectDeltaObject; +import com.evolveum.midpoint.model.api.ModelExecuteOptions; +import com.evolveum.midpoint.model.api.context.SynchronizationPolicyDecision; +import com.evolveum.midpoint.model.common.SystemObjectCache; +import com.evolveum.midpoint.model.common.mapping.Mapping; +import com.evolveum.midpoint.model.common.mapping.MappingFactory; +import com.evolveum.midpoint.model.impl.controller.ModelUtils; +import com.evolveum.midpoint.model.impl.expr.ExpressionEnvironment; +import com.evolveum.midpoint.model.impl.expr.ModelExpressionThreadLocalHolder; +import com.evolveum.midpoint.model.impl.lens.AssignmentEvaluator; +import com.evolveum.midpoint.model.impl.lens.Construction; +import com.evolveum.midpoint.model.impl.lens.ConstructionPack; +import com.evolveum.midpoint.model.impl.lens.EvaluatedAssignmentImpl; +import com.evolveum.midpoint.model.impl.lens.ItemValueWithOrigin; +import com.evolveum.midpoint.model.impl.lens.LensContext; +import com.evolveum.midpoint.model.impl.lens.LensFocusContext; +import com.evolveum.midpoint.model.impl.lens.LensProjectionContext; +import com.evolveum.midpoint.model.impl.lens.LensUtil; +import com.evolveum.midpoint.prism.ItemDefinition; +import com.evolveum.midpoint.prism.PrismContainerDefinition; +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismObjectDefinition; +import com.evolveum.midpoint.prism.PrismPropertyDefinition; +import com.evolveum.midpoint.prism.PrismPropertyValue; +import com.evolveum.midpoint.prism.PrismReference; +import com.evolveum.midpoint.prism.PrismReferenceDefinition; +import com.evolveum.midpoint.prism.PrismReferenceValue; +import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.prism.delta.ContainerDelta; +import com.evolveum.midpoint.prism.delta.DeltaMapTriple; +import com.evolveum.midpoint.prism.delta.DeltaSetTriple; +import com.evolveum.midpoint.prism.delta.ItemDelta; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.prism.delta.PlusMinusZero; +import com.evolveum.midpoint.prism.delta.PrismValueDeltaSetTriple; +import com.evolveum.midpoint.prism.delta.PropertyDelta; +import com.evolveum.midpoint.prism.delta.ReferenceDelta; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.provisioning.api.ProvisioningService; +import com.evolveum.midpoint.repo.api.RepositoryService; +import com.evolveum.midpoint.schema.ResourceShadowDiscriminator; +import com.evolveum.midpoint.schema.constants.ExpressionConstants; +import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.result.OperationResultStatus; +import com.evolveum.midpoint.schema.util.FocusTypeUtil; +import com.evolveum.midpoint.schema.util.ObjectResolver; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; +import com.evolveum.midpoint.schema.util.SchemaDebugUtil; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.util.MiscUtil; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.CommunicationException; +import com.evolveum.midpoint.util.exception.ConfigurationException; +import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.PolicyViolationException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SecurityViolationException; +import com.evolveum.midpoint.util.exception.TunnelException; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; + +/** + * @author Radovan Semancik + */ +@Component +public class FocusLifecycleProcessor { + + @Autowired private ExpressionFactory expressionFactory; + + private static final Trace LOGGER = TraceManager.getTrace(FocusLifecycleProcessor.class); + + + public void processLifecycle(LensContext context, XMLGregorianCalendar now, + Task task, OperationResult parentResult) throws SchemaException, + ObjectNotFoundException, ExpressionEvaluationException, PolicyViolationException, CommunicationException, ConfigurationException, SecurityViolationException { + LensFocusContext focusContext = context.getFocusContext(); + if (focusContext == null) { + return; + } + if (!FocusType.class.isAssignableFrom(focusContext.getObjectTypeClass())) { + // We can do this only for FocusType. + return; + } + + OperationResult result = parentResult.createSubresult(FocusLifecycleProcessor.class.getName() + ".processLifecycle"); + + try { + processLifecycleWithFocus((LensContext)context, now, task, result); + } catch (Throwable e) { + result.recordFatalError(e); + throw e; + } + + result.computeStatus(); + result.recordSuccessIfUnknown(); + } + + private void processLifecycleWithFocus(LensContext context, XMLGregorianCalendar now, + Task task, OperationResult result) throws SchemaException, + ObjectNotFoundException, ExpressionEvaluationException, PolicyViolationException, CommunicationException, ConfigurationException, SecurityViolationException { + + LensFocusContext focusContext = context.getFocusContext(); + ObjectDelta focusDelta = focusContext.getDelta(); + + if (focusDelta != null && focusDelta.isDelete()) { + LOGGER.trace("Skipping lifecycle processing because of focus delete"); + return; + } + + ObjectPolicyConfigurationType objectPolicyConfigurationType = focusContext.getObjectPolicyConfigurationType(); + if (objectPolicyConfigurationType == null) { + LOGGER.trace("Skipping lifecycle processing because there is no object policy for focus"); + return; + } + + LifecycleStateModelType lifecycleStateModel = objectPolicyConfigurationType.getLifecycleStateModel(); + if (lifecycleStateModel == null) { + LOGGER.trace("Skipping lifecycle processing because there is no lifecycle state model for focus"); + return; + } + + PrismObject objectNew = focusContext.getObjectNew(); + String startLifecycleState = objectNew.asObjectable().getLifecycleState(); + if (startLifecycleState == null) { + startLifecycleState = SchemaConstants.LIFECYCLE_ACTIVE; + } + + LifecycleStateType startStateType = findStateDefinition(lifecycleStateModel, startLifecycleState); + if (startStateType == null) { + LOGGER.trace("Skipping lifecycle processing because there is no specification for lifecycle state {}", startLifecycleState); + return; + } + + for (LifecycleStateTransitionType transitionType : startStateType.getTransition()) { + String targetLifecycleState = transitionType.getTargetState(); + if (shouldTransition(context, transitionType, targetLifecycleState, task, result)) { + executeExitActions(context, lifecycleStateModel, startLifecycleState, now, task, result); + LOGGER.debug("Lifecycle state transition of {}: {} -> {}", objectNew, startLifecycleState, targetLifecycleState); + recordLifecycleTransitionDelta(focusContext, targetLifecycleState); + executeEntryActions(context, lifecycleStateModel, targetLifecycleState, now, task, result); + LOGGER.trace("Lifecycle state transition of {} to {} done", objectNew, startLifecycleState, targetLifecycleState); + break; + } + } + } + + private boolean shouldTransition(LensContext context, LifecycleStateTransitionType transitionType, String targetLifecycleState, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { + ExpressionType conditionExpressionType = transitionType.getCondition(); + if (conditionExpressionType == null) { + return false; + } + String desc = "condition for transition to state "+targetLifecycleState+" for "+context.getFocusContext().getHumanReadableName(); + + ExpressionVariables variables = new ExpressionVariables(); + variables.addVariableDefinition(ExpressionConstants.VAR_OBJECT, context.getFocusContext().getObjectNew()); + // TODO: more variables? + + Expression,PrismPropertyDefinition> expression = expressionFactory.makeExpression( + conditionExpressionType, ExpressionUtil.createConditionOutputDefinition(context.getPrismContext()) , desc, task, result); + ExpressionEvaluationContext expressionContext = new ExpressionEvaluationContext(null , variables, desc, task, result); + ExpressionEnvironment env = new ExpressionEnvironment<>(context, null, task, result); + PrismValueDeltaSetTriple> outputTriple = ModelExpressionThreadLocalHolder.evaluateExpressionInContext(expression, expressionContext, env); + PrismPropertyValue expressionOutputValue = ExpressionUtil.getExpressionOutputValue(outputTriple, desc); + return ExpressionUtil.getBooleanConditionOutput(expressionOutputValue); + } + + private void recordLifecycleTransitionDelta(LensFocusContext focusContext, String targetLifecycleState) throws SchemaException { + PropertyDelta lifecycleDelta = PropertyDelta.createModificationReplaceProperty(ObjectType.F_LIFECYCLE_STATE, focusContext.getObjectDefinition(), + targetLifecycleState); + focusContext.swallowToSecondaryDelta(lifecycleDelta); + } + + private void executeEntryActions(LensContext context, LifecycleStateModelType lifecycleStateModel, + String targetLifecycleState, XMLGregorianCalendar now, Task task, OperationResult result) throws SchemaException { + LifecycleStateType stateType = findStateDefinition(lifecycleStateModel, targetLifecycleState); + if (stateType == null) { + return; + } + executeStateActions(context, targetLifecycleState, stateType.getEntryAction(), "entry", now, task, result); + } + + private void executeExitActions(LensContext context, LifecycleStateModelType lifecycleStateModel, + String targetLifecycleState, XMLGregorianCalendar now, Task task, OperationResult result) throws SchemaException { + LifecycleStateType stateType = findStateDefinition(lifecycleStateModel, targetLifecycleState); + if (stateType == null) { + return; + } + executeStateActions(context, targetLifecycleState, stateType.getExitAction(), "exit", now, task, result); + } + + private void executeStateActions(LensContext context, String targetLifecycleState, + List actions, String actionTypeDesc, XMLGregorianCalendar now, Task task, OperationResult result) throws SchemaException { + for (LifecycleStateActionType action: actions) { + LOGGER.trace("Execute {} action {} for state {} of {}", actionTypeDesc, action.getName(), targetLifecycleState, context.getFocusContext().getObjectNew()); + executeDataReduction(context, action.getDataReduction(), now, task, result); + } + } + + private void executeDataReduction(LensContext context, LifecycleStateActionDataReductionType dataReduction, + XMLGregorianCalendar now, Task task, OperationResult result) throws SchemaException { + if (dataReduction == null) { + return; + } + LensFocusContext focusContext = context.getFocusContext(); + PrismObjectDefinition focusDefinition = focusContext.getObjectDefinition(); + for (ItemPathType purgeItemPathType : dataReduction.getPurgeItem()) { + ItemPath purgeItemPath = purgeItemPathType.getItemPath(); + LOGGER.trace("Purging item {} from {}", purgeItemPath, focusContext.getObjectNew()); + ItemDefinition purgeItemDef = focusDefinition.findItemDefinition(purgeItemPath); + ItemDelta purgeItemDelta = purgeItemDef.createEmptyDelta(purgeItemPath); + purgeItemDelta.setValueToReplace(); + focusContext.swallowToSecondaryDelta(purgeItemDelta); + } + } + + private LifecycleStateType findStateDefinition(LifecycleStateModelType lifecycleStateModel, String targetLifecycleState) { + for (LifecycleStateType stateType: lifecycleStateModel.getState()) { + if (targetLifecycleState.equals(stateType.getName())) { + return stateType; + } + } + return null; + } + +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusProcessor.java index 00667e52dc9..7d2d8a1d063 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum + * Copyright (c) 2010-2018 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -85,42 +85,23 @@ public class FocusProcessor { private PrismContainerDefinition activationDefinition; private PrismPropertyDefinition failedLoginsDefinition; - @Autowired - private InboundProcessor inboundProcessor; - - @Autowired - private AssignmentProcessor assignmentProcessor; - - @Autowired - private ObjectTemplateProcessor objectTemplateProcessor; - - @Autowired - private PrismContext prismContext; - - @Autowired - private CredentialsProcessor credentialsProcessor; - - @Autowired - private ModelObjectResolver modelObjectResolver; - - @Autowired - private ActivationComputer activationComputer; - - @Autowired - private ExpressionFactory expressionFactory; + @Autowired private InboundProcessor inboundProcessor; + @Autowired private AssignmentProcessor assignmentProcessor; + @Autowired private ObjectTemplateProcessor objectTemplateProcessor; + @Autowired private PrismContext prismContext; + @Autowired private CredentialsProcessor credentialsProcessor; + @Autowired private ModelObjectResolver modelObjectResolver; + @Autowired private ActivationComputer activationComputer; + @Autowired private ExpressionFactory expressionFactory; + @Autowired private MappingEvaluator mappingHelper; + @Autowired private OperationalDataManager metadataManager; + @Autowired private PolicyRuleProcessor policyRuleProcessor; + @Autowired private FocusLifecycleProcessor focusLifecycleProcessor; @Autowired @Qualifier("cacheRepositoryService") private transient RepositoryService cacheRepositoryService; - @Autowired - private MappingEvaluator mappingHelper; - - @Autowired - private OperationalDataManager metadataManager; - - @Autowired - private PolicyRuleProcessor policyRuleProcessor; public void processFocus(LensContext context, String activityDescription, XMLGregorianCalendar now, Task task, OperationResult result) @@ -260,6 +241,10 @@ private void processFocusFocus(LensContext context, Str LensUtil.partialExecute("assignmentsConflicts", () -> assignmentProcessor.checkForAssignmentConflicts(context, result), partialProcessingOptions::getAssignmentsConflicts); + + LensUtil.partialExecute("focusLifecycle", + () -> focusLifecycleProcessor.processLifecycle(context, now, task, result), + partialProcessingOptions::getFocusLifecycle); // OBJECT TEMPLATE (after assignments) diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestLifecycle.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestLifecycle.java new file mode 100644 index 00000000000..a816282a3e2 --- /dev/null +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestLifecycle.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2018 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.intest; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNotNull; + +import java.io.File; + +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; +import org.springframework.test.context.ContextConfiguration; +import org.testng.annotations.Test; + +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.prism.util.PrismTestUtil; +import com.evolveum.midpoint.prism.xml.XmlTypeConverter; +import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.MiscSchemaUtil; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.util.TestUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentPolicyEnforcementType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemObjectsType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; + +/** + * @author Radovan Semancik + * + */ +@ContextConfiguration(locations = {"classpath:ctx-model-intest-test-main.xml"}) +@DirtiesContext(classMode = ClassMode.AFTER_CLASS) +public class TestLifecycle extends AbstractInitializedModelIntegrationTest { + + public static final File TEST_DIR = new File("src/test/resources/lifecycle"); + + public static final File SYSTEM_CONFIGURATION_LIFECYCLE_FILE = new File(TEST_DIR, "system-configuration-lifecycle.xml"); + + // subtype = dataProcessingBasis + public static final File ROLE_HEADMASTER_FILE = new File(TEST_DIR, "role-headmaster.xml"); + protected static final String ROLE_HEADMASTER_OID = "b9c885ba-034b-11e8-a708-13836b619045"; + + // subtype = dataProcessingBasis + public static final File ROLE_CARETAKER_FILE = new File(TEST_DIR, "role-caretaker.xml"); + protected static final String ROLE_CARETAKER_OID = "9162a952-034b-11e8-afb7-138a763f2350"; + + // no subtype, this is NOT a dataProcessingBasis + public static final File ROLE_GAMBLER_FILE = new File(TEST_DIR, "role-gambler.xml"); + protected static final String ROLE_GAMBLER_OID = "2bb2fb86-034e-11e8-9cf3-77abfc7aafec"; + + public static final String SUBTYPE_EMPLOYEE = "employee"; + private static final Object USER_JACK_TELEPHONE_NUMBER = "12345654321"; + + @Override + public void initSystem(Task initTask, OperationResult initResult) throws Exception { + super.initSystem(initTask, initResult); + + repoAddObjectFromFile(ROLE_HEADMASTER_FILE, initResult); + repoAddObjectFromFile(ROLE_CARETAKER_FILE, initResult); + repoAddObjectFromFile(ROLE_GAMBLER_FILE, initResult); + } + + @Override + protected File getSystemConfigurationFile() { + return SYSTEM_CONFIGURATION_LIFECYCLE_FILE; + } + + /** + * Setup jack. Setting subtype to employee will put him under lifecycle + * control. But before that we want him to have at least one + * processing basis role. + */ + @Test + public void test050SetupJack() throws Exception { + final String TEST_NAME = "test050SetupJack"; + displayTestTitle(TEST_NAME); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + displayWhen(TEST_NAME); + + assignRole(USER_JACK_OID, ROLE_HEADMASTER_OID, task, result); + assignRole(USER_JACK_OID, ROLE_GAMBLER_OID, task, result); + modifyUserReplace(USER_JACK_OID, UserType.F_EMPLOYEE_TYPE, task, result, SUBTYPE_EMPLOYEE); + modifyUserReplace(USER_JACK_OID, UserType.F_TELEPHONE_NUMBER, task, result, USER_JACK_TELEPHONE_NUMBER); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 2); + assertLifecycleState(userAfter, null); + assertTelephoneNumber(userAfter, USER_JACK_TELEPHONE_NUMBER); + assertLinks(userAfter, 0); + } + + private void assertTelephoneNumber(PrismObject user, Object expectedTelephoneNumber) { + assertEquals("Wrong telephoe number in "+user, expectedTelephoneNumber, user.asObjectable().getTelephoneNumber()); + } + + protected void assertLifecycleState(PrismObject object, String expectedLifecycleState) { + assertEquals("Wrong lifecycle state in "+object, expectedLifecycleState, object.asObjectable().getLifecycleState()); + } + + @Test + public void test100AssignJackCaretaker() throws Exception { + final String TEST_NAME = "test100AssignJackCaretaker"; + displayTestTitle(TEST_NAME); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + displayWhen(TEST_NAME); + + assignRole(USER_JACK_OID, ROLE_CARETAKER_OID, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 3); + assertLifecycleState(userAfter, null); + assertTelephoneNumber(userAfter, USER_JACK_TELEPHONE_NUMBER); + } + + @Test + public void test102UnassignJackHeadmaster() throws Exception { + final String TEST_NAME = "test102UnassignJackHeadmaster"; + displayTestTitle(TEST_NAME); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + displayWhen(TEST_NAME); + + unassignRole(USER_JACK_OID, ROLE_HEADMASTER_OID, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 2); + assertLifecycleState(userAfter, null); + assertTelephoneNumber(userAfter, USER_JACK_TELEPHONE_NUMBER); + } + + /** + * This is the real test. Now lifecycle transition should take + * place because jack has no processing basis role. + */ + @Test + public void test110UnassignJackCaretaker() throws Exception { + final String TEST_NAME = "test110UnassignJackCaretaker"; + displayTestTitle(TEST_NAME); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + displayWhen(TEST_NAME); + + unassignRole(USER_JACK_OID, ROLE_CARETAKER_OID, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 1); + assertLifecycleState(userAfter, SchemaConstants.LIFECYCLE_ARCHIVED); + assertTelephoneNumber(userAfter, null); + } + + /** + * Jack is now archived. So, even if we assign a new processing basis + * role the lifecycle should not change. Archival is a one-way process. + */ + @Test + public void test112UnassignJackCaretaker() throws Exception { + final String TEST_NAME = "test110UnassignJackCaretaker"; + displayTestTitle(TEST_NAME); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + displayWhen(TEST_NAME); + + assignRole(USER_JACK_OID, ROLE_HEADMASTER_OID, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 2); + assertLifecycleState(userAfter, SchemaConstants.LIFECYCLE_ARCHIVED); + assertTelephoneNumber(userAfter, null); + } + +} diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMisc.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMisc.java index 79132983ef1..dec58f899f1 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMisc.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMisc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum + * Copyright (c) 2010-2018 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,10 @@ import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertEquals; -import static com.evolveum.midpoint.test.IntegrationTestTools.display; import java.io.File; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.List; -import javax.xml.bind.JAXBException; import javax.xml.transform.dom.DOMSource; import javax.xml.validation.Schema; import javax.xml.validation.Validator; @@ -38,6 +34,7 @@ import org.testng.annotations.Test; import org.w3c.dom.Document; +import com.evolveum.icf.dummy.resource.DummyAccount; import com.evolveum.midpoint.prism.Objectable; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.path.ItemPath; @@ -47,6 +44,7 @@ import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.DummyResourceContoller; import com.evolveum.midpoint.test.util.TestUtil; import com.evolveum.midpoint.util.DOMUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; @@ -64,6 +62,10 @@ public class TestMisc extends AbstractInitializedModelIntegrationTest { protected static final File ROLE_IMPORT_FILTERS_FILE = new File(TEST_DIR, "role-import-filters.xml"); protected static final String ROLE_IMPORT_FILTERS_OID = "aad19b9a-d511-11e7-8bf7-cfecde275e59"; + + protected static final File RESOURCE_SCRIPTY_FILE = new File(TEST_DIR, "resource-dummy-scripty.xml"); + protected static final String RESOURCE_SCRIPTY_OID = "399f5308-0447-11e8-91e9-a7f9c4100ffb"; + protected static final String RESOURCE_DUMMY_SCRIPTY_NAME = "scripty"; public static final byte[] KEY = { 0x01, 0x02, 0x03, 0x04, 0x05 }; @@ -73,14 +75,13 @@ public class TestMisc extends AbstractInitializedModelIntegrationTest { private String userCleanOid; - public TestMisc() throws JAXBException { - super(); - } - @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { super.initSystem(initTask, initResult); + + initDummyResourcePirate(RESOURCE_DUMMY_SCRIPTY_NAME, + RESOURCE_SCRIPTY_FILE, RESOURCE_SCRIPTY_OID, initTask, initResult); } @Test @@ -294,5 +295,36 @@ public void test400ImportRoleWithFilters() throws Exception { assertInducedRole(roleAfter, ROLE_SAILOR_OID); assertInducements(roleAfter, 2); } + + @Test + public void test500AddHocProvisioningScriptAssignJackResourceScripty() throws Exception { + final String TEST_NAME = "test500AddHocProvisioningScriptAssignJackResourceScripty"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + PrismObject userBefore = getUser(USER_JACK_OID); + display("User before", userBefore); + assertAssignments(userBefore, 0); + + // WHEN + displayWhen(TEST_NAME); + assignAccount(USER_JACK_OID, RESOURCE_SCRIPTY_OID, null, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 1); + + assertDummyAccount(RESOURCE_DUMMY_SCRIPTY_NAME, ACCOUNT_JACK_DUMMY_USERNAME, ACCOUNT_JACK_DUMMY_FULLNAME, true); + assertDummyAccountAttribute(RESOURCE_DUMMY_SCRIPTY_NAME, ACCOUNT_JACK_DUMMY_USERNAME, + DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME, + "Mr. POLY JACK SPARROW"); + } } diff --git a/model/model-intest/src/test/resources/lifecycle/role-caretaker.xml b/model/model-intest/src/test/resources/lifecycle/role-caretaker.xml new file mode 100644 index 00000000000..e9056cbbb50 --- /dev/null +++ b/model/model-intest/src/test/resources/lifecycle/role-caretaker.xml @@ -0,0 +1,24 @@ + + + Caretaker + dataProcessingBasis + diff --git a/model/model-intest/src/test/resources/lifecycle/role-gambler.xml b/model/model-intest/src/test/resources/lifecycle/role-gambler.xml new file mode 100644 index 00000000000..332b55e62bb --- /dev/null +++ b/model/model-intest/src/test/resources/lifecycle/role-gambler.xml @@ -0,0 +1,25 @@ + + + Gambler + This is non-employee role. It does NOT work as data processing basis. + + diff --git a/model/model-intest/src/test/resources/lifecycle/role-headmaster.xml b/model/model-intest/src/test/resources/lifecycle/role-headmaster.xml new file mode 100644 index 00000000000..f8c50947be2 --- /dev/null +++ b/model/model-intest/src/test/resources/lifecycle/role-headmaster.xml @@ -0,0 +1,24 @@ + + + Headmaster + dataProcessingBasis + diff --git a/model/model-intest/src/test/resources/lifecycle/system-configuration-lifecycle.xml b/model/model-intest/src/test/resources/lifecycle/system-configuration-lifecycle.xml new file mode 100644 index 00000000000..2e1cdad4a86 --- /dev/null +++ b/model/model-intest/src/test/resources/lifecycle/system-configuration-lifecycle.xml @@ -0,0 +1,224 @@ + + + + + SystemConfiguration + + + File Appender + INFO + + TRACE + com.evolveum.midpoint.common.LoggingConfigurationManager + + + TRACE + com.evolveum.midpoint.notifications + + + %date [%thread] %-5level \(%logger{46}\): %message%n + target/test.log + true + + + + + UserType + employee + + + active + + archival + archived + + + + + + + archived + + archival-data-reduction + + telephoneNumber + + + + + + + + + + + recipient@evolveum.com + + dummy:policyRuleNotifier + + + + + + + + dummy:accountActivationNotifier + link + + + + + + recipient@evolveum.com + + dummy:accountPasswordNotifier + + + + + + recipient@evolveum.com + + dummy:userPasswordNotifier + + + + success + + + recipient@evolveum.com + + dummy:simpleAccountNotifier-SUCCESS + + + + failure + + + recipient@evolveum.com + + dummy:simpleAccountNotifier-FAILURE + + + + add + success + + + recipient@evolveum.com + + dummy:simpleAccountNotifier-ADD-SUCCESS + + + + delete + success + + + recipient@evolveum.com + + dummy:simpleAccountNotifier-DELETE-SUCCESS + + + + + + recipient@evolveum.com + + dummy:simpleUserNotifier + + + + + add + + + + + recipient@evolveum.com + + dummy:simpleUserNotifier-ADD + + + + + + delete + + + + + recipient@evolveum.com + + dummy:simpleUserNotifier-DELETE + + + + + failure + + + recipient@evolveum.com + + dummy:simpleUserNotifier-FAILURE + + + + customEvent + delete + failure + + + + + + recipient@evolveum.com + + + Failure notification of type 2 + + + + + dummy:CustomType2 + + + + target/mail-notifications.log + + + + + 30 + + + + diff --git a/model/model-intest/src/test/resources/misc/resource-dummy-scripty.xml b/model/model-intest/src/test/resources/misc/resource-dummy-scripty.xml new file mode 100644 index 00000000000..3c91bc7d07b --- /dev/null +++ b/model/model-intest/src/test/resources/misc/resource-dummy-scripty.xml @@ -0,0 +1,272 @@ + + + + + + + + Dummy Resource: Scripty + + + + + connectorType + com.evolveum.icf.dummy.connector.DummyConnector + + + connectorVersion + 2.0 + + + + + + + + scripty + + whatever + + USEless + + + + false + false + false + + + + + http://midpoint.evolveum.com/xml/ns/public/resource/instance/10000000-0000-0000-0000-000000000004 + + + + account + default + Default Account + true + ri:AccountObjectClass + + icfs:name + Username + + strong + + $user/name + + + + + + + + ri:fullname + Full Name + + + $user/fullName + + + + + ri:title + + + fullName + + + + + + + + ri:location + Location + + strong + + + $user/locality + + + + + + ri:ship + Ship + + + ri:loot + Loot + explicit + + http://pirates.net/avast + + + + + + + ri:weapon + Weapon + + weak + + extension/weapon + + + + + ri:drink + Drink + false + + strong + + rum + + + + + ri:quote + Quote + true + + strong + + Arr! + + + + + ri:wealth + Wealth + false + + + ri:gossip + Gossip + true + + + ri:water + + true + + + + + daviejones + + + calypso + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + + + + + ri:AccountObjectClass + account + default + true + + + name + + declare namespace icfs="http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/resource-schema-3"; + $account/attributes/icfs:name + + + + + linked + true + + + deleted + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlink + + + + unlinked + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#link + + + + unmatched + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#addFocus + + + + + + + diff --git a/model/model-intest/testng-integration.xml b/model/model-intest/testng-integration.xml index 79bf0b9f16d..4d7b144f72a 100644 --- a/model/model-intest/testng-integration.xml +++ b/model/model-intest/testng-integration.xml @@ -69,6 +69,7 @@ + diff --git a/provisioning/provisioning-api/src/main/java/com/evolveum/midpoint/provisioning/api/ProvisioningService.java b/provisioning/provisioning-api/src/main/java/com/evolveum/midpoint/provisioning/api/ProvisioningService.java index 186ddfa1d89..e762eec3799 100644 --- a/provisioning/provisioning-api/src/main/java/com/evolveum/midpoint/provisioning/api/ProvisioningService.java +++ b/provisioning/provisioning-api/src/main/java/com/evolveum/midpoint/provisioning/api/ProvisioningService.java @@ -343,6 +343,7 @@ void deleteObject(Class type, String oid, Provisioning * script to execute * @param parentResult * parent OperationResult (in/out) + * @return * * @throws ObjectNotFoundException * specified object does not exist @@ -357,7 +358,7 @@ void deleteObject(Class type, String oid, Provisioning * @throws ObjectAlreadyExistsException * if resulting object would have name which already exists in another object of the same type */ - void executeScript(String resourceOid, ProvisioningScriptType script, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, + Object executeScript(String resourceOid, ProvisioningScriptType script, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ObjectAlreadyExistsException, ExpressionEvaluationException; /** diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningServiceImpl.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningServiceImpl.java index f3da89e65d0..34157f7a661 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningServiceImpl.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningServiceImpl.java @@ -749,7 +749,7 @@ public void deleteObject(Class type, String oid, Provi * @see com.evolveum.midpoint.provisioning.api.ProvisioningService#executeScript(java.lang.Class, java.lang.String, com.evolveum.midpoint.xml.ns._public.common.common_3.ProvisioningScriptType, com.evolveum.midpoint.task.api.Task, com.evolveum.midpoint.schema.result.OperationResult) */ @Override - public void executeScript(String resourceOid, ProvisioningScriptType script, + public Object executeScript(String resourceOid, ProvisioningScriptType script, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ObjectAlreadyExistsException, ExpressionEvaluationException { Validate.notNull(resourceOid, "Oid of object for script execution must not be null."); @@ -760,9 +760,10 @@ public void executeScript(String resourceOid, Provisionin result.addArbitraryObjectAsParam("script", script); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); + Object scriptResult; try { - resourceManager.executeScript(resourceOid, script, task, result); + scriptResult = resourceManager.executeScript(resourceOid, script, task, result); } catch (CommunicationException | SchemaException | ConfigurationException | SecurityViolationException | ExpressionEvaluationException | RuntimeException | Error e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); @@ -772,6 +773,7 @@ public void executeScript(String resourceOid, Provisionin result.computeStatus(); result.cleanupResult(); + return scriptResult; } diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionUtil.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionUtil.java index 114bd944e19..19968d6cc4a 100644 --- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionUtil.java +++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum + * Copyright (c) 2010-2018 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -82,8 +82,6 @@ public class ExpressionUtil { private static final Trace LOGGER = TraceManager.getTrace(ExpressionUtil.class); - private static final QName CONDITION_OUTPUT_NAME = new QName(SchemaConstants.NS_C, "condition"); - public static PrismValueDeltaSetTriple toOutputTriple( PrismValueDeltaSetTriple resultTriple, ItemDefinition outputDefinition, Function additionalConvertor, @@ -728,12 +726,15 @@ public static V evaluateExpress Expression expression = expressionFactory.makeExpression(expressionType, outputDefinition, shortDesc, task, parentResult); - ExpressionEvaluationContext context = new ExpressionEvaluationContext(null, variables, shortDesc, task, - parentResult); + ExpressionEvaluationContext context = new ExpressionEvaluationContext(null, variables, shortDesc, task, parentResult); PrismValueDeltaSetTriple outputTriple = expression.evaluate(context); LOGGER.trace("Result of the expression evaluation: {}", outputTriple); + return getExpressionOutputValue(outputTriple, shortDesc); + } + + public static V getExpressionOutputValue(PrismValueDeltaSetTriple outputTriple, String shortDesc) throws ExpressionEvaluationException { if (outputTriple == null) { return null; } @@ -984,9 +985,11 @@ public static V convertToPrismValue(T value, ItemDefin } } - public static Expression,PrismPropertyDefinition> createCondition(ExpressionType conditionExpressionType, ExpressionFactory expressionFactory, String shortDesc, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { - PrismPropertyDefinition conditionOutputDef = new PrismPropertyDefinitionImpl(CONDITION_OUTPUT_NAME, DOMUtil.XSD_BOOLEAN, expressionFactory.getPrismContext()); - return expressionFactory.makeExpression(conditionExpressionType, conditionOutputDef, shortDesc, task, result); + public static Expression,PrismPropertyDefinition> createCondition( + ExpressionType conditionExpressionType, + ExpressionFactory expressionFactory, + String shortDesc, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { + return expressionFactory.makeExpression(conditionExpressionType, createConditionOutputDefinition(expressionFactory.getPrismContext()), shortDesc, task, result); } public static Function createRefConvertor(QName defaultType) { @@ -1009,4 +1012,8 @@ public static Function createRefConvertor(QName defaultType) { } }; } + + public static PrismPropertyDefinition createConditionOutputDefinition(PrismContext prismContext) { + return new PrismPropertyDefinitionImpl<>(ExpressionConstants.OUTPUT_ELEMENT_NAME, DOMUtil.XSD_BOOLEAN, prismContext); + } }