/* * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.evolveum.midpoint.model.common.expression; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.function.Function; import javax.xml.namespace.QName; import com.evolveum.midpoint.model.common.expression.functions.BasicExpressionFunctions; import com.evolveum.midpoint.model.common.expression.functions.BasicExpressionFunctionsXPath; import com.evolveum.midpoint.model.common.expression.functions.FunctionLibrary; import com.evolveum.midpoint.model.common.expression.functions.LogExpressionFunctions; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.Visitor; import com.evolveum.midpoint.prism.crypto.EncryptionException; import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.PlusMinusZero; import com.evolveum.midpoint.prism.delta.PrismValueDeltaSetTriple; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.path.ItemPathSegment; import com.evolveum.midpoint.prism.path.NameItemPathSegment; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.query.*; import com.evolveum.midpoint.prism.util.JavaTypeConverter; import com.evolveum.midpoint.prism.util.PrismUtil; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.prism.xml.XsdTypeMapper; import com.evolveum.midpoint.schema.constants.ExpressionConstants; import com.evolveum.midpoint.schema.constants.MidPointConstants; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ObjectResolver; import com.evolveum.midpoint.security.api.MidPointPrincipal; import com.evolveum.midpoint.security.api.SecurityEnforcer; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.DOMUtil; import com.evolveum.midpoint.util.Holder; import com.evolveum.midpoint.util.PrettyPrinter; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SecurityViolationException; import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ExpressionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingType; 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.midpoint.xml.ns._public.common.common_3.QueryInterpretationOfNoValueType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; import org.jetbrains.annotations.Nullable; /** * @author semancik * */ public class ExpressionUtil { private static final Trace LOGGER = TraceManager.getTrace(ExpressionUtil.class); private static final QName CONDITION_OUTPUT_NAME = new QName(SchemaConstants.NS_C, "condition"); public static <V extends PrismValue> PrismValueDeltaSetTriple<V> toOutputTriple( PrismValueDeltaSetTriple<V> resultTriple, ItemDefinition outputDefinition, Function<Object, Object> additionalConvertor, final ItemPath residualPath, final Protector protector, final PrismContext prismContext) { PrismValueDeltaSetTriple<V> clonedTriple = resultTriple.clone(); final Class<?> resultTripleValueClass = resultTriple.getRealValueClass(); if (resultTripleValueClass == null) { // triple is empty. type does not matter. return clonedTriple; } Class<?> expectedJavaType = XsdTypeMapper.toJavaType(outputDefinition.getTypeName()); if (expectedJavaType == null) { expectedJavaType = prismContext.getSchemaRegistry() .getCompileTimeClass(outputDefinition.getTypeName()); } if (resultTripleValueClass == expectedJavaType) { return clonedTriple; } final Class<?> finalExpectedJavaType = expectedJavaType; clonedTriple.accept((Visitor) visitable -> { if (visitable instanceof PrismPropertyValue<?>) { PrismPropertyValue<Object> pval = (PrismPropertyValue<Object>) visitable; Object realVal = pval.getValue(); if (realVal != null) { if (Structured.class.isAssignableFrom(resultTripleValueClass)) { if (residualPath != null && !residualPath.isEmpty()) { realVal = ((Structured) realVal).resolve(residualPath); } } if (finalExpectedJavaType != null) { Object convertedVal = convertValue(finalExpectedJavaType, additionalConvertor, realVal, protector, prismContext); pval.setValue(convertedVal); } } } }); return clonedTriple; } /** * Slightly more powerful version of "convert" as compared to * JavaTypeConverter. This version can also encrypt/decrypt and also handles * polystrings. */ public static <I, O> O convertValue(Class<O> finalExpectedJavaType, Function<Object, Object> additionalConvertor, I inputVal, Protector protector, PrismContext prismContext) { if (inputVal == null) { return null; } if (finalExpectedJavaType.isInstance(inputVal)) { return (O) inputVal; } Object intermediateVal; if (finalExpectedJavaType == ProtectedStringType.class) { String valueToEncrypt; if (inputVal instanceof String) { valueToEncrypt = (String) inputVal; } else { valueToEncrypt = JavaTypeConverter.convert(String.class, inputVal); } try { intermediateVal = protector.encryptString(valueToEncrypt); } catch (EncryptionException e) { throw new SystemException(e.getMessage(), e); } } else if (inputVal instanceof ProtectedStringType) { try { intermediateVal = protector.decryptString((ProtectedStringType) inputVal); } catch (EncryptionException e) { throw new SystemException(e.getMessage(), e); } } else { intermediateVal = inputVal; } if (additionalConvertor != null) { intermediateVal = additionalConvertor.apply(intermediateVal); } O convertedVal = JavaTypeConverter.convert(finalExpectedJavaType, intermediateVal); PrismUtil.recomputeRealValue(convertedVal, prismContext); return convertedVal; } public static Object resolvePath(ItemPath path, ExpressionVariables variables, Object defaultContext, ObjectResolver objectResolver, String shortDesc, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { Object root = defaultContext; ItemPath relativePath = path; ItemPathSegment first = path.first(); String varDesc = "default context"; if (first.isVariable()) { QName varName = ((NameItemPathSegment) first).getName(); varDesc = "variable " + PrettyPrinter.prettyPrint(varName); relativePath = path.rest(); if (variables.containsKey(varName)) { root = variables.get(varName); } else { throw new SchemaException("No variable with name " + varName + " in " + shortDesc); } } if (root == null) { return null; } if (relativePath.isEmpty()) { return root; } if (root instanceof ObjectReferenceType) { root = resolveReference((ObjectReferenceType) root, objectResolver, varDesc, shortDesc, task, result); } if (root instanceof Objectable) { return (((Objectable) root).asPrismObject()).find(relativePath); } if (root instanceof PrismObject<?>) { return ((PrismObject<?>) root).find(relativePath); } else if (root instanceof PrismContainer<?>) { return ((PrismContainer<?>) root).find(relativePath); } else if (root instanceof PrismContainerValue<?>) { return ((PrismContainerValue<?>) root).find(relativePath); } else if (root instanceof Item<?, ?>) { // Except for container (which is handled above) throw new SchemaException( "Cannot apply path " + relativePath + " to " + root + " in " + shortDesc); } else if (root instanceof ObjectDeltaObject<?>) { return ((ObjectDeltaObject<?>) root).findIdi(relativePath); } else if (root instanceof ItemDeltaItem<?, ?>) { return ((ItemDeltaItem<?, ?>) root).findIdi(relativePath); } else { throw new IllegalArgumentException( "Unexpected root " + root + " (relative path:" + relativePath + ") in " + shortDesc); } } public static Object convertVariableValue(Object originalValue, String variableName, ObjectResolver objectResolver, String contextDescription, PrismContext prismContext, Task task, OperationResult result) throws ExpressionSyntaxException, ObjectNotFoundException { if (originalValue instanceof PrismValue) { ((PrismValue) originalValue).setPrismContext(prismContext); // TODO - or revive? Or make sure prismContext is set here? } else if (originalValue instanceof Item) { ((Item) originalValue).setPrismContext(prismContext); // TODO - or revive? Or make sure prismContext is set here? } if (originalValue instanceof ObjectReferenceType) { try { originalValue = resolveReference((ObjectReferenceType)originalValue, objectResolver, variableName, contextDescription, task, result); } catch (SchemaException e) { throw new ExpressionSyntaxException("Schema error during variable "+variableName+" resolution in "+contextDescription+": "+e.getMessage(), e); } } if (originalValue instanceof PrismObject<?>) { return ((PrismObject<?>)originalValue).asObjectable(); } if (originalValue instanceof PrismContainerValue<?>) { return ((PrismContainerValue<?>)originalValue).asContainerable(); } if (originalValue instanceof PrismPropertyValue<?>) { return ((PrismPropertyValue<?>)originalValue).getValue(); } if (originalValue instanceof PrismReferenceValue) { if (((PrismReferenceValue) originalValue).getDefinition() != null) { return ((PrismReferenceValue) originalValue).asReferencable(); } } if (originalValue instanceof PrismProperty<?>) { PrismProperty<?> prop = (PrismProperty<?>)originalValue; PrismPropertyDefinition<?> def = prop.getDefinition(); if (def != null) { if (def.isSingleValue()) { return prop.getRealValue(); } else { return prop.getRealValues(); } } else { return prop.getValues(); } } if (originalValue instanceof PrismReference) { PrismReference prop = (PrismReference)originalValue; PrismReferenceDefinition def = prop.getDefinition(); if (def != null) { if (def.isSingleValue()) { return prop.getRealValue(); } else { return prop.getRealValues(); } } else { return prop.getValues(); } } if (originalValue instanceof PrismContainer<?>) { PrismContainer<?> container = (PrismContainer<?>)originalValue; PrismContainerDefinition<?> def = container.getDefinition(); if (def != null) { if (def.isSingleValue()) { return container.getRealValue(); } else { return container.getRealValues(); } } else { return container.getValues(); } } return originalValue; } public static Map<String,Object> prepareScriptVariables(ExpressionVariables variables, ObjectResolver objectResolver, Collection<FunctionLibrary> functions, String contextDescription, PrismContext prismContext, Task task, OperationResult result) throws ExpressionSyntaxException, ObjectNotFoundException { Map<String,Object> scriptVariables = new HashMap<>(); // Functions if (functions != null) { for (FunctionLibrary funcLib: functions) { scriptVariables.put(funcLib.getVariableName(), funcLib.getGenericFunctions()); } } // Variables if (variables != null) { for (Entry<QName, Object> variableEntry: variables.entrySet()) { if (variableEntry.getKey() == null) { // This is the "root" node. We have no use for it in JSR223, just skip it continue; } String variableName = variableEntry.getKey().getLocalPart(); Object variableValue = ExpressionUtil.convertVariableValue(variableEntry.getValue(), variableName, objectResolver, contextDescription, prismContext, task, result); scriptVariables.put(variableName, variableValue); } } return scriptVariables; } private static PrismObject<?> resolveReference(ObjectReferenceType ref, ObjectResolver objectResolver, String varDesc, String contextDescription, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { if (ref.getOid() == null) { throw new SchemaException( "Null OID in reference in variable " + varDesc + " in " + contextDescription); } else { try { ObjectType objectType = objectResolver.resolve(ref, ObjectType.class, null, contextDescription, task, result); if (objectType == null) { throw new IllegalArgumentException( "Resolve returned null for " + ref + " in " + contextDescription); } return objectType.asPrismObject(); } catch (ObjectNotFoundException e) { throw new ObjectNotFoundException("Object not found during variable " + varDesc + " resolution in " + contextDescription + ": " + e.getMessage(), e, ref.getOid()); } catch (SchemaException e) { throw new SchemaException("Schema error during variable " + varDesc + " resolution in " + contextDescription + ": " + e.getMessage(), e); } } } public static <ID extends ItemDefinition> ID resolveDefinitionPath(ItemPath path, ExpressionVariables variables, PrismContainerDefinition<?> defaultContext, String shortDesc) throws SchemaException { while (path != null && !path.isEmpty() && !(path.first() instanceof NameItemPathSegment)) { path = path.rest(); } Object root = defaultContext; ItemPath relativePath = path; NameItemPathSegment first = (NameItemPathSegment) path.first(); if (first.isVariable()) { relativePath = path.rest(); QName varName = first.getName(); if (variables.containsKey(varName)) { Object varValue = variables.get(varName); if (varValue instanceof ItemDeltaItem<?, ?>) { root = ((ItemDeltaItem<?, ?>) varValue).getDefinition(); } else if (varValue instanceof Item<?, ?>) { root = ((Item<?, ?>) varValue).getDefinition(); } else if (varValue instanceof Objectable) { root = ((Objectable) varValue).asPrismObject().getDefinition(); } else if (varValue instanceof ItemDefinition) { root = varValue; } else { throw new IllegalStateException("Unexpected content of variable " + varName + ": " + varValue + " (" + varValue.getClass() + ")"); } if (root == null) { throw new IllegalStateException( "Null definition in content of variable " + varName + ": " + varValue); } } else { throw new SchemaException("No variable with name " + varName + " in " + shortDesc); } } if (root == null) { return null; } if (relativePath.isEmpty()) { return (ID) root; } ItemDefinition result = null; if (root instanceof PrismObjectDefinition<?>) { return ((PrismObjectDefinition<?>) root).findItemDefinition(relativePath); } else if (root instanceof PrismContainerDefinition<?>) { return ((PrismContainerDefinition<?>) root).findItemDefinition(relativePath); } else if (root instanceof ItemDefinition) { // Except for container (which is handled above) throw new SchemaException( "Cannot apply path " + relativePath + " to " + root + " in " + shortDesc); } else { throw new IllegalArgumentException("Unexpected root " + root + " in " + shortDesc); } } public static <IV extends PrismValue, ID extends ItemDefinition> ItemDeltaItem<IV, ID> toItemDeltaItem( Object object, ObjectResolver objectResolver, String string, OperationResult result) { if (object == null) { return null; } if (object instanceof ItemDeltaItem<?, ?>) { return (ItemDeltaItem<IV, ID>) object; } if (object instanceof PrismObject<?>) { return (ItemDeltaItem<IV, ID>) new ObjectDeltaObject((PrismObject<?>) object, null, (PrismObject<?>) object); } else if (object instanceof Item<?, ?>) { return new ItemDeltaItem<IV, ID>((Item<IV, ID>) object, null, (Item<IV, ID>) object); } else if (object instanceof ItemDelta<?, ?>) { return new ItemDeltaItem<IV, ID>(null, (ItemDelta<IV, ID>) object, null); } else { throw new IllegalArgumentException("Unexpected object " + object + " " + object.getClass()); } } public static FunctionLibrary createBasicFunctionLibrary(PrismContext prismContext, Protector protector) { FunctionLibrary lib = new FunctionLibrary(); lib.setVariableName(MidPointConstants.FUNCTION_LIBRARY_BASIC_VARIABLE_NAME); lib.setNamespace(MidPointConstants.NS_FUNC_BASIC); BasicExpressionFunctions func = new BasicExpressionFunctions(prismContext, protector); lib.setGenericFunctions(func); BasicExpressionFunctionsXPath funcXPath = new BasicExpressionFunctionsXPath(func); lib.setXmlFunctions(funcXPath); return lib; } public static FunctionLibrary createLogFunctionLibrary(PrismContext prismContext) { FunctionLibrary lib = new FunctionLibrary(); lib.setVariableName(MidPointConstants.FUNCTION_LIBRARY_LOG_VARIABLE_NAME); lib.setNamespace(MidPointConstants.NS_FUNC_LOG); LogExpressionFunctions func = new LogExpressionFunctions(prismContext); lib.setGenericFunctions(func); return lib; } public static ObjectQuery evaluateQueryExpressions(ObjectQuery origQuery, ExpressionVariables variables, ExpressionFactory expressionFactory, PrismContext prismContext, String shortDesc, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { if (origQuery == null) { return null; } ObjectQuery query = origQuery.clone(); ObjectFilter evaluatedFilter = evaluateFilterExpressionsInternal(query.getFilter(), variables, expressionFactory, prismContext, shortDesc, task, result); query.setFilter(evaluatedFilter); return query; } public static ObjectFilter evaluateFilterExpressions(ObjectFilter origFilter, ExpressionVariables variables, ExpressionFactory expressionFactory, PrismContext prismContext, String shortDesc, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { if (origFilter == null) { return null; } return evaluateFilterExpressionsInternal(origFilter, variables, expressionFactory, prismContext, shortDesc, task, result); } public static boolean hasExpressions(@Nullable ObjectFilter filter) { if (filter == null) { return false; } Holder<Boolean> result = new Holder<>(false); filter.accept(f -> { if (f instanceof ValueFilter) { ValueFilter<?, ?> vf = (ValueFilter<?, ?>) f; if (vf.getExpression() != null) { result.setValue(true); } } }); return result.getValue(); } private static ObjectFilter evaluateFilterExpressionsInternal(ObjectFilter filter, ExpressionVariables variables, ExpressionFactory expressionFactory, PrismContext prismContext, String shortDesc, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { if (filter == null) { return null; } if (filter instanceof InOidFilter) { ExpressionWrapper expressionWrapper = ((InOidFilter) filter).getExpression(); if (expressionWrapper == null || expressionWrapper.getExpression() == null) { LOGGER.warn("No valueExpression in filter in {}. Returning original filter", shortDesc); InOidFilter inOidFilter = (InOidFilter) filter; if (inOidFilter.getOids() != null && !inOidFilter.getOids().isEmpty()){ return filter.clone(); } return NoneFilter.createNone(); } ExpressionType valueExpression = getExpression(expressionWrapper, shortDesc); try { Collection<String> expressionResult = evaluateStringExpression(variables, prismContext, valueExpression, expressionFactory, shortDesc, task, result); if (expressionResult == null || expressionResult.isEmpty()) { LOGGER.debug("Result of search filter expression was null or empty. Expression: {}", valueExpression); return createFilterForNoValue(filter, valueExpression); } // TODO: log more context LOGGER.trace("Search filter expression in the rule for {} evaluated to {}.", new Object[] { shortDesc, expressionResult }); InOidFilter evaluatedFilter = (InOidFilter) filter.clone(); evaluatedFilter.setOids(expressionResult); evaluatedFilter.setExpression(null); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Transformed filter to:\n{}", evaluatedFilter.debugDump()); } return evaluatedFilter; } catch (Exception ex) { throw new ExpressionEvaluationException(ex); } } else if (filter instanceof FullTextFilter) { ExpressionWrapper expressionWrapper = ((FullTextFilter) filter).getExpression(); if (expressionMissing(expressionWrapper, shortDesc)) { return filter.clone(); } ExpressionType valueExpression = getExpression(expressionWrapper, shortDesc); try { Collection<String> expressionResult = evaluateStringExpression(variables, prismContext, valueExpression, expressionFactory, shortDesc, task, result); if (expressionResult == null || expressionResult.isEmpty()) { LOGGER.debug("Result of search filter expression was null or empty. Expression: {}", valueExpression); return createFilterForNoValue(filter, valueExpression); } // TODO: log more context LOGGER.trace("Search filter expression in the rule for {} evaluated to {}.", shortDesc, expressionResult); FullTextFilter evaluatedFilter = (FullTextFilter) filter.clone(); evaluatedFilter.setValues(expressionResult); evaluatedFilter.setExpression(null); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Transformed filter to:\n{}", evaluatedFilter.debugDump()); } return evaluatedFilter; } catch (Exception ex) { throw new ExpressionEvaluationException(ex); } } else if (filter instanceof LogicalFilter) { List<ObjectFilter> conditions = ((LogicalFilter) filter).getConditions(); LogicalFilter evaluatedFilter = ((LogicalFilter) filter).cloneEmpty(); for (ObjectFilter condition : conditions) { ObjectFilter evaluatedSubFilter = evaluateFilterExpressionsInternal(condition, variables, expressionFactory, prismContext, shortDesc, task, result); evaluatedFilter.addCondition(evaluatedSubFilter); } return evaluatedFilter; } else if (filter instanceof ValueFilter) { ValueFilter valueFilter = (ValueFilter) filter; if (valueFilter.getValues() != null && !valueFilter.getValues().isEmpty()) { // We have value. Nothing to evaluate. return valueFilter.clone(); } ExpressionWrapper expressionWrapper = valueFilter.getExpression(); if (expressionMissing(expressionWrapper, shortDesc)) { return valueFilter.clone(); } ExpressionType valueExpression = getExpression(expressionWrapper, shortDesc); try { PrismValue expressionResult = evaluateExpression(variables, prismContext, valueExpression, filter, expressionFactory, shortDesc, task, result); if (expressionResult == null || expressionResult.isEmpty()) { LOGGER.debug("Result of search filter expression was null or empty. Expression: {}", valueExpression); return createFilterForNoValue(valueFilter, valueExpression); } // TODO: log more context LOGGER.trace("Search filter expression in the rule for {} evaluated to {}.", new Object[] { shortDesc, expressionResult }); ValueFilter evaluatedFilter = valueFilter.clone(); evaluatedFilter.setValue(expressionResult); evaluatedFilter.setExpression(null); // } if (LOGGER.isTraceEnabled()) { LOGGER.trace("Transformed filter to:\n{}", evaluatedFilter.debugDump()); } return evaluatedFilter; } catch (RuntimeException ex) { LoggingUtils.logException(LOGGER, "Couldn't evaluate expression " + valueExpression + ".", ex); throw new SystemException( "Couldn't evaluate expression" + valueExpression + ": " + ex.getMessage(), ex); } catch (SchemaException ex) { LoggingUtils.logException(LOGGER, "Couldn't evaluate expression " + valueExpression + ".", ex); throw new SchemaException( "Couldn't evaluate expression" + valueExpression + ": " + ex.getMessage(), ex); } catch (ObjectNotFoundException ex) { LoggingUtils.logException(LOGGER, "Couldn't evaluate expression " + valueExpression + ".", ex); throw new ObjectNotFoundException( "Couldn't evaluate expression" + valueExpression + ": " + ex.getMessage(), ex); } catch (ExpressionEvaluationException ex) { LoggingUtils.logException(LOGGER, "Couldn't evaluate expression " + valueExpression + ".", ex); throw new ExpressionEvaluationException( "Couldn't evaluate expression" + valueExpression + ": " + ex.getMessage(), ex); } } else if (filter instanceof ExistsFilter) { ExistsFilter evaluatedFilter = ((ExistsFilter) filter).cloneEmpty(); ObjectFilter evaluatedSubFilter = evaluateFilterExpressionsInternal(((ExistsFilter) filter).getFilter(), variables, expressionFactory, prismContext, shortDesc, task, result); evaluatedFilter.setFilter(evaluatedSubFilter); return evaluatedFilter; } else if (filter instanceof TypeFilter) { TypeFilter evaluatedFilter = ((TypeFilter) filter).cloneEmpty(); ObjectFilter evaluatedSubFilter = evaluateFilterExpressionsInternal(((TypeFilter) filter).getFilter(), variables, expressionFactory, prismContext, shortDesc, task, result); evaluatedFilter.setFilter(evaluatedSubFilter); return evaluatedFilter; } else if (filter instanceof OrgFilter) { return filter; } else if (filter instanceof AllFilter || filter instanceof NoneFilter || filter instanceof UndefinedFilter) { return filter; } else { throw new IllegalStateException("Unsupported filter type: " + filter.getClass()); } } private static boolean expressionMissing(ExpressionWrapper expressionWrapper, String shortDesc) { if (expressionWrapper == null || expressionWrapper.getExpression() == null) { LOGGER.warn("No valueExpression in filter in {}. Returning original filter", shortDesc); return true; } return false; } private static ExpressionType getExpression(ExpressionWrapper expressionWrapper, String shortDesc) throws SchemaException { if (!(expressionWrapper.getExpression() instanceof ExpressionType)) { throw new SchemaException("Unexpected expression type " + expressionWrapper.getExpression().getClass() + " in filter in " + shortDesc); } return (ExpressionType) expressionWrapper.getExpression(); } private static ObjectFilter createFilterForNoValue(ObjectFilter filter, ExpressionType valueExpression) throws ExpressionEvaluationException { QueryInterpretationOfNoValueType queryInterpretationOfNoValue = valueExpression.getQueryInterpretationOfNoValue(); if (queryInterpretationOfNoValue == null) { queryInterpretationOfNoValue = QueryInterpretationOfNoValueType.FILTER_EQUAL_NULL; } switch (queryInterpretationOfNoValue) { case FILTER_UNDEFINED: return UndefinedFilter.createUndefined(); case FILTER_NONE: return NoneFilter.createNone(); case FILTER_ALL: return AllFilter.createAll(); case FILTER_EQUAL_NULL: if (filter instanceof ValueFilter) { ValueFilter evaluatedFilter = (ValueFilter) filter.clone(); evaluatedFilter.setExpression(null); return evaluatedFilter; } else if (filter instanceof InOidFilter) { return NoneFilter.createNone(); } else if (filter instanceof FullTextFilter) { return NoneFilter.createNone(); // because full text search for 'no value' is meaningless } else { throw new IllegalArgumentException("Unknown filter to evaluate: " + filter); } case ERROR: throw new ExpressionEvaluationException("Expression "+valueExpression+" evaluated to no value"); default: throw new IllegalArgumentException("Unknown value "+queryInterpretationOfNoValue+" in queryInterpretationOfNoValue in "+valueExpression); } } private static <V extends PrismValue> V evaluateExpression(ExpressionVariables variables, PrismContext prismContext, ExpressionType expressionType, ObjectFilter filter, ExpressionFactory expressionFactory, String shortDesc, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { // TODO refactor after new query engine is implemented ItemDefinition outputDefinition = null; if (filter instanceof ValueFilter) { outputDefinition = ((ValueFilter) filter).getDefinition(); } if (outputDefinition == null) { outputDefinition = new PrismPropertyDefinitionImpl(ExpressionConstants.OUTPUT_ELEMENT_NAME, DOMUtil.XSD_STRING, prismContext); } return (V) evaluateExpression(variables, outputDefinition, expressionType, expressionFactory, shortDesc, task, parentResult); // String expressionResult = // expressionHandler.evaluateExpression(currentShadow, valueExpression, // shortDesc, result); } public static <V extends PrismValue, D extends ItemDefinition> V evaluateExpression( ExpressionVariables variables, D outputDefinition, ExpressionType expressionType, ExpressionFactory expressionFactory, String shortDesc, Task task, OperationResult parentResult) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException { Expression<V, D> expression = expressionFactory.makeExpression(expressionType, outputDefinition, shortDesc, task, parentResult); ExpressionEvaluationContext context = new ExpressionEvaluationContext(null, variables, shortDesc, task, parentResult); PrismValueDeltaSetTriple<V> outputTriple = expression.evaluate(context); LOGGER.trace("Result of the expression evaluation: {}", outputTriple); if (outputTriple == null) { return null; } Collection<V> nonNegativeValues = outputTriple.getNonNegativeValues(); if (nonNegativeValues == null || nonNegativeValues.isEmpty()) { return null; } if (nonNegativeValues.size() > 1) { throw new ExpressionEvaluationException("Expression returned more than one value (" + nonNegativeValues.size() + ") in " + shortDesc); } return nonNegativeValues.iterator().next(); } private static Collection<String> evaluateStringExpression(ExpressionVariables variables, PrismContext prismContext, ExpressionType expressionType, ExpressionFactory expressionFactory, String shortDesc, Task task, OperationResult parentResult) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException { PrismPropertyDefinitionImpl<String> outputDefinition = new PrismPropertyDefinitionImpl( ExpressionConstants.OUTPUT_ELEMENT_NAME, DOMUtil.XSD_STRING, prismContext); outputDefinition.setMaxOccurs(-1); Expression<PrismPropertyValue<String>, PrismPropertyDefinition<String>> expression = expressionFactory .makeExpression(expressionType, outputDefinition, shortDesc, task, parentResult); ExpressionEvaluationContext context = new ExpressionEvaluationContext(null, variables, shortDesc, task, parentResult); PrismValueDeltaSetTriple<PrismPropertyValue<String>> outputTriple = expression.evaluate(context); LOGGER.trace("Result of the expression evaluation: {}", outputTriple); if (outputTriple == null) { return null; } Collection<PrismPropertyValue<String>> nonNegativeValues = outputTriple.getNonNegativeValues(); if (nonNegativeValues == null || nonNegativeValues.isEmpty()) { return null; } return PrismValue.getRealValuesOfCollection((Collection) nonNegativeValues); // return nonNegativeValues.iterator().next(); } public static PrismPropertyValue<Boolean> evaluateCondition(ExpressionVariables variables, ExpressionType expressionType, ExpressionFactory expressionFactory, String shortDesc, Task task, OperationResult parentResult) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException { ItemDefinition outputDefinition = new PrismPropertyDefinitionImpl( ExpressionConstants.OUTPUT_ELEMENT_NAME, DOMUtil.XSD_BOOLEAN, expressionFactory.getPrismContext()); return (PrismPropertyValue<Boolean>) evaluateExpression(variables, outputDefinition, expressionType, expressionFactory, shortDesc, task, parentResult); } public static boolean getBooleanConditionOutput(PrismPropertyValue<Boolean> conditionOutput) { if (conditionOutput == null) { return false; } Boolean value = conditionOutput.getValue(); if (value == null) { return false; } return value; } public static Map<QName, Object> compileVariablesAndSources(ExpressionEvaluationContext params) { Map<QName, Object> variablesAndSources = new HashMap<QName, Object>(); if (params.getVariables() != null) { for (Entry<QName, Object> entry : params.getVariables().entrySet()) { variablesAndSources.put(entry.getKey(), entry.getValue()); } } if (params.getSources() != null) { for (Source<?, ?> source : params.getSources()) { variablesAndSources.put(source.getName(), source); } } return variablesAndSources; } public static boolean hasExplicitTarget(List<MappingType> mappingTypes) { for (MappingType mappingType : mappingTypes) { if (hasExplicitTarget(mappingType)) { return true; } } return false; } private static boolean hasExplicitTarget(MappingType mappingType) { return mappingType.getTarget() != null; } public static boolean computeConditionResult( Collection<PrismPropertyValue<Boolean>> booleanPropertyValues) { if (booleanPropertyValues == null || booleanPropertyValues.isEmpty()) { // No value means false return false; } boolean hasFalse = false; for (PrismPropertyValue<Boolean> pval : booleanPropertyValues) { Boolean value = pval.getValue(); if (Boolean.TRUE.equals(value)) { return true; } if (Boolean.FALSE.equals(value)) { hasFalse = true; } } if (hasFalse) { return false; } // No value or all values null. Return default. return true; } public static PlusMinusZero computeConditionResultMode(boolean condOld, boolean condNew) { if (condOld && condNew) { return PlusMinusZero.ZERO; } if (!condOld && !condNew) { return null; } if (condOld && !condNew) { return PlusMinusZero.MINUS; } if (!condOld && condNew) { return PlusMinusZero.PLUS; } throw new IllegalStateException("notreached"); } public static void addActorVariable(ExpressionVariables scriptVariables, SecurityEnforcer securityEnforcer) { // There can already be a value, because for mappings, we create the // variable before parsing sources. // For other scripts we do it just before the execution, to catch all // possible places where scripts can be executed. UserType oldActor = (UserType) scriptVariables.get(ExpressionConstants.VAR_ACTOR); if (oldActor != null) { return; } UserType actor = null; try { if (securityEnforcer != null) { if (!securityEnforcer.isAuthenticated()) { // This is most likely evaluation of role // condition before // the authentication is complete. scriptVariables.addVariableDefinition(ExpressionConstants.VAR_ACTOR, null); return; } MidPointPrincipal principal = securityEnforcer.getPrincipal(); if (principal != null) { actor = principal.getUser(); } } if (actor == null) { LOGGER.debug("Couldn't get principal information - the 'actor' variable is set to null"); } } catch (SecurityViolationException e) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't get principal information - the 'actor' variable is set to null", e); } scriptVariables.addVariableDefinition(ExpressionConstants.VAR_ACTOR, actor); } public static <D extends ItemDefinition> Object convertToOutputValue(Long longValue, D outputDefinition, Protector protector) throws ExpressionEvaluationException, SchemaException { if (longValue == null) { return null; } QName outputType = outputDefinition.getTypeName(); if (outputType.equals(DOMUtil.XSD_INT)) { return longValue.intValue(); } else if (outputType.equals(DOMUtil.XSD_LONG)) { return longValue; } else { return convertToOutputValue(longValue.toString(), outputDefinition, protector); } } public static <D extends ItemDefinition> Object convertToOutputValue(String stringValue, D outputDefinition, Protector protector) throws ExpressionEvaluationException, SchemaException { if (stringValue == null) { return null; } QName outputType = outputDefinition.getTypeName(); if (outputType.equals(DOMUtil.XSD_STRING)) { return stringValue; } else if (outputType.equals(ProtectedStringType.COMPLEX_TYPE)) { try { return protector.encryptString(stringValue); } catch (EncryptionException e) { throw new ExpressionEvaluationException("Crypto error: " + e.getMessage(), e); } } else if (XmlTypeConverter.canConvert(outputType)) { Class<?> outputJavaType = XsdTypeMapper.toJavaType(outputType); try { return XmlTypeConverter.toJavaValue(stringValue, outputJavaType, true); } catch (NumberFormatException e) { throw new SchemaException("Cannot convert string '" + stringValue + "' to data type " + outputType + ": invalid number format", e); } catch (IllegalArgumentException e) { throw new SchemaException("Cannot convert string '" + stringValue + "' to data type " + outputType + ": " + e.getMessage(), e); } } else { throw new IllegalArgumentException( "Expression cannot generate values for properties of type " + outputType); } } public static <T> boolean isEmpty(T val) { if (val == null) { return true; } if (val instanceof String && ((String)val).isEmpty()) { return true; } if (val instanceof PolyString && ((PolyString)val).isEmpty()) { return true; } return false; } public static <T, V extends PrismValue> V convertToPrismValue(T value, ItemDefinition definition, String contextDescription, PrismContext prismContext) throws ExpressionEvaluationException { if (definition instanceof PrismReferenceDefinition) { return (V) ((ObjectReferenceType) value).asReferenceValue(); } else if (definition instanceof PrismContainerDefinition) { try { prismContext.adopt((Containerable) value); ((Containerable) value).asPrismContainerValue().applyDefinition(definition); } catch (SchemaException e) { throw new ExpressionEvaluationException(e.getMessage() + " " + contextDescription, e); } return (V) ((Containerable) value).asPrismContainerValue(); } else { return (V) new PrismPropertyValue<>(value); } } public static Expression<PrismPropertyValue<Boolean>,PrismPropertyDefinition<Boolean>> createCondition(ExpressionType conditionExpressionType, ExpressionFactory expressionFactory, String shortDesc, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { PrismPropertyDefinition<Boolean> conditionOutputDef = new PrismPropertyDefinitionImpl<Boolean>(CONDITION_OUTPUT_NAME, DOMUtil.XSD_BOOLEAN, expressionFactory.getPrismContext()); return expressionFactory.makeExpression(conditionExpressionType, conditionOutputDef, shortDesc, task, result); } public static Function<Object, Object> createRefConvertor(QName defaultType) { return (o) -> { if (o == null || o instanceof ObjectReferenceType) { return o; } else if (o instanceof PrismReferenceValue) { ObjectReferenceType rv = new ObjectReferenceType(); rv.setupReferenceValue((PrismReferenceValue) o); return rv; } else if (o instanceof String) { return new ObjectReferenceType().oid((String) o).type(defaultType); } else { //throw new IllegalArgumentException("The value couldn't be converted to an object reference: " + o); return o; // let someone else complain at this } }; } }