Skip to content

Commit

Permalink
Start implementing expression profiles PoC
Browse files Browse the repository at this point in the history
Expression profiles rely upon the reliable and precise determination
of the origin of individual expressions - i.e., in which part of which
object they were defined. This allows editing of such places by persons
that are less experienced or trustworthy than e.g. the root; in exchange
for using more restricting profiles for execution of such expressions.

However, this problem is harder than one could expect. The complexity
stems from various ways in which parts of midPoint configuration are
mixed together - links (inheritance, reuse) between resources, object
type definitions, object templates, policy rules, correlators, and many
more. In the most complex cases (arbitrary object merging) we will have
to use the provenance metadata.

This commit provides first steps towards the origin determination and
propagation throughout the system. The basic concepts are:

1. ConfigurationItemOrigin describing the origin itself.
2. ConfigurationItem that couples the config data with the origin.
3. Typed "configuration items" that facilitate the work with all kinds
of configuration data, along with precise and understandable error
reporting. Highly experimental for now, though.

Other minor changes:
- Fixed forgotten InboundMappingUseType#DATA_TRANSFER -> SYNCHRONIZATION
- Adapted code to changes in ItemDeltaItem/ObjectDeltaObject
(prism 45570df5a1259d94950f572a08da1ae1eb6e8e00)

Work in progress.
  • Loading branch information
mederly committed Jul 20, 2023
1 parent 6274b8d commit 10c8252
Show file tree
Hide file tree
Showing 171 changed files with 3,280 additions and 1,618 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2376,9 +2376,11 @@ private static String createSimulationResultIcon() {

public static ObjectFilter evaluateExpressionsInFilter(ObjectFilter objectFilter, VariablesMap variables, OperationResult result, PageBase pageBase) {
try {
return ExpressionUtil.evaluateFilterExpressions(objectFilter, variables, MiscSchemaUtil.getExpressionProfile(),
pageBase.getExpressionFactory(), pageBase.getPrismContext(), "collection filter",
pageBase.createSimpleTask(result.getOperation()), result);
return ExpressionUtil.evaluateFilterExpressions(
objectFilter, variables,
MiscSchemaUtil.getExpressionProfile(),
pageBase.getExpressionFactory(),
"collection filter", pageBase.createSimpleTask(result.getOperation()), result);
} catch (SchemaException | ObjectNotFoundException | ExpressionEvaluationException | CommunicationException |
ConfigurationException | SecurityViolationException ex) {
result.recordPartialError("Unable to evaluate filter exception, ", ex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,12 @@ protected void processVariables(VariablesMap variablesMap, T rowValue) {
}

protected <V> Collection<V> evaluate(VariablesMap variablesMap, ExpressionType expression, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, CommunicationException, SecurityViolationException, ConfigurationException, ObjectNotFoundException {
return (Collection<V>) ExpressionUtil.evaluateStringExpression(variablesMap, getPageBase().getPrismContext(), expression,
MiscSchemaUtil.getExpressionProfile(), getPageBase().getExpressionFactory(), "evaluate column expression",
task, result);
//noinspection unchecked
return (Collection<V>) ExpressionUtil.evaluateStringExpression(
variablesMap, expression,
MiscSchemaUtil.getExpressionProfile(),
getPageBase().getExpressionFactory(),
"evaluate column expression", task, result);
}

// todo figure out how to improve this, looks horrible
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -753,8 +753,11 @@ private ObjectFilter createAutocompleteFilter(String text) {
VariablesMap variables = new VariablesMap();
variables.addVariableDefinition(ExpressionConstants.VAR_INPUT, text, def);

return ExpressionUtil.evaluateFilterExpressions(filter, variables, MiscSchemaUtil.getExpressionProfile(),
panel.page.getExpressionFactory(), ctx, "group selection search filter template", task, result);
return ExpressionUtil.evaluateFilterExpressions(
filter, variables,
MiscSchemaUtil.getExpressionProfile(),
panel.page.getExpressionFactory(),
"group selection search filter template", task, result);
} catch (Exception ex) {
result.recordFatalError(ex);
LoggingUtils.logUnexpectedException(LOGGER,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,11 @@ protected void processVariables(VariablesMap variablesMap, C rowValue) {
protected Collection<String> evaluate(VariablesMap variablesMap, ExpressionType expression, Task task, OperationResult result)
throws SchemaException, ExpressionEvaluationException, CommunicationException, SecurityViolationException, ConfigurationException, ObjectNotFoundException {

return ExpressionUtil.evaluateStringExpression(variablesMap, getPageBase().getPrismContext(), expression,
MiscSchemaUtil.getExpressionProfile(), getPageBase().getExpressionFactory(), "evaluate column expression",
task, task.getResult());
return ExpressionUtil.evaluateStringExpression(
variablesMap, expression,
MiscSchemaUtil.getExpressionProfile(),
getPageBase().getExpressionFactory(),
"evaluate column expression", task, task.getResult());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -577,10 +577,8 @@ private void updateRequestWithMidpointQuery(RepositoryQueryDiagRequest request,
objectType = ObjectType.COMPLEX_TYPE;
}
@SuppressWarnings("unchecked")
Class<? extends ObjectType> clazz = (Class<? extends ObjectType>) prismContext.getSchemaRegistry().getCompileTimeClassForObjectType(objectType);
if (clazz == null) {
throw new SchemaException("Couldn't find compile-time class for object type of " + objectType);
}
Class<? extends ObjectType> clazz = (Class<? extends ObjectType>)
prismContext.getSchemaRegistry().getCompileTimeClassForObjectTypeRequired(objectType);

ObjectQuery queryWithExprEvaluated = null;
if (midPointQueryScript != null) {
Expand All @@ -597,8 +595,11 @@ private void updateRequestWithMidpointQuery(RepositoryQueryDiagRequest request,
if (queryWithExprEvaluated == null) {
QueryType queryType = prismContext.parserFor(queryText).language(dataLanguage).parseRealValue(QueryType.class);
ObjectQuery objectQuery = prismContext.getQueryConverter().createObjectQuery(clazz, queryType);
queryWithExprEvaluated = ExpressionUtil.evaluateQueryExpressions(objectQuery, new VariablesMap(),
MiscSchemaUtil.getExpressionProfile(), getExpressionFactory(), getPrismContext(), "evaluate query expressions", task, result);
queryWithExprEvaluated = ExpressionUtil.evaluateQueryExpressions(
objectQuery, new VariablesMap(),
MiscSchemaUtil.getExpressionProfile(),
getExpressionFactory(),
"evaluate query expressions", task, result);
}

request.setType(clazz);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ protected void onInitialize() {

@Override
protected Class<C> getDefaultType() {
return view == null ? (Class<C>) ObjectType.class : view.getTargetClass(getPrismContext());
//noinspection unchecked
return view == null ? (Class<C>) ObjectType.class : view.getTargetClass();
}

private void initView() {
Expand Down Expand Up @@ -144,7 +145,8 @@ protected List<C> searchObjects(Class<C> type, ObjectQuery query, Collection<Sel
variables.putAll(getSearchModel().getObject().getFilterVariables(getVariables(), getPageBase()));
processReferenceVariables(variables);
}
Collection<SelectorOptions<GetOperationOptions>> defaultOptions = DefaultColumnUtils.createOption(getObjectCollectionView().getTargetClass(getPrismContext()), getSchemaService());
Collection<SelectorOptions<GetOperationOptions>> defaultOptions =
DefaultColumnUtils.createOption(getObjectCollectionView().getTargetClass(), getSchemaService());

List<C> list = (List<C>) getModelInteractionService().searchObjectsFromCollection(getReport().getObjectCollection().getCollection(), qNameType, defaultOptions, query.getPaging(), variables, task, result);
if (LOGGER.isTraceEnabled()) {
Expand All @@ -161,7 +163,8 @@ protected boolean match(C selectedValue, C foundValue) {
@Override
protected Integer countObjects(Class<C> type, ObjectQuery query, Collection<SelectorOptions<GetOperationOptions>> currentOptions, Task task, OperationResult result)
throws CommunicationException, ObjectNotFoundException, SchemaException, SecurityViolationException, ConfigurationException, ExpressionEvaluationException {
Collection<SelectorOptions<GetOperationOptions>> defaultOptions = DefaultColumnUtils.createOption(getObjectCollectionView().getTargetClass(getPrismContext()), getSchemaService());
Collection<SelectorOptions<GetOperationOptions>> defaultOptions =
DefaultColumnUtils.createOption(getObjectCollectionView().getTargetClass(), getSchemaService());
QName qNameType = WebComponentUtil.anyClassToQName(getPrismContext(), type);
VariablesMap variables = new VariablesMap();
if (getSearchModel().getObject() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,10 @@
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractRoleType;

public class VirtualAssignmentSpecification<R extends AbstractRoleType> {
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

private ObjectFilter filter;
private Class<R> type;

public ObjectFilter getFilter() {
return filter;
}

public Class<R> getType() {
return type;
}

public void setFilter(ObjectFilter filter) {
this.filter = filter;
}

public void setType(Class<R> type) {
this.type = type;
}
public record VirtualAssignmentSpecification<R extends AbstractRoleType>(
@NotNull Class<R> type,
@Nullable ObjectFilter filter) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* Copyright (C) 2010-2023 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.schema.config;

import java.io.Serial;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;

import com.evolveum.midpoint.util.annotation.Experimental;
import com.evolveum.midpoint.util.exception.ConfigurationException;
import com.evolveum.midpoint.util.exception.SystemException;

import com.google.common.base.Strings;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.prism.Containerable;
import com.evolveum.midpoint.prism.path.ItemPath;

/**
* Helper class that provides complex information about a configuration item (e.g., a mapping).
* Currently, the most prominent information is the origin of the item value.
*/
@Experimental
public class ConfigurationItem<T extends Serializable> implements Serializable {

@Serial private static final long serialVersionUID = 0L;

private final @NotNull T value;
private final @NotNull ConfigurationItemOrigin origin;

/** For internal use. */
protected ConfigurationItem(
@NotNull ConfigurationItem<T> original) {
this.value = original.value;
this.origin = original.origin;
}

public ConfigurationItem(
@NotNull T value,
@NotNull ConfigurationItemOrigin origin) {
this.value = value;
this.origin = origin;
}

public static @NotNull <T extends Serializable> ConfigurationItem<T> of(
@NotNull T item, @NotNull ConfigurationItemOrigin origin) {
return new ConfigurationItem<>(item, origin);
}

public static @NotNull <T extends Serializable> List<ConfigurationItem<T>> ofListEmbedded(@NotNull List<T> items) {
return ofList(items, OriginProvider.embedded());
}

public static @NotNull <T extends Serializable> List<ConfigurationItem<T>> ofList(
@NotNull List<T> items, @NotNull OriginProvider<? super T> originProvider) {
return items.stream()
.map(i -> of(i, originProvider.origin(i)))
.toList();
}

public @NotNull T value() {
return value;
}

public @NotNull ConfigurationItemOrigin origin() {
return origin;
}

public @NotNull ConfigurationItemOrigin originFor(@NotNull ItemPath path) {
return origin.child(path);
}

public <C extends Containerable> @NotNull OriginProvider<C> originProviderFor(@NotNull ItemPath path) {
return item -> originFor(path);
}

public <X extends ConfigurationItem<T>> @NotNull X as(@NotNull Class<X> clazz) {
try {
var constructor = clazz.getDeclaredConstructor(ConfigurationItem.class);
return constructor.newInstance(this);
} catch (Throwable t) {
throw SystemException.unexpected(t, "when converting " + this + " to " + clazz);
}
}

@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || obj.getClass() != this.getClass()) {
return false;
}
var that = (ConfigurationItem<?>) obj;
return Objects.equals(this.value, that.value)
&& Objects.equals(this.origin, that.origin);
}

@Override
public int hashCode() {
return Objects.hash(value, origin);
}

@Override
public String toString() {
return "ConfigurationItem[" +
"value=" + value + ", " +
"origin=" + origin + ']';
}

/** To be overridden in specific subclasses. */
public @NotNull String localDescription() {
return value.getClass().getSimpleName();
}

public @NotNull String fullDescription() {
return localDescription() + " in " + origin.fullDescription();
}

/** The last "%s" is filled with full description */
public void configCheck(boolean value, String template, Object... arguments) throws ConfigurationException {
if (!value) {
Object[] augmentedArguments = new Object[arguments.length + 1];
System.arraycopy(arguments, 0, augmentedArguments, 0, arguments.length);
augmentedArguments[arguments.length] = fullDescription();

throw new ConfigurationException(
Strings.lenientFormat(template, augmentedArguments));
}
}

@Contract("null, _, _ -> fail")
public <V> @NotNull V configNonNull(V value, String template, Object... arguments) throws ConfigurationException {
configCheck(value != null, template, arguments);
assert value != null;
return value;
}
}

0 comments on commit 10c8252

Please sign in to comment.