Skip to content

Commit

Permalink
Add 'runAsRef' and task filters to scripting rules
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mederly committed May 28, 2020
1 parent 0901dda commit f0edcd2
Show file tree
Hide file tree
Showing 30 changed files with 635 additions and 140 deletions.
Expand Up @@ -134,12 +134,22 @@ <T extends ItemDefinition> T findItemDefinitionByFullPath(Class<? extends Object
// Takes XSD types into account as well
<T> Class<T> determineClassForType(QName type);

default <T> Class<T> determineClassForTypeRequired(QName type, Class<T> 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<T>) clazz;
}
}

default <T> Class<T> determineClassForTypeRequired(QName type) {
Class<T> clazz = determineClassForType(type);
if (clazz != null) {
return clazz;
} else {
throw new IllegalStateException("No class for " + type);
throw new IllegalArgumentException("No class for " + type);
}
}

Expand Down
Expand Up @@ -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;

Expand Down
Expand Up @@ -2074,6 +2074,29 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="runAsRef" type="tns:ObjectReferenceType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
<p>
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.
</p>
<p>
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.
</p>
<p>
If no user is specified then the identify of currently logged-in user or
the owner of the task will be used.
</p>
</xsd:documentation>
<xsd:appinfo>
<a:objectReferenceTargetType>tns:FocusType</a:objectReferenceTargetType>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
Expand Down Expand Up @@ -2820,6 +2843,7 @@
Options for asynchronous script execution.
</xsd:documentation>
<xsd:appinfo>
<a:container>true</a:container>
<a:since>4.2</a:since>
<a:experimental>true</a:experimental>
</xsd:appinfo>
Expand All @@ -2831,15 +2855,16 @@
Mode of execution.
</xsd:documentation>
</xsd:annotation>
</xsd:element><xsd:element name="taskTemplateRef" type="c:ObjectReferenceType" minOccurs="0">
</xsd:element>
<xsd:element name="taskTemplateRef" type="c:ObjectReferenceType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Task template to use.

EXPERIMENTAL.
</xsd:documentation>
<xsd:appinfo>
<a:objectReferenceTargetType>TaskType</a:objectReferenceTargetType>
<a:objectReferenceTargetType>c:TaskType</a:objectReferenceTargetType>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
Expand Down
@@ -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;
}
23 changes: 23 additions & 0 deletions infra/util/src/main/java/com/evolveum/midpoint/util/MiscUtil.java
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -813,4 +816,24 @@ public static Class<?> determineCommonAncestor(Collection<Class<?>> 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 <T extends Throwable> void throwAsSame(Throwable original, String message) throws T {
try {
Constructor<? extends Throwable> 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;
}
}
}
Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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();
}
}
}
Expand Up @@ -142,6 +142,7 @@ public interface ModelService {
* unknown error from underlying layers or other unexpected
* state
*/
@NotNull
<T extends ObjectType> PrismObject<T> getObject(Class<T> type, String oid, Collection<SelectorOptions<GetOperationOptions>> options,
Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, SecurityViolationException,
CommunicationException, ConfigurationException, ExpressionEvaluationException;
Expand Down
@@ -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<PrismObject<? extends ObjectType>> resolve(@NotNull ObjectReferenceType reference,
Collection<SelectorOptions<GetOperationOptions>> options, @NotNull Source source,
FilterEvaluator filterEvaluator, Task task, OperationResult result) throws SchemaException,
ObjectNotFoundException, ExpressionEvaluationException, CommunicationException,
ConfigurationException, SecurityViolationException;
}
Expand Up @@ -163,7 +163,7 @@ private CertificationManager getCertificationManagerChecked() {
return certificationManager;
}


@NotNull
@Override
public <T extends ObjectType> PrismObject<T> getObject(Class<T> clazz, String oid,
Collection<SelectorOptions<GetOperationOptions>> rawOptions, Task task, OperationResult parentResult) throws ObjectNotFoundException,
Expand Down
Expand Up @@ -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.*;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -1447,7 +1449,7 @@ private boolean determineDeputyValidity(PrismObject<UserType> potentialDeputy, L
@Nullable AbstractWorkItemType workItem, QName privilegeLimitationItemName, Task task, OperationResult result) {
AssignmentEvaluator.Builder<UserType> builder =
new AssignmentEvaluator.Builder<UserType>()
.repository(cacheRepositoryService)
.referenceResolver(referenceResolver)
.focusOdo(new ObjectDeltaObject<>(potentialDeputy, null, potentialDeputy, potentialDeputy.getDefinition()))
.channel(null)
.objectResolver(objectResolver)
Expand Down

0 comments on commit f0edcd2

Please sign in to comment.