From f0edcd22236929f4656f255b7a9285b9256c286a Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Thu, 28 May 2020 19:32:14 +0200 Subject: [PATCH] Add 'runAsRef' and task filters to scripting rules 1) Scripting action can be run under different user (runAsRef). This is implemented using new experimental RunAsRunner class. 2) Task template reference can now contain a filter. It is resolved using new experimental ReferenceResolver (assignment evaluator was refactored to use it as well). 3) Various expressions (including those in filters) are now evaluated for positive values only. (Evaluating old state was useless.) A part of MID-5967 implementation. --- .../midpoint/prism/schema/SchemaRegistry.java | 12 +- .../midpoint/schema/util/ExceptionUtil.java | 9 +- .../xml/ns/public/common/common-policy-3.xsd | 29 +++- .../midpoint/util/CheckedRunnable.java | 21 +++ .../com/evolveum/midpoint/util/MiscUtil.java | 23 ++++ .../evolveum/midpoint/util/TestMiscUtil.java | 53 ++++++++ .../midpoint/model/api/ModelService.java | 1 + .../model/api/util/ReferenceResolver.java | 53 ++++++++ .../impl/controller/ModelController.java | 2 +- .../ModelInteractionServiceImpl.java | 6 +- .../model/impl/lens/AssignmentCollector.java | 37 +++-- .../lens/assignments/AssignmentEvaluator.java | 11 +- .../lens/assignments/TargetsEvaluation.java | 105 ++++++--------- .../projector/focus/AssignmentProcessor.java | 10 +- .../policy/scriptExecutor/ActionContext.java | 8 ++ .../scriptExecutor/LinkSourceFinder.java | 7 +- .../PolicyRuleScriptExecutor.java | 18 ++- .../scriptExecutor/ScriptingTaskCreator.java | 51 ++++++- .../SynchronousScriptExecutor.java | 3 +- .../model/impl/security/RunAsRunner.java | 97 +++++++++++++ .../impl/security/RunAsRunnerFactory.java | 34 +++++ .../model/impl/util/ModelImplUtils.java | 7 +- .../impl/util/ReferenceResolverImpl.java | 127 ++++++++++++++++++ .../lens/TestAbstractAssignmentEvaluator.java | 22 +-- .../impl/lens/TestAssignmentProcessor2.java | 2 +- .../model/intest/TestMemberRecompute.java | 4 +- .../member-recompute/archetype-department.xml | 15 ++- .../task-template-recompute-members.xml | 2 +- .../test/AbstractModelIntegrationTest.java | 3 + .../common/expression/ExpressionUtil.java | 3 +- 30 files changed, 635 insertions(+), 140 deletions(-) create mode 100644 infra/util/src/main/java/com/evolveum/midpoint/util/CheckedRunnable.java create mode 100644 model/model-api/src/main/java/com/evolveum/midpoint/model/api/util/ReferenceResolver.java create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/RunAsRunner.java create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/RunAsRunnerFactory.java create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/util/ReferenceResolverImpl.java diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/schema/SchemaRegistry.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/schema/SchemaRegistry.java index a6240d38261..01e6873a60c 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/schema/SchemaRegistry.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/schema/SchemaRegistry.java @@ -134,12 +134,22 @@ T findItemDefinitionByFullPath(Class Class determineClassForType(QName type); + default Class determineClassForTypeRequired(QName type, Class expected) { + Class clazz = determineClassForTypeRequired(type); + if (!expected.isAssignableFrom(clazz)) { + throw new IllegalArgumentException("Expected to get " + expected + " but got " + clazz + " instead, for " + type); + } else { + //noinspection unchecked + return (Class) clazz; + } + } + default Class determineClassForTypeRequired(QName type) { Class clazz = determineClassForType(type); if (clazz != null) { return clazz; } else { - throw new IllegalStateException("No class for " + type); + throw new IllegalArgumentException("No class for " + type); } } diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ExceptionUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ExceptionUtil.java index 9279cb46a69..4fda3904034 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ExceptionUtil.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ExceptionUtil.java @@ -7,14 +7,7 @@ package com.evolveum.midpoint.schema.util; import com.evolveum.midpoint.util.LocalizableMessage; -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; -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.exception.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.CriticalityType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ErrorSelectorType; 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 a8252c390a4..0a088cd38ec 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 @@ -2074,6 +2074,29 @@ + + + +

+ Reference to the user whose identity will be used to execute the script, + including search for relevant objects and/or task submission in case of + asynchronous execution. +

+

+ The whole process will use authorizations that this user has. If the script + triggers any change then the change will be audited under the identity of + this user. +

+

+ If no user is specified then the identify of currently logged-in user or + the owner of the task will be used. +

+
+ + tns:FocusType + +
+
@@ -2820,6 +2843,7 @@ Options for asynchronous script execution. + true 4.2 true @@ -2831,7 +2855,8 @@ Mode of execution. - + + Task template to use. @@ -2839,7 +2864,7 @@ EXPERIMENTAL. - TaskType + c:TaskType diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/CheckedRunnable.java b/infra/util/src/main/java/com/evolveum/midpoint/util/CheckedRunnable.java new file mode 100644 index 00000000000..ef744ea4cc4 --- /dev/null +++ b/infra/util/src/main/java/com/evolveum/midpoint/util/CheckedRunnable.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2010-2018 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.util; + +import java.io.Serializable; + +import com.evolveum.midpoint.util.annotation.Experimental; +import com.evolveum.midpoint.util.exception.CommonException; + +/** + * Almost the same as Runnable but this one can throw CommonException and is serializable. + */ +@Experimental +@FunctionalInterface +public interface CheckedRunnable extends Serializable { + void run() throws CommonException; +} diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/MiscUtil.java b/infra/util/src/main/java/com/evolveum/midpoint/util/MiscUtil.java index 05340c33bba..5c2bef2630b 100644 --- a/infra/util/src/main/java/com/evolveum/midpoint/util/MiscUtil.java +++ b/infra/util/src/main/java/com/evolveum/midpoint/util/MiscUtil.java @@ -10,6 +10,8 @@ import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; @@ -31,6 +33,7 @@ import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; +import com.evolveum.midpoint.util.annotation.Experimental; import com.evolveum.midpoint.util.exception.SchemaException; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; @@ -813,4 +816,24 @@ public static Class determineCommonAncestor(Collection> classes) { return null; } } + + /** + * Re-throws the original exception wrapped in the same class (e.g. SchemaException as SchemaException) + * but with additional message. It is used to preserve meaning of the exception but adding some contextual + * information. + */ + @Experimental + public static void throwAsSame(Throwable original, String message) throws T { + try { + Constructor constructor = original.getClass().getConstructor(String.class, Throwable.class); + //noinspection unchecked + throw (T) constructor.newInstance(message, original); + } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | InvocationTargetException e) { + // We won't try to be smart. Not possible to instantiate the exception, so won't bother. + // E.g. if it would not be possible to enclose inner exception in outer one, it's better to keep the original + // to preserve the stack trace. + //noinspection unchecked + throw (T) original; + } + } } diff --git a/infra/util/src/test/java/com/evolveum/midpoint/util/TestMiscUtil.java b/infra/util/src/test/java/com/evolveum/midpoint/util/TestMiscUtil.java index c1c9d4ce7b8..c84fa808fea 100644 --- a/infra/util/src/test/java/com/evolveum/midpoint/util/TestMiscUtil.java +++ b/infra/util/src/test/java/com/evolveum/midpoint/util/TestMiscUtil.java @@ -6,6 +6,8 @@ */ package com.evolveum.midpoint.util; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.assertEquals; @@ -15,6 +17,8 @@ import java.util.List; import java.util.Vector; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; + import org.testng.annotations.Test; import com.evolveum.midpoint.tools.testng.AbstractUnitTest; @@ -83,4 +87,53 @@ public void testCarthesian() { System.out.println(combinations); assertEquals("Wrong number of results", 24, combinations.size()); } + + @SuppressWarnings({ "NumericOverflow", "divzero", "unused" }) + @Test + public void testThrowAsSameForArithmeticException() { + try { + try { + int a = 1/0; + } catch (ArithmeticException e) { + MiscUtil.throwAsSame(e, "Exception in processing: " + e.getMessage()); + } + fail("Unexpected success"); + } catch (Throwable t) { + assertThat(t).as("Exception").isExactlyInstanceOf(ArithmeticException.class); + assertThat(t.getMessage()).as("Message").isEqualTo("/ by zero"); // no wrapping + t.printStackTrace(); + } + } + + @Test + public void testThrowAsSameForIllegalStateException() { + try { + try { + throw new IllegalStateException("Hi"); + } catch (IllegalStateException e) { + MiscUtil.throwAsSame(e, "Exception in processing: " + e.getMessage()); + } + fail("Unexpected success"); + } catch (Throwable t) { + assertThat(t).as("Exception").isExactlyInstanceOf(IllegalStateException.class); + assertThat(t.getMessage()).as("Message").isEqualTo("Exception in processing: Hi"); + t.printStackTrace(); + } + } + + @Test + public void testThrowAsSameForObjectNotFoundException() { + try { + try { + throw new ObjectNotFoundException("Hi"); + } catch (ObjectNotFoundException e) { + MiscUtil.throwAsSame(e, "Exception in processing: " + e.getMessage()); + } + fail("Unexpected success"); + } catch (Throwable t) { + assertThat(t).as("Exception").isExactlyInstanceOf(ObjectNotFoundException.class); + assertThat(t.getMessage()).as("Message").isEqualTo("Exception in processing: Hi"); + t.printStackTrace(); + } + } } diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelService.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelService.java index bc6a0d9f675..aa39bc5066a 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelService.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelService.java @@ -142,6 +142,7 @@ public interface ModelService { * unknown error from underlying layers or other unexpected * state */ + @NotNull PrismObject getObject(Class type, String oid, Collection> options, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException; diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/util/ReferenceResolver.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/util/ReferenceResolver.java new file mode 100644 index 00000000000..dc27242bb34 --- /dev/null +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/util/ReferenceResolver.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.model.api.util; + +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.query.ObjectFilter; +import com.evolveum.midpoint.schema.GetOperationOptions; +import com.evolveum.midpoint.schema.SelectorOptions; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.util.annotation.Experimental; +import com.evolveum.midpoint.util.exception.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; + +import org.jetbrains.annotations.NotNull; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; + +/** + Resolves references in intelligent way: taking filters (and embedded expressions) into account. + * + * Different from ObjectResolver: + * 1) more focused (resolving references only, no additional methods) + * 2) advanced functionality (filters with expressions) +*/ +@Experimental +public interface ReferenceResolver { + + enum Source { + REPOSITORY, MODEL + } + + @FunctionalInterface + interface FilterEvaluator extends Serializable { + ObjectFilter evaluate(ObjectFilter rawFilter, OperationResult result) throws SchemaException, + ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, + ConfigurationException, SecurityViolationException; + } + + List> resolve(@NotNull ObjectReferenceType reference, + Collection> options, @NotNull Source source, + FilterEvaluator filterEvaluator, Task task, OperationResult result) throws SchemaException, + ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, + ConfigurationException, SecurityViolationException; +} 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 54bed7aafcd..3660688c027 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 @@ -163,7 +163,7 @@ private CertificationManager getCertificationManagerChecked() { return certificationManager; } - + @NotNull @Override public PrismObject getObject(Class clazz, String oid, Collection> rawOptions, Task task, OperationResult parentResult) throws ObjectNotFoundException, diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java index 087384777e7..d94e308517d 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java @@ -28,9 +28,11 @@ import com.evolveum.midpoint.TerminateSessionEvent; import com.evolveum.midpoint.model.api.authentication.*; +import com.evolveum.midpoint.model.api.util.ReferenceResolver; import com.evolveum.midpoint.model.common.stringpolicy.*; import com.evolveum.midpoint.model.impl.lens.projector.AssignmentOrigin; import com.evolveum.midpoint.model.impl.lens.projector.ContextLoader; +import com.evolveum.midpoint.model.impl.util.ReferenceResolverImpl; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.schema.cache.CacheConfigurationManager; import com.evolveum.midpoint.schema.util.*; @@ -159,7 +161,6 @@ public class ModelInteractionServiceImpl implements ModelInteractionService { private static final Trace LOGGER = TraceManager.getTrace(ModelInteractionServiceImpl.class); @Autowired private ContextFactory contextFactory; - @Autowired private Projector projector; @Autowired private SecurityEnforcer securityEnforcer; @Autowired private SecurityContextManager securityContextManager; @Autowired private SchemaTransformer schemaTransformer; @@ -169,6 +170,7 @@ public class ModelInteractionServiceImpl implements ModelInteractionService { @Autowired @Qualifier("cacheRepositoryService") private transient RepositoryService cacheRepositoryService; + @Autowired private ReferenceResolver referenceResolver; @Autowired private SystemObjectCache systemObjectCache; @Autowired private ArchetypeManager archetypeManager; @Autowired private RelationRegistry relationRegistry; @@ -1447,7 +1449,7 @@ private boolean determineDeputyValidity(PrismObject potentialDeputy, L @Nullable AbstractWorkItemType workItem, QName privilegeLimitationItemName, Task task, OperationResult result) { AssignmentEvaluator.Builder builder = new AssignmentEvaluator.Builder() - .repository(cacheRepositoryService) + .referenceResolver(referenceResolver) .focusOdo(new ObjectDeltaObject<>(potentialDeputy, null, potentialDeputy, potentialDeputy.getDefinition())) .channel(null) .objectResolver(objectResolver) diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentCollector.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentCollector.java index c5fba1df95b..f82d6a6a0b7 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentCollector.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentCollector.java @@ -6,15 +6,14 @@ */ package com.evolveum.midpoint.model.impl.lens; +import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; + +import static com.evolveum.midpoint.util.MiscUtil.emptyIfNull; + import java.util.ArrayList; import java.util.Collection; import java.util.List; -import com.evolveum.midpoint.model.impl.lens.assignments.AssignmentEvaluator; -import com.evolveum.midpoint.model.impl.lens.projector.AssignmentOrigin; -import com.evolveum.midpoint.model.impl.lens.projector.ContextLoader; -import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.schema.cache.CacheConfigurationManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @@ -22,37 +21,35 @@ import com.evolveum.midpoint.common.ActivationComputer; import com.evolveum.midpoint.common.Clock; import com.evolveum.midpoint.model.api.context.EvaluatedAssignment; +import com.evolveum.midpoint.model.api.util.ReferenceResolver; import com.evolveum.midpoint.model.common.ArchetypeManager; import com.evolveum.midpoint.model.common.SystemObjectCache; import com.evolveum.midpoint.model.common.mapping.MappingFactory; +import com.evolveum.midpoint.model.impl.lens.assignments.AssignmentEvaluator; +import com.evolveum.midpoint.model.impl.lens.projector.AssignmentOrigin; +import com.evolveum.midpoint.model.impl.lens.projector.ContextLoader; import com.evolveum.midpoint.model.impl.lens.projector.mappings.MappingEvaluator; +import com.evolveum.midpoint.prism.PrismContainerDefinition; +import com.evolveum.midpoint.prism.PrismContainerValue; +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.PlusMinusZero; import com.evolveum.midpoint.prism.util.ItemDeltaItem; import com.evolveum.midpoint.prism.util.ObjectDeltaObject; -import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.repo.cache.RepositoryCache; import com.evolveum.midpoint.repo.common.ObjectResolver; import com.evolveum.midpoint.schema.RelationRegistry; +import com.evolveum.midpoint.schema.cache.CacheConfigurationManager; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.FocusTypeUtil; import com.evolveum.midpoint.task.api.Task; -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.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ArchetypePolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentHolderType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; -import static com.evolveum.midpoint.util.MiscUtil.emptyIfNull; - -import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; - /** * @author katka * @@ -62,9 +59,7 @@ public class AssignmentCollector { private final static Trace LOGGER = TraceManager.getTrace(AssignmentCollector.class); - @Autowired - @Qualifier("cacheRepositoryService") - private RepositoryService repositoryService; + @Autowired private ReferenceResolver referenceResolver; @Autowired private SystemObjectCache systemObjectCache; @Autowired private ArchetypeManager archetypeManager; @Autowired private RelationRegistry relationRegistry; @@ -97,7 +92,7 @@ public Collection> col if (!focusBean.getAssignment().isEmpty() || forcedAssignments != null) { AssignmentEvaluator.Builder builder = new AssignmentEvaluator.Builder() - .repository(repositoryService) + .referenceResolver(referenceResolver) .focusOdo(new ObjectDeltaObject<>(focus, null, focus, focus.getDefinition())) .channel(null) .objectResolver(objectResolver) diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/assignments/AssignmentEvaluator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/assignments/AssignmentEvaluator.java index 0ed7f76d8bf..e2cfd6bff3c 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/assignments/AssignmentEvaluator.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/assignments/AssignmentEvaluator.java @@ -12,6 +12,7 @@ import com.evolveum.midpoint.common.ActivationComputer; import com.evolveum.midpoint.model.api.context.EvaluationOrder; +import com.evolveum.midpoint.model.api.util.ReferenceResolver; import com.evolveum.midpoint.model.common.SystemObjectCache; import com.evolveum.midpoint.model.common.mapping.MappingFactory; import com.evolveum.midpoint.model.impl.lens.LensContext; @@ -75,7 +76,7 @@ public class AssignmentEvaluator { // Spring beans and bean-like objects used - final RepositoryService repository; + final ReferenceResolver referenceResolver; final ObjectResolver objectResolver; final SystemObjectCache systemObjectCache; final RelationRegistry relationRegistry; @@ -91,7 +92,7 @@ public class AssignmentEvaluator { private final MemberOfEngine memberOfEngine; private AssignmentEvaluator(Builder builder) { - repository = builder.repository; + referenceResolver = builder.referenceResolver; focusOdo = builder.focusOdo; lensContext = builder.lensContext; channel = builder.channel; @@ -272,7 +273,7 @@ public boolean isMemberOfInvocationResultChanged(DeltaSetTriple { - private RepositoryService repository; + private ReferenceResolver referenceResolver; private ObjectDeltaObject focusOdo; private LensContext lensContext; private String channel; @@ -291,8 +292,8 @@ public static final class Builder { public Builder() { } - public Builder repository(RepositoryService val) { - repository = val; + public Builder referenceResolver(ReferenceResolver val) { + referenceResolver = val; return this; } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/assignments/TargetsEvaluation.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/assignments/TargetsEvaluation.java index 2ff49247943..384fe02ccfd 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/assignments/TargetsEvaluation.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/assignments/TargetsEvaluation.java @@ -7,6 +7,7 @@ package com.evolveum.midpoint.model.impl.lens.assignments; +import static com.evolveum.midpoint.model.api.util.ReferenceResolver.Source.REPOSITORY; import static com.evolveum.midpoint.model.impl.lens.assignments.Util.isChanged; import static com.evolveum.midpoint.prism.util.PrismTestUtil.getPrismContext; @@ -14,6 +15,10 @@ import java.util.Collections; import java.util.List; +import com.evolveum.midpoint.model.api.util.ReferenceResolver; + +import com.evolveum.midpoint.util.MiscUtil; + import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.model.api.ModelExecuteOptions; @@ -24,7 +29,6 @@ import com.evolveum.midpoint.model.impl.lens.LensUtil; import com.evolveum.midpoint.model.impl.util.ModelImplUtils; import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.query.ObjectFilter; import com.evolveum.midpoint.repo.common.expression.ExpressionUtil; import com.evolveum.midpoint.repo.common.expression.ExpressionVariables; import com.evolveum.midpoint.schema.constants.ExpressionConstants; @@ -37,7 +41,6 @@ import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; /** * Evaluates assignment target(s) - if there are any. @@ -171,7 +174,8 @@ private boolean shouldEvaluateAllAssignmentRelationsOnRecompute() { } @NotNull - private List> getTargets() throws SchemaException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { + private List> getTargets() throws SchemaException, ExpressionEvaluationException, + CommunicationException, ConfigurationException, SecurityViolationException { try { return resolveTargets(segment, ctx, result); } catch (ObjectNotFoundException ex) { @@ -182,75 +186,54 @@ private List> getTargets() throws Sc // For OrgType references we trigger the reconciliation (see MID-2242) ctx.evalAssignment.setForceRecon(true); return Collections.emptyList(); + } catch (SchemaException | ExpressionEvaluationException | CommunicationException | ConfigurationException | + SecurityViolationException | RuntimeException e) { + MiscUtil.throwAsSame(e, "Couldn't resolve targets in " + segment.assignment + " in " + + segment.sourceDescription + ": " + e.getMessage()); + throw e; // just to make compiler happy (exception is thrown in the above statement) } } @NotNull - private List> resolveTargets(AssignmentPathSegmentImpl segment, EvaluationContext ctx, + private List> resolveTargets(AssignmentPathSegmentImpl segment, EvaluationContext ctx, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { ObjectReferenceType targetRef = segment.assignment.getTargetRef(); - String oid = targetRef.getOid(); - - // Target is referenced, need to fetch it - Class targetClass; - if (targetRef.getType() != null) { - targetClass = ctx.ae.prismContext.getSchemaRegistry().determineCompileTimeClass(targetRef.getType()); - if (targetClass == null) { - throw new SchemaException("Cannot determine type from " + targetRef.getType() + " in target reference in " + segment.assignment + " in " + segment.sourceDescription); - } - } else { - throw new SchemaException("Missing type in target reference in " + segment.assignment + " in " + segment.sourceDescription); - } - - if (oid == null) { - LOGGER.trace("Resolving dynamic target ref"); - if (targetRef.getFilter() == null) { - throw new SchemaException("The OID and filter are both null in assignment targetRef in "+segment.source); - } - return resolveTargetsFromFilter(targetClass, targetRef.getFilter(), segment, ctx, result); - } else { - LOGGER.trace("Resolving target {}:{} from repository", targetClass.getSimpleName(), oid); - PrismObject target; - try { - target = ctx.ae.repository.getObject(targetClass, oid, null, result); - } catch (SchemaException e) { - throw new SchemaException(e.getMessage() + " in " + segment.sourceDescription, e); - } - // Not handling object not found exception here. Caller will handle that. - if (target == null) { - throw new IllegalArgumentException("Got null target from repository, oid:"+oid+", class:"+targetClass+" (should not happen, probably a bug) in "+segment.sourceDescription); - } - return Collections.singletonList(target); - } + ReferenceResolver.FilterEvaluator filterEvaluator = createFilterEvaluator(segment, ctx); + return ctx.ae.referenceResolver.resolve(targetRef, null, REPOSITORY, filterEvaluator, ctx.task, result); } @NotNull - private List> resolveTargetsFromFilter(Class targetClass, - SearchFilterType filter, AssignmentPathSegmentImpl segment, - EvaluationContext ctx, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException{ - ModelExpressionThreadLocalHolder.pushExpressionEnvironment(new ExpressionEnvironment<>(ctx.ae.lensContext, null, ctx.task, result)); - try { - PrismObject systemConfiguration = ctx.ae.systemObjectCache.getSystemConfiguration(result); - ExpressionVariables variables = ModelImplUtils.getDefaultExpressionVariables(segment.source, null, null, systemConfiguration.asObjectable(), ctx.ae.prismContext); - variables.put(ExpressionConstants.VAR_SOURCE, segment.getOrderOneObject(), ObjectType.class); - AssignmentPathVariables assignmentPathVariables = LensUtil.computeAssignmentPathVariables(ctx.assignmentPath); - if (assignmentPathVariables != null) { - ModelImplUtils.addAssignmentPathVariables(assignmentPathVariables, variables, getPrismContext()); - } - variables.addVariableDefinitions(ctx.ae.getAssignmentEvaluationVariables()); - ObjectFilter origFilter = ctx.ae.prismContext.getQueryConverter().parseFilter(filter, targetClass); - // TODO: expression profile should be determined from the holding object archetype - ExpressionProfile expressionProfile = MiscSchemaUtil.getExpressionProfile(); - ObjectFilter evaluatedFilter = ExpressionUtil.evaluateFilterExpressions(origFilter, variables, expressionProfile, ctx.ae.mappingFactory.getExpressionFactory(), ctx.ae.prismContext, " evaluating resource filter expression ", ctx.task, result); - if (evaluatedFilter == null) { - throw new SchemaException("The OID is null and filter could not be evaluated in assignment targetRef in "+segment.source); - } + private ReferenceResolver.FilterEvaluator createFilterEvaluator(AssignmentPathSegmentImpl segment, + EvaluationContext ctx) { + return (rawFilter, result1) -> { + ModelExpressionThreadLocalHolder.pushExpressionEnvironment( + new ExpressionEnvironment<>(ctx.ae.lensContext, null, ctx.task, result1)); + try { + // TODO: expression profile should be determined from the holding object archetype + ExpressionProfile expressionProfile = MiscSchemaUtil.getExpressionProfile(); + ExpressionVariables variables = createVariables(segment, ctx, result1); + return ExpressionUtil.evaluateFilterExpressions(rawFilter, variables, expressionProfile, + ctx.ae.mappingFactory.getExpressionFactory(), ctx.ae.prismContext, + " evaluating resource filter expression ", ctx.task, result1); + } finally { + ModelExpressionThreadLocalHolder.popExpressionEnvironment(); + } + }; + } - return ctx.ae.repository.searchObjects(targetClass, ctx.ae.prismContext.queryFactory().createQuery(evaluatedFilter), null, result); - // we don't check for no targets here; as we don't care for referential integrity - } finally { - ModelExpressionThreadLocalHolder.popExpressionEnvironment(); + @NotNull + private ExpressionVariables createVariables(AssignmentPathSegmentImpl segment, EvaluationContext ctx, + OperationResult result) throws SchemaException { + PrismObject systemConfiguration = ctx.ae.systemObjectCache.getSystemConfiguration(result); + ExpressionVariables variables = ModelImplUtils.getDefaultExpressionVariables(segment.source, null, + null, systemConfiguration.asObjectable(), ctx.ae.prismContext); + variables.put(ExpressionConstants.VAR_SOURCE, segment.getOrderOneObject(), ObjectType.class); + AssignmentPathVariables assignmentPathVariables = LensUtil.computeAssignmentPathVariables(ctx.assignmentPath); + if (assignmentPathVariables != null) { + ModelImplUtils.addAssignmentPathVariables(assignmentPathVariables, variables, getPrismContext()); } + variables.addVariableDefinitions(ctx.ae.getAssignmentEvaluationVariables()); + return variables; } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/AssignmentProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/AssignmentProcessor.java index f1f228909f7..d234797ee23 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/AssignmentProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/AssignmentProcessor.java @@ -14,7 +14,7 @@ import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; -import com.evolveum.midpoint.model.impl.lens.*; +import com.evolveum.midpoint.model.api.util.ReferenceResolver; import com.evolveum.midpoint.model.impl.lens.construction.EvaluatedConstructionImpl; import com.evolveum.midpoint.model.impl.lens.construction.EvaluatedConstructionPack; import com.evolveum.midpoint.model.impl.lens.projector.ComplexConstructionConsumer; @@ -55,7 +55,6 @@ import com.evolveum.midpoint.prism.path.UniformItemPath; import com.evolveum.midpoint.prism.util.ObjectDeltaObject; 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.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; @@ -96,14 +95,11 @@ @ProcessorExecution(focusRequired = true, focusType = AssignmentHolderType.class) public class AssignmentProcessor implements ProjectorProcessor { - @Autowired - @Qualifier("cacheRepositoryService") - private RepositoryService repositoryService; - @Autowired @Qualifier("modelObjectResolver") private ObjectResolver objectResolver; + @Autowired private ReferenceResolver referenceResolver; @Autowired private SystemObjectCache systemObjectCache; @Autowired private RelationRegistry relationRegistry; @Autowired private PrismContext prismContext; @@ -1127,7 +1123,7 @@ private void addReferences(Collection extractedReferences, private AssignmentEvaluator createAssignmentEvaluator(LensContext context, XMLGregorianCalendar now) throws SchemaException { return new AssignmentEvaluator.Builder() - .repository(repositoryService) + .referenceResolver(referenceResolver) .focusOdo(context.getFocusContext().getObjectDeltaObject()) .lensContext(context) .channel(context.getChannel()) diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/ActionContext.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/ActionContext.java index f4b231dd304..0594a8a71be 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/ActionContext.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/ActionContext.java @@ -7,9 +7,12 @@ package com.evolveum.midpoint.model.impl.lens.projector.policy.scriptExecutor; +import com.evolveum.midpoint.model.api.context.EvaluatedPolicyRule; import com.evolveum.midpoint.model.impl.lens.EvaluatedPolicyRuleImpl; import com.evolveum.midpoint.model.impl.lens.LensContext; import com.evolveum.midpoint.model.impl.lens.LensFocusContext; +import com.evolveum.midpoint.schema.constants.ExpressionConstants; +import com.evolveum.midpoint.schema.expression.VariablesMap; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.xml.ns._public.common.common_3.ScriptExecutionPolicyActionType; @@ -36,4 +39,9 @@ class ActionContext { this.task = task; this.beans = beans; } + + void putIntoVariables(VariablesMap variables) { + variables.put(ExpressionConstants.VAR_POLICY_ACTION, action, ScriptExecutionPolicyActionType.class); + variables.put(ExpressionConstants.VAR_POLICY_RULE, rule, EvaluatedPolicyRule.class); + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/LinkSourceFinder.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/LinkSourceFinder.java index 528061b17a4..04a7210d72a 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/LinkSourceFinder.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/LinkSourceFinder.java @@ -88,7 +88,10 @@ CompleteQuery getSourcesAsQuery(List sourceSele ExpressionEvaluationException { ObjectQuery query = createQuery(sourceSelectors); Class objectType = getSourceType(); - return new CompleteQuery<>(objectType, query, null); + CompleteQuery completeQuery = new CompleteQuery<>(objectType, query, null); + + LOGGER.trace("Sources as query:\n{}", completeQuery.debugDumpLazily()); + return completeQuery; } private ObjectQuery createQuery(List sourceSelectors) throws CommunicationException, @@ -133,7 +136,6 @@ private List createExpectedReferenceValues(LinkSourceObject @NotNull private List> searchForSources(CompleteQuery completeQuery) throws SchemaException { - LOGGER.trace("Looking for link sources using computed query:\n{}", completeQuery.debugDumpLazily()); //noinspection unchecked return (List) beans.repositoryService.searchObjects(completeQuery.getType(), completeQuery.getQuery(), completeQuery.getOptions(), result); @@ -141,7 +143,6 @@ private List> searchForSources(CompleteQuery searchForSourceReferences(CompleteQuery completeQuery) throws SchemaException { - LOGGER.trace("Looking for link sources (as references) using computed query:\n{}", completeQuery.debugDumpLazily()); List references = new ArrayList<>(); beans.repositoryService.searchObjectsIterative(completeQuery.getType(), completeQuery.getQuery(), (object, parentResult) -> diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/PolicyRuleScriptExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/PolicyRuleScriptExecutor.java index e35635fe356..7b192570c00 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/PolicyRuleScriptExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/PolicyRuleScriptExecutor.java @@ -7,11 +7,18 @@ package com.evolveum.midpoint.model.impl.lens.projector.policy.scriptExecutor; import com.evolveum.midpoint.model.api.ModelService; +import com.evolveum.midpoint.model.api.util.ReferenceResolver; import com.evolveum.midpoint.model.impl.ModelObjectResolver; import com.evolveum.midpoint.model.impl.scripting.ScriptingExpressionEvaluator; +import com.evolveum.midpoint.model.impl.security.RunAsRunner; +import com.evolveum.midpoint.model.impl.security.RunAsRunnerFactory; import com.evolveum.midpoint.repo.common.expression.ExpressionFactory; import com.evolveum.midpoint.security.api.SecurityContextManager; +import com.evolveum.midpoint.util.exception.CommonException; + +import com.evolveum.midpoint.util.logging.LoggingUtils; + import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -54,8 +61,10 @@ public class PolicyRuleScriptExecutor { @Autowired ModelService modelService; @Autowired SecurityContextManager securityContextManager; @Autowired ModelObjectResolver modelObjectResolver; + @Autowired ReferenceResolver referenceResolver; @Autowired ExpressionFactory expressionFactory; @Autowired ScriptingExpressionEvaluator scriptingExpressionEvaluator; + @Autowired RunAsRunnerFactory runAsRunnerFactory; public void execute(@NotNull LensContext context, Task task, OperationResult parentResult) throws SchemaException { @@ -108,13 +117,18 @@ private void executeScriptsFromCollectedRules(List enabledActions = rule.getEnabledActions(ScriptExecutionPolicyActionType.class); LOGGER.trace("Rule {} has {} enabled script execution actions", rule, enabledActions.size()); for (ScriptExecutionPolicyActionType action : enabledActions) { ActionContext actx = new ActionContext(action, rule, context, task, this); - executeScriptingAction(actx, result); + try { + // We should consider ordering actions to be executed by runAsRef to avoid unnecessary context switches. + runAsRunner.runAs(() -> executeScriptingAction(actx, result), actx.action.getRunAsRef(), result); + } catch (CommonException e) { + LoggingUtils.logUnexpectedException(LOGGER, "Couldn't execute scripting action - continuing with others (if present)", e); + } } } } catch (Throwable t) { diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/ScriptingTaskCreator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/ScriptingTaskCreator.java index 0b412e78fe7..c81e1f60b0d 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/ScriptingTaskCreator.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/ScriptingTaskCreator.java @@ -7,9 +7,22 @@ package com.evolveum.midpoint.model.impl.lens.projector.policy.scriptExecutor; +import static com.evolveum.midpoint.model.api.util.ReferenceResolver.Source.MODEL; import static com.evolveum.midpoint.schema.util.ObjectTypeUtil.createObjectRef; import static com.evolveum.midpoint.xml.ns._public.common.common_3.TaskExecutionStatusType.RUNNABLE; +import com.evolveum.midpoint.model.api.util.ReferenceResolver; + +import com.evolveum.midpoint.model.common.expression.ExpressionEnvironment; +import com.evolveum.midpoint.model.common.expression.ModelExpressionThreadLocalHolder; +import com.evolveum.midpoint.model.impl.util.ModelImplUtils; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.repo.common.expression.ExpressionUtil; +import com.evolveum.midpoint.repo.common.expression.ExpressionVariables; +import com.evolveum.midpoint.schema.expression.ExpressionProfile; +import com.evolveum.midpoint.schema.util.MiscSchemaUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; + import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.prism.PrismProperty; @@ -18,12 +31,11 @@ import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.security.api.MidPointPrincipal; import com.evolveum.midpoint.util.exception.*; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AsynchronousScriptExecutionType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskRecurrenceType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ExecuteScriptType; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; +import java.util.List; + /** * Creates tasks of given type (single-run, iterative) for given (specified) executeScript beans. */ @@ -58,8 +70,15 @@ TaskType createEmptyTask(OperationResult result) AsynchronousScriptExecutionType asynchronousExecution = actx.action.getAsynchronousExecution(); TaskType newTask; if (asynchronousExecution.getTaskTemplateRef() != null) { - newTask = beans.modelObjectResolver.resolve(asynchronousExecution.getTaskTemplateRef(), TaskType.class, - null, "task template", actx.task, result); + List> tasks = beans.referenceResolver.resolve(asynchronousExecution.getTaskTemplateRef(), null, + MODEL, createTaskFilterEvaluator(), actx.task, result); + if (tasks.isEmpty()) { + throw new ObjectNotFoundException("Task template was specified but was not found"); + } else if (tasks.size() > 1) { + throw new IllegalStateException("Task template reference resolution lead to more than one task template: " + tasks); + } else { + newTask = (TaskType) tasks.get(0).asObjectable(); + } } else { newTask = new TaskType(beans.prismContext); newTask.setName(PolyStringType.fromOrig("Execute script")); @@ -73,6 +92,28 @@ TaskType createEmptyTask(OperationResult result) return newTask; } + private ReferenceResolver.FilterEvaluator createTaskFilterEvaluator() { + return (rawFilter, result1) -> { + ModelExpressionThreadLocalHolder.pushExpressionEnvironment( + new ExpressionEnvironment<>(actx.context, null, actx.task, result1)); + try { + ExpressionVariables variables = createVariables(); + ExpressionProfile expressionProfile = MiscSchemaUtil.getExpressionProfile(); + return ExpressionUtil.evaluateFilterExpressions(rawFilter, variables, expressionProfile, + beans.expressionFactory, beans.prismContext, + "evaluating task template filter expression ", actx.task, result1); + } finally { + ModelExpressionThreadLocalHolder.popExpressionEnvironment(); + } + }; + } + + private ExpressionVariables createVariables() throws SchemaException { + ExpressionVariables variables = ModelImplUtils.getDefaultExpressionVariables(actx.context, null); + actx.putIntoVariables(variables); + return variables; + } + /** * Inserts script into task. */ diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/SynchronousScriptExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/SynchronousScriptExecutor.java index 5d1d76c35d8..7d77e4f693a 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/SynchronousScriptExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/scriptExecutor/SynchronousScriptExecutor.java @@ -78,8 +78,7 @@ private ExecuteScriptType addInputIfNeeded(ExecuteScriptType specifiedExecuteScr private VariablesMap createInitialVariables() { VariablesMap rv = new VariablesMap(); - rv.put(ExpressionConstants.VAR_POLICY_ACTION, actx.action, ScriptExecutionPolicyActionType.class); - rv.put(ExpressionConstants.VAR_POLICY_RULE, actx.rule, EvaluatedPolicyRule.class); + actx.putIntoVariables(rv); rv.put(ExpressionConstants.VAR_MODEL_CONTEXT, actx.context, ModelContext.class); return rv; } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/RunAsRunner.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/RunAsRunner.java new file mode 100644 index 00000000000..55ca5cedbcc --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/RunAsRunner.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.model.impl.security; + +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.security.api.MidPointPrincipal; +import com.evolveum.midpoint.util.CheckedRunnable; +import com.evolveum.midpoint.util.annotation.Experimental; +import com.evolveum.midpoint.util.exception.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; + +import org.jetbrains.annotations.NotNull; +import org.springframework.security.core.Authentication; + +import javax.xml.namespace.QName; +import java.util.Objects; + +/** + * Takes care of optimized "run as" operations. + * + * By optimization we mean that if the currently logged-in user is the same as the one specified in runAs, + * no login procedure is carried out. + * + * BEWARE: In order to return the original security context, the close() method must be called. + * This object is therefore to be used in try-with-resources context ONLY. + */ +@Experimental +public class RunAsRunner implements AutoCloseable { + + @NotNull private final RunAsRunnerFactory beans; + + private final Authentication originalAuthentication; + + // Should be instantiated via factory only. + RunAsRunner(@NotNull RunAsRunnerFactory factory) { + this.beans = factory; + this.originalAuthentication = beans.securityContextManager.getAuthentication(); + } + + @Override + public void close() { + beans.securityContextManager.setupPreAuthenticatedSecurityContext(originalAuthentication); + } + + public void runAs(CheckedRunnable runnable, ObjectReferenceType identityRef, OperationResult parentResult) + throws CommonException { + establishRequiredIdentity(identityRef, parentResult); + runnable.run(); + } + + private void establishRequiredIdentity(ObjectReferenceType identityRef, OperationResult parentResult) + throws SecurityViolationException, SchemaException, ObjectNotFoundException, CommunicationException, + ConfigurationException, ExpressionEvaluationException { + if (identityRef != null) { + String oid = identityRef.getOid(); + if (oid == null) { + throw new UnsupportedOperationException("Identity reference without OID is not supported"); + } + if (!isLoggedIn(oid)) { + QName typeName = Objects.requireNonNull(identityRef.getType(), "target type"); + logIn(typeName, oid, parentResult); + } + } else { + // no requirements here + } + } + + private boolean isLoggedIn(@NotNull String oid) throws SecurityViolationException { + MidPointPrincipal principal = beans.securityContextManager.getPrincipal(); + return principal != null && oid.equals(principal.getOid()); + } + + private void logIn(QName typeName, String oid, OperationResult parentResult) throws SchemaException, + ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, + ExpressionEvaluationException { + Class clazz = getObjectClass(typeName); + PrismObject focus = beans.repositoryService.getObject(clazz, oid, null, parentResult); + beans.securityContextManager.setupPreAuthenticatedSecurityContext(focus); + } + + @NotNull + private Class getObjectClass(QName typeName) { + Class clazz = beans.prismContext.getSchemaRegistry().determineClassForTypeRequired(typeName); + if (!FocusType.class.isAssignableFrom(clazz)) { + throw new IllegalStateException("Identity is not a FocusType: " + clazz + " (name: " + typeName + ")"); + } + //noinspection unchecked + return (Class) clazz; + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/RunAsRunnerFactory.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/RunAsRunnerFactory.java new file mode 100644 index 00000000000..f1ced8d625c --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/RunAsRunnerFactory.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.model.impl.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.repo.api.RepositoryService; +import com.evolveum.midpoint.security.api.SecurityContextManager; + +/** + * Factory for RunAsRunner instances. Its sole purpose is to provide necessary autowired prism beans. + */ +@Component +public class RunAsRunnerFactory { + + @Autowired PrismContext prismContext; + @Autowired @Qualifier("cacheRepositoryService") RepositoryService repositoryService; + @Autowired SecurityContextManager securityContextManager; + + /** + * Do not forget to close returned runner! Use try-with-resources construct. + */ + public RunAsRunner runner() { + return new RunAsRunner(this); + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/util/ModelImplUtils.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/util/ModelImplUtils.java index 6b37631038b..36ccff2ac0c 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/util/ModelImplUtils.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/util/ModelImplUtils.java @@ -588,12 +588,11 @@ public static ExpressionVariables getDefaultExpressionVariables(@NotNull LensCon variables.registerAlias(ExpressionConstants.VAR_ACCOUNT, ExpressionConstants.VAR_PROJECTION); variables.registerAlias(ExpressionConstants.VAR_SHADOW, ExpressionConstants.VAR_PROJECTION); variables.put(ExpressionConstants.VAR_RESOURCE, projCtx.getResource(), projCtx.getResource().asPrismObject().getDefinition()); + variables.put(ExpressionConstants.VAR_OPERATION, projCtx.getOperation().getValue(), String.class); + variables.put(ExpressionConstants.VAR_ITERATION, LensUtil.getIterationVariableValue(projCtx), Integer.class); + variables.put(ExpressionConstants.VAR_ITERATION_TOKEN, LensUtil.getIterationTokenVariableValue(projCtx), String.class); } - variables.put(ExpressionConstants.VAR_OPERATION, projCtx.getOperation().getValue(), String.class); - variables.put(ExpressionConstants.VAR_ITERATION, LensUtil.getIterationVariableValue(projCtx), Integer.class); - variables.put(ExpressionConstants.VAR_ITERATION_TOKEN, LensUtil.getIterationTokenVariableValue(projCtx), String.class); - variables.put(ExpressionConstants.VAR_CONFIGURATION, context.getSystemConfiguration(), context.getSystemConfiguration().getDefinition()); return variables; } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/util/ReferenceResolverImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/util/ReferenceResolverImpl.java new file mode 100644 index 00000000000..003a8fa52c9 --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/util/ReferenceResolverImpl.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.model.impl.util; + +import static java.util.Collections.singletonList; + +import java.util.Collection; +import java.util.List; + +import com.evolveum.midpoint.model.api.util.ReferenceResolver; + +import com.evolveum.midpoint.schema.SearchResultList; + +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.model.api.ModelService; +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.query.ObjectFilter; +import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.repo.api.RepositoryService; +import com.evolveum.midpoint.schema.GetOperationOptions; +import com.evolveum.midpoint.schema.SelectorOptions; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.task.api.Task; +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; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; + +@Experimental +@Component +public class ReferenceResolverImpl implements ReferenceResolver { + + private static final Trace LOGGER = TraceManager.getTrace(ReferenceResolverImpl.class); + + @Autowired private PrismContext prismContext; + @Autowired private ModelService modelService; + @Autowired @Qualifier("cacheRepositoryService") private RepositoryService repositoryService; + + public List> resolve(@NotNull ObjectReferenceType reference, + Collection> options, @NotNull Source source, + FilterEvaluator filterEvaluator, Task task, OperationResult result) throws SchemaException, + ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, + ConfigurationException, SecurityViolationException { + String oid = reference.getOid(); + + Class targetClass = getTargetClass(reference); + + if (oid == null) { + return resolveFromFilter(targetClass, reference, options, source, filterEvaluator, task, result); + } else { + return singletonList(resolveFromOid(targetClass, oid, options, source, task, result)); + } + } + + private Class getTargetClass(@NotNull ObjectReferenceType reference) { + Class targetClass; + if (reference.getType() != null) { + targetClass = prismContext.getSchemaRegistry().determineClassForTypeRequired(reference.getType(), ObjectType.class); + } else { + throw new IllegalArgumentException("Missing type in reference " + reference); + } + return targetClass; + } + + @NotNull + private PrismObject resolveFromOid(Class targetClass, String oid, + Collection> options, @NotNull Source source, Task task, + OperationResult result) + throws SchemaException, ObjectNotFoundException, ConfigurationException, CommunicationException, + SecurityViolationException, ExpressionEvaluationException { + LOGGER.trace("Resolving {}:{} from {}", targetClass.getSimpleName(), oid, source); + switch (source) { + case REPOSITORY: + return repositoryService.getObject(targetClass, oid, options, result); + case MODEL: + return modelService.getObject(targetClass, oid, options, task, result); + default: + throw new AssertionError(source); + } + } + + @NotNull + private List> resolveFromFilter(Class targetClass, + ObjectReferenceType reference, Collection> options, @NotNull Source source, + FilterEvaluator filterEvaluator, Task task, OperationResult result) throws SchemaException, + ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, + ConfigurationException, SecurityViolationException { + LOGGER.trace("Resolving filter on {} from {}", targetClass.getSimpleName(), source); + SearchFilterType filterBean = reference.getFilter(); + if (filterBean == null) { + throw new IllegalArgumentException("The OID and filter are both null in a reference: " + reference); + } + ObjectFilter filter = prismContext.getQueryConverter().parseFilter(filterBean, targetClass); + ObjectFilter evaluatedFilter = filterEvaluator != null ? filterEvaluator.evaluate(filter, result) : filter; + + if (evaluatedFilter == null) { + throw new SchemaException("The OID is null and filter could not be evaluated in " + reference); + } + ObjectQuery query = prismContext.queryFactory().createQuery(evaluatedFilter); + SearchResultList> objects; + switch (source) { + case REPOSITORY: + objects = repositoryService.searchObjects(targetClass, query, options, result); + break; + case MODEL: + objects = modelService.searchObjects(targetClass, query, options, task, result); + break; + default: + throw new AssertionError(source); + } + //noinspection unchecked + return (List>) objects; + } +} diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAbstractAssignmentEvaluator.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAbstractAssignmentEvaluator.java index d0fb687a5d7..68cb3353833 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAbstractAssignmentEvaluator.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAbstractAssignmentEvaluator.java @@ -17,13 +17,6 @@ import java.util.*; import javax.xml.namespace.QName; -import com.evolveum.midpoint.model.impl.lens.assignments.AssignmentEvaluator; -import com.evolveum.midpoint.model.impl.lens.assignments.EvaluatedAssignmentImpl; - -import com.evolveum.midpoint.model.impl.lens.construction.Construction; -import com.evolveum.midpoint.model.impl.lens.construction.EvaluatedConstructionImpl; -import com.evolveum.midpoint.model.impl.lens.projector.ContextLoader; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.annotation.DirtiesContext; @@ -34,11 +27,17 @@ import com.evolveum.midpoint.common.ActivationComputer; import com.evolveum.midpoint.common.Clock; +import com.evolveum.midpoint.model.api.util.ReferenceResolver; import com.evolveum.midpoint.model.common.SystemObjectCache; import com.evolveum.midpoint.model.common.mapping.MappingFactory; import com.evolveum.midpoint.model.common.mapping.MappingImpl; import com.evolveum.midpoint.model.common.mapping.PrismValueDeltaSetTripleProducer; +import com.evolveum.midpoint.model.impl.lens.assignments.AssignmentEvaluator; +import com.evolveum.midpoint.model.impl.lens.assignments.EvaluatedAssignmentImpl; +import com.evolveum.midpoint.model.impl.lens.construction.Construction; +import com.evolveum.midpoint.model.impl.lens.construction.EvaluatedConstructionImpl; import com.evolveum.midpoint.model.impl.lens.projector.AssignmentOrigin; +import com.evolveum.midpoint.model.impl.lens.projector.ContextLoader; import com.evolveum.midpoint.model.impl.lens.projector.Projector; import com.evolveum.midpoint.model.impl.lens.projector.mappings.MappingEvaluator; import com.evolveum.midpoint.prism.*; @@ -60,12 +59,16 @@ import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.util.TestUtil; import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; @ContextConfiguration(locations = { "classpath:ctx-model-test-main.xml" }) @DirtiesContext(classMode = ClassMode.AFTER_CLASS) public abstract class TestAbstractAssignmentEvaluator extends AbstractLensTest { + @Autowired private ReferenceResolver referenceResolver; @Autowired private RepositoryService repositoryService; @Autowired @Qualifier("modelObjectResolver") private ObjectResolver objectResolver; @Autowired private SystemObjectCache systemObjectCache; @@ -77,7 +80,6 @@ public abstract class TestAbstractAssignmentEvaluator extends AbstractLensTest { @Autowired private Projector projector; @Autowired private ContextLoader contextLoader; - public abstract File[] getRoleCorpFiles(); @Override @@ -893,7 +895,7 @@ protected AssignmentEvaluator createAssignmentEvaluator(ObjectDeltaObj focusContext.setObjectNew(focusOdo.getNewObject()); return new AssignmentEvaluator.Builder() - .repository(repositoryService) + .referenceResolver(referenceResolver) .focusOdo(focusOdo) .objectResolver(objectResolver) .systemObjectCache(systemObjectCache) diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java index dda39c34924..6bdabcbbf6c 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java @@ -515,7 +515,7 @@ public void test062JackDeputyOfGuybrushDeputyOfBarbossaInLoginMode() throws Exce LensContext context = new LensContextPlaceholder<>(jack, prismContext); AssignmentEvaluator assignmentEvaluator = new AssignmentEvaluator.Builder() - .repository(repositoryService) + .referenceResolver(referenceResolver) .focusOdo(new ObjectDeltaObject<>(jack, null, jack, jack.getDefinition())) .lensContext(context) .channel(context.getChannel()) diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMemberRecompute.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMemberRecompute.java index 55f11419971..59435e0d056 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMemberRecompute.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMemberRecompute.java @@ -13,6 +13,7 @@ import com.evolveum.midpoint.prism.PrismPropertyDefinition; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; +import com.evolveum.midpoint.test.PredefinedTestMethodTracing; import com.evolveum.midpoint.util.exception.*; import org.jetbrains.annotations.NotNull; @@ -126,8 +127,7 @@ public void test100ChangeCostCenter() throws Exception { Task recomputeTask = waitForTaskFinish(taskOid, false); assertTask(recomputeTask, "recompute task after") .display() - .assertSuccess() - .assertClosed(); + .assertSuccess(); assertUserAfterByUsername("user-dcs-0000") .assertCostCenter("07999"); diff --git a/model/model-intest/src/test/resources/member-recompute/archetype-department.xml b/model/model-intest/src/test/resources/member-recompute/archetype-department.xml index 1d1b1f77a08..ea59c5fb431 100644 --- a/model/model-intest/src/test/resources/member-recompute/archetype-department.xml +++ b/model/model-intest/src/test/resources/member-recompute/archetype-department.xml @@ -7,6 +7,7 @@ department @@ -35,7 +36,19 @@ iterative - + + + + + + + + +