/*
* 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.impl.lens;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.function.Supplier;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import com.evolveum.midpoint.common.refinery.RefinedResourceSchemaImpl;
import com.evolveum.midpoint.model.api.context.EvaluatedPolicyRule;
import com.evolveum.midpoint.model.api.context.EvaluatedPolicyRuleTrigger;
import com.evolveum.midpoint.model.common.expression.*;
import com.evolveum.midpoint.model.impl.util.Utils;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.midpoint.prism.polystring.PrismDefaultPolyStringNormalizer;
import com.evolveum.midpoint.schema.SchemaConstantsGenerated;
import com.evolveum.midpoint.schema.util.ObjectResolver;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.schema.util.ResourceTypeUtil;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.exception.*;
import com.evolveum.midpoint.util.logging.LoggingUtils;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CredentialsCapabilityType;
import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.PasswordCapabilityType;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.mutable.MutableBoolean;
import com.evolveum.midpoint.common.ActivationComputer;
import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition;
import com.evolveum.midpoint.common.refinery.RefinedResourceSchema;
import com.evolveum.midpoint.model.common.mapping.Mapping;
import com.evolveum.midpoint.model.common.mapping.PrismValueDeltaSetTripleProducer;
import com.evolveum.midpoint.model.impl.expr.ModelExpressionThreadLocalHolder;
import com.evolveum.midpoint.model.impl.lens.projector.ValueMatcher;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.delta.DeltaSetTriple;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.delta.PrismValueDeltaSetTriple;
import com.evolveum.midpoint.prism.delta.PropertyDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.provisioning.api.ProvisioningService;
import com.evolveum.midpoint.schema.CapabilityUtil;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.ResourceShadowDiscriminator;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.constants.ExpressionConstants;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ShadowUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.MiscUtil;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author semancik
*
*/
public class LensUtil {
private static final Trace LOGGER = TraceManager.getTrace(LensUtil.class);
public static <F extends ObjectType> void traceContext(Trace logger, String activity, String phase,
boolean important, LensContext<F> context, boolean showTriples) throws SchemaException {
if (logger.isTraceEnabled()) {
logger.trace("Lens context:\n"+
"---[ {} context {} ]--------------------------------\n"+
"{}\n",
activity, phase, context.dump(showTriples));
}
}
public static <F extends ObjectType> ResourceType getResourceReadOnly(LensContext<F> context,
String resourceOid, ProvisioningService provisioningService, Task task, OperationResult result) throws ObjectNotFoundException,
CommunicationException, SchemaException, ConfigurationException, SecurityViolationException {
ResourceType resourceType = context.getResource(resourceOid);
if (resourceType == null) {
// Fetching from provisioning to take advantage of caching and
// pre-parsed schema
Collection<SelectorOptions<GetOperationOptions>> options = SelectorOptions.createCollection(GetOperationOptions.createReadOnly());
resourceType = provisioningService.getObject(ResourceType.class, resourceOid, options, task, result)
.asObjectable();
context.rememberResource(resourceType);
}
return resourceType;
}
public static <F extends ObjectType> ResourceType getResourceReadOnly(LensContext<F> context, String resourceOid, ObjectResolver objectResolver,
Task task, OperationResult result) throws ObjectNotFoundException,
CommunicationException, SchemaException, ConfigurationException, SecurityViolationException {
ResourceType resourceType = context.getResource(resourceOid);
if (resourceType == null) {
ObjectReferenceType ref = new ObjectReferenceType();
ref.setType(ResourceType.COMPLEX_TYPE);
ref.setOid(resourceOid);
Collection<SelectorOptions<GetOperationOptions>> options = SelectorOptions.createCollection(GetOperationOptions.createReadOnly());
resourceType = objectResolver.resolve(ref, ResourceType.class, options, "resource fetch in lens", task, result);
context.rememberResource(resourceType);
}
return resourceType;
}
public static String refineProjectionIntent(ShadowKindType kind, String intent, ResourceType resource, PrismContext prismContext) throws SchemaException {
RefinedResourceSchema refinedSchema = RefinedResourceSchemaImpl.getRefinedSchema(resource, LayerType.MODEL, prismContext);
RefinedObjectClassDefinition rObjClassDef = refinedSchema.getRefinedDefinition(kind, intent);
if (rObjClassDef == null) {
throw new SchemaException("No projection definition for kind="+kind+" intent="+intent+" in "+resource);
}
return rObjClassDef.getIntent();
}
public static <F extends FocusType> LensProjectionContext getProjectionContext(LensContext<F> context, PrismObject<ShadowType> equivalentAccount,
ProvisioningService provisioningService, PrismContext prismContext,
Task task, OperationResult result) throws ObjectNotFoundException,
CommunicationException, SchemaException, ConfigurationException, SecurityViolationException {
ShadowType equivalentAccountType = equivalentAccount.asObjectable();
ShadowKindType kind = ShadowUtil.getKind(equivalentAccountType);
return getProjectionContext(context, ShadowUtil.getResourceOid(equivalentAccountType),
kind, equivalentAccountType.getIntent(), provisioningService,
prismContext, task, result);
}
public static <F extends FocusType> LensProjectionContext getProjectionContext(LensContext<F> context, String resourceOid,
ShadowKindType kind, String intent,
ProvisioningService provisioningService, PrismContext prismContext,
Task task, OperationResult result) throws ObjectNotFoundException,
CommunicationException, SchemaException, ConfigurationException, SecurityViolationException {
ResourceType resource = getResourceReadOnly(context, resourceOid, provisioningService, task, result);
String refinedIntent = refineProjectionIntent(kind, intent, resource, prismContext);
ResourceShadowDiscriminator rsd = new ResourceShadowDiscriminator(resourceOid, kind, refinedIntent);
return context.findProjectionContext(rsd);
}
public static <F extends ObjectType> LensProjectionContext getOrCreateProjectionContext(LensContext<F> context,
ResourceShadowDiscriminator rsd) {
LensProjectionContext accountSyncContext = context.findProjectionContext(rsd);
if (accountSyncContext == null) {
accountSyncContext = context.createProjectionContext(rsd);
ResourceType resource = context.getResource(rsd.getResourceOid());
accountSyncContext.setResource(resource);
}
accountSyncContext.setDoReconciliation(context.isDoReconciliationForAllProjections());
return accountSyncContext;
}
public static <F extends ObjectType> LensProjectionContext createAccountContext(LensContext<F> context, ResourceShadowDiscriminator rsd){
return new LensProjectionContext(context, rsd);
}
/**
* Consolidate the mappings of a single item to a delta. It takes the convenient structure of ItemValueWithOrigin triple.
* It produces the delta considering the mapping exclusion, authoritativeness and strength.
*
* filterExistingValues: if true, then values that already exist in the item are not added (and those that don't exist are not removed)
*/
@NotNull
public static <V extends PrismValue, D extends ItemDefinition, I extends ItemValueWithOrigin<V,D>>
ItemDelta<V,D> consolidateTripleToDelta(
ItemPath itemPath, DeltaSetTriple<I> triple, D itemDefinition,
ItemDelta<V,D> aprioriItemDelta, PrismContainer<?> itemContainer, ValueMatcher<?> valueMatcher, Comparator<V> comparator,
boolean addUnchangedValues, boolean filterExistingValues, boolean isExclusiveStrong,
String contextDescription, boolean applyWeak) throws ExpressionEvaluationException, PolicyViolationException, SchemaException {
ItemDelta<V,D> itemDelta = itemDefinition.createEmptyDelta(itemPath);
Item<V,D> itemExisting = null;
if (itemContainer != null) {
itemExisting = itemContainer.findItem(itemPath);
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Consolidating {} triple:\n{}\nApriori Delta:\n{}\nExisting item:\n{}",
itemPath, triple.debugDump(1),
DebugUtil.debugDump(aprioriItemDelta, 1),
DebugUtil.debugDump(itemExisting, 1));
}
Collection<V> allValues = collectAllValues(triple, valueMatcher);
final MutableBoolean itemHasStrongMutable = new MutableBoolean(false);
SimpleVisitor<I> visitor = pvwo -> {
if (pvwo.getMapping().getStrength() == MappingStrengthType.STRONG) {
itemHasStrongMutable.setValue(true);
}
};
triple.accept(visitor);
boolean ignoreNormalMappings = itemHasStrongMutable.booleanValue() && isExclusiveStrong;
// We will process each value individually. I really mean each value. This whole method deals with
// a single item (e.g. attribute). But this loop iterates over every potential value of that item.
for (V value : allValues) {
LOGGER.trace(" consolidating value: {}", value);
// Check what to do with the value using the usual "triple routine". It means that if a value is
// in zero set than we need no delta, plus set means add delta and minus set means delete delta.
// The first set that the value is present determines the result.
Collection<ItemValueWithOrigin<V,D>> zeroPvwos =
collectPvwosFromSet(value, triple.getZeroSet(), valueMatcher);
Collection<ItemValueWithOrigin<V,D>> plusPvwos =
collectPvwosFromSet(value, triple.getPlusSet(), valueMatcher);
Collection<ItemValueWithOrigin<V,D>> minusPvwos =
collectPvwosFromSet(value, triple.getMinusSet(), valueMatcher);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("PVWOs for value {}:\nzero = {}\nplus = {}\nminus = {}",
value, zeroPvwos, plusPvwos, minusPvwos);
}
boolean zeroHasStrong = false;
if (!zeroPvwos.isEmpty()) {
for (ItemValueWithOrigin<V,D> pvwo : zeroPvwos) {
PrismValueDeltaSetTripleProducer<V,D> mapping = pvwo.getMapping();
if (mapping.getStrength() == MappingStrengthType.STRONG) {
zeroHasStrong = true;
}
}
}
if (zeroHasStrong && aprioriItemDelta != null && aprioriItemDelta.isValueToDelete(value, true)) {
throw new PolicyViolationException("Attempt to delete value "+value+" from item "+itemPath
+" but that value is mandated by a strong mapping (in "+contextDescription+")");
}
if (!zeroPvwos.isEmpty() && !addUnchangedValues) {
// Value unchanged, nothing to do
LOGGER.trace("Value {} unchanged, doing nothing", value);
continue;
}
PrismValueDeltaSetTripleProducer<V, D> exclusiveMapping = null;
Collection<ItemValueWithOrigin<V,D>> pvwosToAdd = null;
if (addUnchangedValues) {
pvwosToAdd = MiscUtil.union(zeroPvwos, plusPvwos);
} else {
pvwosToAdd = plusPvwos;
}
if (!pvwosToAdd.isEmpty()) {
boolean weakOnly = true;
boolean hasStrong = false;
// There may be several mappings that imply that value. So check them all for
// exclusions and strength
for (ItemValueWithOrigin<V,D> pvwoToAdd : pvwosToAdd) {
PrismValueDeltaSetTripleProducer<V,D> mapping = pvwoToAdd.getMapping();
if (mapping.getStrength() != MappingStrengthType.WEAK) {
weakOnly = false;
}
if (mapping.getStrength() == MappingStrengthType.STRONG) {
hasStrong = true;
}
if (mapping.isExclusive()) {
if (exclusiveMapping == null) {
exclusiveMapping = mapping;
} else {
String message = "Exclusion conflict in " + contextDescription + ", item " + itemPath +
", conflicting constructions: " + exclusiveMapping + " and " + mapping;
LOGGER.error(message);
throw new ExpressionEvaluationException(message);
}
}
}
if (weakOnly) {
// Postpone processing of weak values until we process all other values
LOGGER.trace("Value {} mapping is weak in item {}, postponing processing in {}",
value, itemPath, contextDescription);
continue;
}
if (!hasStrong && ignoreNormalMappings) {
LOGGER.trace("Value {} mapping is normal in item {} and we have exclusiveStrong, skipping processing in {}",
value, itemPath, contextDescription);
continue;
}
if (hasStrong && aprioriItemDelta != null && aprioriItemDelta.isValueToDelete(value, true)) {
throw new PolicyViolationException("Attempt to delete value "+value+" from item "+itemPath
+" but that value is mandated by a strong mapping (in "+contextDescription+")");
}
if (!hasStrong && (aprioriItemDelta != null && !aprioriItemDelta.isEmpty())) {
// There is already a delta, skip this
LOGGER.trace("Value {} mapping is not strong and the item {} already has a delta that is more concrete, " +
"skipping adding in {}", value, itemPath, contextDescription);
continue;
}
if (filterExistingValues && hasValue(itemExisting, value, valueMatcher, comparator)) {
LOGGER.trace("Value {} NOT added to delta for item {} because the item already has that value in {}",
value, itemPath, contextDescription);
continue;
}
LOGGER.trace("Value {} added to delta as ADD for item {} in {}", value, itemPath, contextDescription);
itemDelta.addValueToAdd((V)value.clone());
continue;
}
// We need to check for empty plus set. Values that are both in plus and minus are considered to be added.
// So check for that special case here to avoid removing them.
if (!minusPvwos.isEmpty() && plusPvwos.isEmpty()) {
boolean weakOnly = true;
boolean hasStrong = false;
boolean hasAuthoritative = false;
// There may be several mappings that imply that value. So check them all for
// exclusions and strength
for (ItemValueWithOrigin<V,D> pvwo : minusPvwos) {
PrismValueDeltaSetTripleProducer<V,D> mapping = pvwo.getMapping();
if (mapping.getStrength() != MappingStrengthType.WEAK) {
weakOnly = false;
}
if (mapping.getStrength() == MappingStrengthType.STRONG) {
hasStrong = true;
}
if (mapping.isAuthoritative()) {
hasAuthoritative = true;
}
}
if (!hasAuthoritative) {
LOGGER.trace("Value {} has no authoritative mapping for item {}, skipping deletion in {}",
value, itemPath, contextDescription);
continue;
}
if (!hasStrong && (aprioriItemDelta != null && !aprioriItemDelta.isEmpty())) {
// There is already a delta, skip this
LOGGER.trace("Value {} mapping is not strong and the item {} already has a delta that is more concrete, skipping deletion in {}",
value, itemPath, contextDescription);
continue;
}
if (weakOnly && (itemExisting != null && !itemExisting.isEmpty())) {
// There is already a value, skip this
LOGGER.trace("Value {} mapping is weak and the item {} already has a value, skipping deletion in {}",
value, itemPath, contextDescription);
continue;
}
if (weakOnly && !applyWeak && (itemExisting == null || itemExisting.isEmpty())){
// There is a weak mapping on a property, but we do not have full account available, so skipping deletion of the value is better way
LOGGER.trace("Value {} mapping is weak and the full account could not be fetched, skipping deletion in {}",
value, itemPath, contextDescription);
continue;
}
if (filterExistingValues && !hasValue(itemExisting, value, valueMatcher, comparator)) {
LOGGER.trace("Value {} NOT add to delta as DELETE because item {} the item does not have that value in {} (matcher: {})",
value, itemPath, contextDescription, valueMatcher);
continue;
}
LOGGER.trace("Value {} added to delta as DELETE for item {} in {}",
value, itemPath, contextDescription);
itemDelta.addValueToDelete((V)value.clone());
}
if (!zeroPvwos.isEmpty()) {
boolean weakOnly = true;
boolean hasStrong = false;
boolean hasAuthoritative = false;
// There may be several mappings that imply that value. So check them all for
// exclusions and strength
for (ItemValueWithOrigin<V,D> pvwo : zeroPvwos) {
PrismValueDeltaSetTripleProducer<V,D> mapping = pvwo.getMapping();
if (mapping.getStrength() != MappingStrengthType.WEAK) {
weakOnly = false;
}
if (mapping.getStrength() == MappingStrengthType.STRONG) {
hasStrong = true;
}
if (mapping.isAuthoritative()) {
hasAuthoritative = true;
}
}
if (aprioriItemDelta != null && aprioriItemDelta.isReplace()) {
// Any strong mappings in the zero set needs to be re-applied as otherwise the replace will destroy it
if (hasStrong) {
LOGGER.trace("Value {} added to delta for item {} in {} because there is strong mapping in the zero set",
value, itemPath, contextDescription);
itemDelta.addValueToAdd((V)value.clone());
continue;
}
}
}
}
Item<V,D> itemNew = null;
if (itemContainer != null) {
itemNew = itemContainer.findItem(itemPath);
}
if (!hasValue(itemNew, itemDelta)) {
// The application of computed delta results in no value, apply weak mappings
Collection<? extends ItemValueWithOrigin<V,D>> nonNegativePvwos = triple.getNonNegativeValues();
Collection<V> valuesToAdd = addWeakValues(nonNegativePvwos, OriginType.ASSIGNMENTS, applyWeak);
if (valuesToAdd.isEmpty()) {
valuesToAdd = addWeakValues(nonNegativePvwos, OriginType.OUTBOUND, applyWeak);
}
if (valuesToAdd.isEmpty()) {
valuesToAdd = addWeakValues(nonNegativePvwos, null, applyWeak);
}
LOGGER.trace("No value for item {} in {}, weak mapping processing yielded values: {}",
itemPath, contextDescription, valuesToAdd);
itemDelta.addValuesToAdd(valuesToAdd);
} else {
LOGGER.trace("Existing values for item {} in {}, weak mapping processing skipped",
new Object[]{itemPath, contextDescription});
}
if (itemExisting != null) {
List<V> existingValues = itemExisting.getValues();
if (existingValues != null) {
itemDelta.setEstimatedOldValues(PrismValue.cloneCollection(existingValues));
}
}
return itemDelta;
}
private static <V extends PrismValue, D extends ItemDefinition> boolean hasValue(Item<V,D> item, ItemDelta<V,D> itemDelta) throws SchemaException {
if (item == null || item.isEmpty()) {
if (itemDelta != null && itemDelta.addsAnyValue()) {
return true;
} else {
return false;
}
} else {
if (itemDelta == null || itemDelta.isEmpty()) {
return true;
} else {
Item<V,D> clonedItem = item.clone();
itemDelta.applyToMatchingPath(clonedItem);
return !clonedItem.isEmpty();
}
}
}
private static <V extends PrismValue, D extends ItemDefinition> Collection<V> addWeakValues(Collection<? extends ItemValueWithOrigin<V,D>> pvwos, OriginType origin, boolean applyWeak) {
Collection<V> values = new ArrayList<V>();
for (ItemValueWithOrigin<V,D> pvwo: pvwos) {
if (pvwo.getMapping().getStrength() == MappingStrengthType.WEAK && applyWeak) {
if (origin == null || origin == pvwo.getItemValue().getOriginType()) {
values.add((V)pvwo.getItemValue().clone());
}
}
}
return values;
}
private static <V extends PrismValue, D extends ItemDefinition> boolean hasValue(Item<V,D> existingUserItem, V newValue, ValueMatcher<?> valueMatcher, Comparator<V> comparator) {
if (existingUserItem == null) {
return false;
}
if (valueMatcher != null && newValue instanceof PrismPropertyValue) {
return valueMatcher.hasRealValue((PrismProperty)existingUserItem, (PrismPropertyValue)newValue);
} else {
return existingUserItem.contains(newValue, true, comparator);
}
}
private static <V extends PrismValue, D extends ItemDefinition> Collection<V> collectAllValues
(DeltaSetTriple<? extends ItemValueWithOrigin<V,D>> triple, ValueMatcher<?> valueMatcher) throws SchemaException {
Collection<V> allValues = new HashSet<>();
collectAllValuesFromSet(allValues, triple.getZeroSet(), valueMatcher);
collectAllValuesFromSet(allValues, triple.getPlusSet(), valueMatcher);
collectAllValuesFromSet(allValues, triple.getMinusSet(), valueMatcher);
return allValues;
}
private static <V extends PrismValue, D extends ItemDefinition, T> void collectAllValuesFromSet(Collection<V> allValues,
Collection<? extends ItemValueWithOrigin<V,D>> collection, ValueMatcher<T> valueMatcher) throws SchemaException {
if (collection == null) {
return;
}
for (ItemValueWithOrigin<V,D> pvwo : collection) {
V pval = pvwo.getItemValue();
if (valueMatcher == null) {
if (!PrismValue.containsRealValue(allValues, pval)) {
allValues.add(pval);
}
} else {
boolean found = false;
for (V valueFromAllvalues: allValues) {
if (valueMatcher.match(((PrismPropertyValue<T>)valueFromAllvalues).getValue(),
((PrismPropertyValue<T>)pval).getValue())) {
found = true;
break;
}
}
if (!found) {
allValues.add(pval);
}
}
}
}
private static <V extends PrismValue, D extends ItemDefinition> Collection<ItemValueWithOrigin<V,D>> collectPvwosFromSet(V pvalue,
Collection<? extends ItemValueWithOrigin<V,D>> deltaSet, ValueMatcher<?> valueMatcher) throws SchemaException {
Collection<ItemValueWithOrigin<V,D>> pvwos = new ArrayList<>();
for (ItemValueWithOrigin<V,D> setPvwo : deltaSet) {
if (setPvwo.equalsRealValue(pvalue, valueMatcher)) {
pvwos.add(setPvwo);
}
}
return pvwos;
}
public static PropertyDelta<XMLGregorianCalendar> createActivationTimestampDelta(ActivationStatusType status, XMLGregorianCalendar now,
PrismContainerDefinition<ActivationType> activationDefinition, OriginType origin) {
QName timestampPropertyName;
if (status == null || status == ActivationStatusType.ENABLED) {
timestampPropertyName = ActivationType.F_ENABLE_TIMESTAMP;
} else if (status == ActivationStatusType.DISABLED) {
timestampPropertyName = ActivationType.F_DISABLE_TIMESTAMP;
} else if (status == ActivationStatusType.ARCHIVED) {
timestampPropertyName = ActivationType.F_ARCHIVE_TIMESTAMP;
} else {
throw new IllegalArgumentException("Unknown activation status "+status);
}
PrismPropertyDefinition<XMLGregorianCalendar> timestampDef = activationDefinition.findPropertyDefinition(timestampPropertyName);
PropertyDelta<XMLGregorianCalendar> timestampDelta
= timestampDef.createEmptyDelta(new ItemPath(FocusType.F_ACTIVATION, timestampPropertyName));
timestampDelta.setValueToReplace(new PrismPropertyValue<XMLGregorianCalendar>(now, origin, null));
return timestampDelta;
}
public static <F extends ObjectType> void moveTriggers(LensProjectionContext projCtx, LensFocusContext<F> focusCtx) throws SchemaException {
ObjectDelta<ShadowType> projSecondaryDelta = projCtx.getSecondaryDelta();
if (projSecondaryDelta == null) {
return;
}
Collection<? extends ItemDelta> modifications = projSecondaryDelta.getModifications();
Iterator<? extends ItemDelta> iterator = modifications.iterator();
while (iterator.hasNext()) {
ItemDelta projModification = iterator.next();
LOGGER.trace("MOD: {}\n{}", projModification.getPath(), projModification.debugDump());
if (projModification.getPath().equivalent(SchemaConstants.PATH_TRIGGER)) {
focusCtx.swallowToProjectionWaveSecondaryDelta(projModification);
iterator.remove();
}
}
}
public static Object getIterationVariableValue(LensProjectionContext accCtx) {
Integer iterationOld = null;
PrismObject<ShadowType> shadowCurrent = accCtx.getObjectCurrent();
if (shadowCurrent != null) {
iterationOld = shadowCurrent.asObjectable().getIteration();
}
if (iterationOld == null) {
return accCtx.getIteration();
}
PrismPropertyDefinition<Integer> propDef = new PrismPropertyDefinitionImpl<>(ExpressionConstants.VAR_ITERATION,
DOMUtil.XSD_INT, accCtx.getPrismContext());
PrismProperty<Integer> propOld = propDef.instantiate();
propOld.setRealValue(iterationOld);
PropertyDelta<Integer> propDelta = propDef.createEmptyDelta(new ItemPath(ExpressionConstants.VAR_ITERATION));
propDelta.setValueToReplace(new PrismPropertyValue<Integer>(accCtx.getIteration()));
PrismProperty<Integer> propNew = propDef.instantiate();
propNew.setRealValue(accCtx.getIteration());
ItemDeltaItem<PrismPropertyValue<Integer>,PrismPropertyDefinition<Integer>> idi = new ItemDeltaItem<>(propOld, propDelta, propNew);
return idi;
}
public static Object getIterationTokenVariableValue(LensProjectionContext accCtx) {
String iterationTokenOld = null;
PrismObject<ShadowType> shadowCurrent = accCtx.getObjectCurrent();
if (shadowCurrent != null) {
iterationTokenOld = shadowCurrent.asObjectable().getIterationToken();
}
if (iterationTokenOld == null) {
return accCtx.getIterationToken();
}
PrismPropertyDefinition<String> propDef = new PrismPropertyDefinitionImpl<>(
ExpressionConstants.VAR_ITERATION_TOKEN, DOMUtil.XSD_STRING, accCtx.getPrismContext());
PrismProperty<String> propOld = propDef.instantiate();
propOld.setRealValue(iterationTokenOld);
PropertyDelta<String> propDelta = propDef.createEmptyDelta(new ItemPath(ExpressionConstants.VAR_ITERATION_TOKEN));
propDelta.setValueToReplace(new PrismPropertyValue<String>(accCtx.getIterationToken()));
PrismProperty<String> propNew = propDef.instantiate();
propNew.setRealValue(accCtx.getIterationToken());
ItemDeltaItem<PrismPropertyValue<String>,PrismPropertyDefinition<String>> idi = new ItemDeltaItem<>(propOld, propDelta, propNew);
return idi;
}
/**
* Extracts the delta from this projection context and also from all other projection contexts that have
* equivalent discriminator.
*/
public static <F extends ObjectType, T> PropertyDelta<T> findAPrioriDelta(LensContext<F> context,
LensProjectionContext projCtx, ItemPath projectionPropertyPath) throws SchemaException {
PropertyDelta<T> aPrioriDelta = null;
for (LensProjectionContext aProjCtx: findRelatedContexts(context, projCtx)) {
ObjectDelta<ShadowType> aProjDelta = aProjCtx.getDelta();
if (aProjDelta != null) {
PropertyDelta<T> aPropProjDelta = aProjDelta.findPropertyDelta(projectionPropertyPath);
if (aPropProjDelta != null) {
if (aPrioriDelta == null) {
aPrioriDelta = aPropProjDelta.clone();
} else {
aPrioriDelta.merge(aPropProjDelta);
}
}
}
}
return aPrioriDelta;
}
/**
* Extracts the delta from this projection context and also from all other projection contexts that have
* equivalent discriminator.
*/
public static <F extends ObjectType, T> ObjectDelta<ShadowType> findAPrioriDelta(LensContext<F> context,
LensProjectionContext projCtx) throws SchemaException {
ObjectDelta<ShadowType> aPrioriDelta = null;
for (LensProjectionContext aProjCtx: findRelatedContexts(context, projCtx)) {
ObjectDelta<ShadowType> aProjDelta = aProjCtx.getDelta();
if (aProjDelta != null) {
if (aPrioriDelta == null) {
aPrioriDelta = aProjDelta.clone();
} else {
aPrioriDelta.merge(aProjDelta);
}
}
}
return aPrioriDelta;
}
/**
* Returns a list of context that have equivalent discriminator with the reference context. Ordered by "order" in the
* discriminator.
*/
public static <F extends ObjectType> List<LensProjectionContext> findRelatedContexts(
LensContext<F> context, LensProjectionContext refProjCtx) {
List<LensProjectionContext> projCtxs = new ArrayList<LensProjectionContext>();
ResourceShadowDiscriminator refDiscr = refProjCtx.getResourceShadowDiscriminator();
if (refDiscr == null) {
return projCtxs;
}
for (LensProjectionContext aProjCtx: context.getProjectionContexts()) {
ResourceShadowDiscriminator aDiscr = aProjCtx.getResourceShadowDiscriminator();
if (refDiscr.equivalent(aDiscr)) {
projCtxs.add(aProjCtx);
}
}
Comparator<? super LensProjectionContext> orderComparator = new Comparator<LensProjectionContext>() {
@Override
public int compare(LensProjectionContext ctx1, LensProjectionContext ctx2) {
int order1 = ctx1.getResourceShadowDiscriminator().getOrder();
int order2 = ctx2.getResourceShadowDiscriminator().getOrder();
return Integer.compare(order1, order2);
}
};
Collections.sort(projCtxs, orderComparator);
return projCtxs;
}
public static <F extends ObjectType> boolean hasLowerOrderContext(LensContext<F> context,
LensProjectionContext refProjCtx) {
ResourceShadowDiscriminator refDiscr = refProjCtx.getResourceShadowDiscriminator();
for (LensProjectionContext aProjCtx: context.getProjectionContexts()) {
ResourceShadowDiscriminator aDiscr = aProjCtx.getResourceShadowDiscriminator();
if (refDiscr.equivalent(aDiscr) && (refDiscr.getOrder() > aDiscr.getOrder())) {
return true;
}
}
return false;
}
public static <F extends ObjectType> boolean hasDependentContext(LensContext<F> context,
LensProjectionContext targetProjectionContext) {
for (LensProjectionContext projectionContext: context.getProjectionContexts()) {
for (ResourceObjectTypeDependencyType dependency: projectionContext.getDependencies()) {
if (isDependencyTargetContext(projectionContext, targetProjectionContext, dependency)) {
return true;
}
}
}
return false;
}
public static <F extends ObjectType> boolean isDependencyTargetContext(LensProjectionContext sourceProjContext, LensProjectionContext targetProjectionContext, ResourceObjectTypeDependencyType dependency) {
ResourceShadowDiscriminator refDiscr = new ResourceShadowDiscriminator(dependency,
sourceProjContext.getResource().getOid(), sourceProjContext.getKind());
return targetProjectionContext.compareResourceShadowDiscriminator(refDiscr, false);
}
public static <F extends ObjectType> LensProjectionContext findLowerOrderContext(LensContext<F> context,
LensProjectionContext refProjCtx) {
int minOrder = -1;
LensProjectionContext foundCtx = null;
ResourceShadowDiscriminator refDiscr = refProjCtx.getResourceShadowDiscriminator();
for (LensProjectionContext aProjCtx: context.getProjectionContexts()) {
ResourceShadowDiscriminator aDiscr = aProjCtx.getResourceShadowDiscriminator();
if (refDiscr.equivalent(aDiscr) && (refDiscr.getOrder() > aDiscr.getOrder())) {
if (minOrder < 0 || (aDiscr.getOrder() < minOrder)) {
minOrder = aDiscr.getOrder();
foundCtx = aProjCtx;
}
}
}
return foundCtx;
}
public static <T extends ObjectType, F extends ObjectType> void setContextOid(LensContext<F> context,
LensElementContext<T> objectContext, String oid) {
objectContext.setOid(oid);
// Check if we need to propagate this oid also to higher-order contexts
if (!(objectContext instanceof LensProjectionContext)) {
return;
}
LensProjectionContext refProjCtx = (LensProjectionContext)objectContext;
ResourceShadowDiscriminator refDiscr = refProjCtx.getResourceShadowDiscriminator();
if (refDiscr == null) {
return;
}
for (LensProjectionContext aProjCtx: context.getProjectionContexts()) {
ResourceShadowDiscriminator aDiscr = aProjCtx.getResourceShadowDiscriminator();
if (aDiscr != null && refDiscr.equivalent(aDiscr) && (refDiscr.getOrder() < aDiscr.getOrder())) {
aProjCtx.setOid(oid);
}
}
}
public static <F extends FocusType> PrismObjectDefinition<F> getFocusDefinition(LensContext<F> context) {
LensFocusContext<F> focusContext = context.getFocusContext();
if (focusContext == null) {
return null;
}
Class<F> typeClass = focusContext.getObjectTypeClass();
return context.getPrismContext().getSchemaRegistry().findObjectDefinitionByCompileTimeClass(typeClass);
}
public static int determineMaxIterations(IterationSpecificationType iterationSpecType) {
if (iterationSpecType != null) {
return iterationSpecType.getMaxIterations();
} else {
return 0;
}
}
public static <F extends ObjectType> String formatIterationToken(LensContext<F> context,
LensElementContext<?> accountContext, IterationSpecificationType iterationType,
int iteration, ExpressionFactory expressionFactory, ExpressionVariables variables,
Task task, OperationResult result)
throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException {
if (iterationType == null) {
return formatIterationTokenDefault(iteration);
}
ExpressionType tokenExpressionType = iterationType.getTokenExpression();
if (tokenExpressionType == null) {
return formatIterationTokenDefault(iteration);
}
PrismPropertyDefinition<String> outputDefinition = new PrismPropertyDefinitionImpl<>(ExpressionConstants.VAR_ITERATION_TOKEN,
DOMUtil.XSD_STRING, context.getPrismContext());
Expression<PrismPropertyValue<String>,PrismPropertyDefinition<String>> expression = expressionFactory.makeExpression(tokenExpressionType, outputDefinition , "iteration token expression in "+accountContext.getHumanReadableName(), task, result);
Collection<Source<?,?>> sources = new ArrayList<>();
PrismPropertyDefinitionImpl<Integer> inputDefinition = new PrismPropertyDefinitionImpl<>(ExpressionConstants.VAR_ITERATION,
DOMUtil.XSD_INT, context.getPrismContext());
inputDefinition.setMaxOccurs(1);
PrismProperty<Integer> input = inputDefinition.instantiate();
input.add(new PrismPropertyValue<Integer>(iteration));
ItemDeltaItem<PrismPropertyValue<Integer>,PrismPropertyDefinition<Integer>> idi = new ItemDeltaItem<>(input);
Source<PrismPropertyValue<Integer>,PrismPropertyDefinition<Integer>> iterationSource = new Source<>(idi, ExpressionConstants.VAR_ITERATION);
sources.add(iterationSource);
ExpressionEvaluationContext expressionContext = new ExpressionEvaluationContext(sources , variables,
"iteration token expression in "+accountContext.getHumanReadableName(), task, result);
PrismValueDeltaSetTriple<PrismPropertyValue<String>> outputTriple = ModelExpressionThreadLocalHolder.evaluateExpressionInContext(expression, expressionContext, task, result);
Collection<PrismPropertyValue<String>> outputValues = outputTriple.getNonNegativeValues();
if (outputValues.isEmpty()) {
return "";
}
if (outputValues.size() > 1) {
throw new ExpressionEvaluationException("Iteration token expression in "+accountContext.getHumanReadableName()+" returned more than one value ("+outputValues.size()+" values)");
}
String realValue = outputValues.iterator().next().getValue();
if (realValue == null) {
return "";
}
return realValue;
}
public static String formatIterationTokenDefault(int iteration) {
if (iteration == 0) {
return "";
}
return Integer.toString(iteration);
}
public static <F extends ObjectType> boolean evaluateIterationCondition(LensContext<F> context,
LensElementContext<?> accountContext, IterationSpecificationType iterationType,
int iteration, String iterationToken, boolean beforeIteration,
ExpressionFactory expressionFactory, ExpressionVariables variables, Task task, OperationResult result)
throws ExpressionEvaluationException, SchemaException, ObjectNotFoundException {
if (iterationType == null) {
return true;
}
ExpressionType expressionType;
String desc;
if (beforeIteration) {
expressionType = iterationType.getPreIterationCondition();
desc = "pre-iteration expression in "+accountContext.getHumanReadableName();
} else {
expressionType = iterationType.getPostIterationCondition();
desc = "post-iteration expression in "+accountContext.getHumanReadableName();
}
if (expressionType == null) {
return true;
}
PrismPropertyDefinition<Boolean> outputDefinition = new PrismPropertyDefinitionImpl<>(
ExpressionConstants.OUTPUT_ELEMENT_NAME,
DOMUtil.XSD_BOOLEAN, context.getPrismContext());
Expression<PrismPropertyValue<Boolean>,PrismPropertyDefinition<Boolean>> expression = expressionFactory.makeExpression(expressionType, outputDefinition , desc, task, result);
variables.addVariableDefinition(ExpressionConstants.VAR_ITERATION, iteration);
variables.addVariableDefinition(ExpressionConstants.VAR_ITERATION_TOKEN, iterationToken);
ExpressionEvaluationContext expressionContext = new ExpressionEvaluationContext(null , variables, desc, task, result);
PrismValueDeltaSetTriple<PrismPropertyValue<Boolean>> outputTriple = ModelExpressionThreadLocalHolder.evaluateExpressionInContext(expression, expressionContext, task, result);
Collection<PrismPropertyValue<Boolean>> outputValues = outputTriple.getNonNegativeValues();
if (outputValues.isEmpty()) {
return false;
}
if (outputValues.size() > 1) {
throw new ExpressionEvaluationException(desc+" returned more than one value ("+outputValues.size()+" values)");
}
Boolean realValue = outputValues.iterator().next().getValue();
if (realValue == null) {
return false;
}
return realValue;
}
/**
* Used for assignments and similar objects that do not have separate lifecycle.
*/
public static boolean isAssignmentValid(FocusType focus, AssignmentType assignmentType, XMLGregorianCalendar now, ActivationComputer activationComputer) {
String focusLifecycleState = focus.getLifecycleState();
if (!activationComputer.lifecycleHasActiveAssignments(focusLifecycleState)) {
return false;
}
return isValid(assignmentType.getLifecycleState(), assignmentType.getActivation(), now, activationComputer);
}
public static boolean isFocusValid(FocusType focus, XMLGregorianCalendar now, ActivationComputer activationComputer) {
return isValid(focus.getLifecycleState(), focus.getActivation(), now, activationComputer);
}
private static boolean isValid(String lifecycleState, ActivationType activationType, XMLGregorianCalendar now, ActivationComputer activationComputer) {
TimeIntervalStatusType validityStatus = activationComputer.getValidityStatus(activationType, now);
ActivationStatusType effectiveStatus = activationComputer.getEffectiveStatus(lifecycleState, activationType, validityStatus);
return effectiveStatus == ActivationStatusType.ENABLED;
}
public static AssignmentPathVariables computeAssignmentPathVariables(AssignmentPathImpl assignmentPath) throws SchemaException {
if (assignmentPath == null || assignmentPath.isEmpty()) {
return null;
}
AssignmentPathVariables vars = new AssignmentPathVariables();
vars.setAssignmentPath(assignmentPath.clone());
Iterator<AssignmentPathSegmentImpl> iterator = assignmentPath.getSegments().iterator();
while (iterator.hasNext()) {
AssignmentPathSegmentImpl segment = iterator.next();
ItemDeltaItem<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> segmentAssignmentIdi = segment.getAssignmentIdi();
ItemDeltaItem<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> magicAssignmentIdi;
// Magic assignment
if (vars.getMagicAssignment() == null) {
magicAssignmentIdi = segmentAssignmentIdi.clone();
vars.setMagicAssignment(magicAssignmentIdi);
} else {
// Collect extension values from the assignment extension
magicAssignmentIdi = vars.getMagicAssignment();
mergeExtension(magicAssignmentIdi, segmentAssignmentIdi);
}
// Collect extension values from the source object extension
ObjectType segmentSource = segment.getSource();
if (segmentSource != null) {
mergeExtension(magicAssignmentIdi, segmentSource.asPrismObject());
}
// immediate assignment (use assignment from previous iteration)
vars.setImmediateAssignment(vars.getThisAssignment());
// this assignment
ItemDeltaItem<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> thisAssignment = segmentAssignmentIdi.clone();
vars.setThisAssignment(thisAssignment);
if (iterator.hasNext() && segmentSource instanceof AbstractRoleType) {
vars.setImmediateRole((PrismObject<? extends AbstractRoleType>) segmentSource.asPrismObject());
}
}
AssignmentPathSegmentImpl focusAssignmentSegment = assignmentPath.first();
ItemDeltaItem<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> focusAssignment = focusAssignmentSegment.getAssignmentIdi().clone();
vars.setFocusAssignment(focusAssignment);
return vars;
}
private static void mergeExtension(ItemDeltaItem<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> destIdi, ItemDeltaItem<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> srcIdi) throws SchemaException {
mergeExtension(destIdi.getItemOld(), srcIdi.getItemOld());
mergeExtension(destIdi.getItemNew(), srcIdi.getItemNew());
if (srcIdi.getDelta() != null || srcIdi.getSubItemDeltas() != null) {
throw new UnsupportedOperationException("Merge of IDI with deltas not supported");
}
}
private static void mergeExtension(Item<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> dstItem,
Item<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> srcItem) throws SchemaException {
if (srcItem == null || dstItem == null) {
return;
}
PrismContainer<Containerable> srcExtension = ((PrismContainer<AssignmentType>)srcItem).findContainer(AssignmentType.F_EXTENSION);
mergeExtensionContainers(dstItem, srcExtension);
}
private static <O extends ObjectType> void mergeExtension(ItemDeltaItem<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> destIdi,
PrismObject<O> srcObject) throws SchemaException {
if (srcObject == null) {
return;
}
PrismContainer<Containerable> srcExtension = srcObject.findContainer(ObjectType.F_EXTENSION);
mergeExtensionContainers(destIdi.getItemNew(), srcExtension);
mergeExtensionContainers(destIdi.getItemOld(), srcExtension);
}
private static void mergeExtensionContainers(Item<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> dstItem, PrismContainer<Containerable> srcExtension) throws SchemaException {
if (dstItem == null) {
return;
}
PrismContainer<AssignmentType> dstContainer = (PrismContainer<AssignmentType>) dstItem;
if (srcExtension != null && !srcExtension.getValue().isEmpty()) {
PrismContainer<Containerable> dstExtension = dstContainer.findOrCreateContainer(AssignmentType.F_EXTENSION);
for (Item<?,?> srcExtensionItem: srcExtension.getValue().getItems()) {
Item<?,?> magicItem = dstExtension.findItem(srcExtensionItem.getElementName());
if (magicItem == null) {
dstExtension.add(srcExtensionItem.clone());
}
}
}
}
private static void mergeExtension(PrismContainer<Containerable> magicExtension, PrismContainer<Containerable> segmentExtension) throws SchemaException {
if (segmentExtension != null && !segmentExtension.getValue().isEmpty()) {
for (Item<?,?> segmentItem: segmentExtension.getValue().getItems()) {
Item<?,?> magicItem = magicExtension.findItem(segmentItem.getElementName());
if (magicItem == null) {
magicExtension.add(segmentItem.clone());
}
}
}
}
public static <V extends PrismValue,D extends ItemDefinition> Mapping.Builder<V,D> addAssignmentPathVariables(Mapping.Builder<V,D> builder, AssignmentPathVariables assignmentPathVariables) {
ExpressionVariables expressionVariables = new ExpressionVariables();
Utils.addAssignmentPathVariables(assignmentPathVariables, expressionVariables);
return builder.addVariableDefinitions(expressionVariables.getMap());
}
public static <F extends ObjectType> void checkContextSanity(LensContext<F> context, String activityDescription,
OperationResult result) throws SchemaException, PolicyViolationException {
LensFocusContext<F> focusContext = context.getFocusContext();
if (focusContext != null) {
PrismObject<F> focusObjectNew = focusContext.getObjectNew();
if (focusObjectNew != null) {
PolyStringType namePolyType = focusObjectNew.asObjectable().getName();
if (namePolyType == null) {
throw new SchemaException("Focus "+focusObjectNew+" does not have a name after "+activityDescription);
}
ObjectPolicyConfigurationType objectPolicyConfigurationType = focusContext.getObjectPolicyConfigurationType();
checkObjectPolicy(focusContext, objectPolicyConfigurationType);
}
}
}
private static <F extends ObjectType> void checkObjectPolicy(LensFocusContext<F> focusContext, ObjectPolicyConfigurationType objectPolicyConfigurationType) throws SchemaException, PolicyViolationException {
if (objectPolicyConfigurationType == null) {
return;
}
PrismObject<F> focusObjectNew = focusContext.getObjectNew();
ObjectDelta<F> focusDelta = focusContext.getDelta();
for (PropertyConstraintType propertyConstraintType: objectPolicyConfigurationType.getPropertyConstraint()) {
ItemPath itemPath = propertyConstraintType.getPath().getItemPath();
if (BooleanUtils.isTrue(propertyConstraintType.isOidBound())) {
if (focusDelta != null) {
if (focusDelta.isAdd()) {
PrismProperty<Object> propNew = focusObjectNew.findProperty(itemPath);
if (propNew != null) {
// prop delta is OK, but it has to match
if (focusObjectNew.getOid() != null) {
if (!focusObjectNew.getOid().equals(propNew.getRealValue().toString())) {
throw new PolicyViolationException("Cannot set "+itemPath+" to a value different than OID in oid bound mode");
}
}
}
} else {
PropertyDelta<Object> nameDelta = focusDelta.findPropertyDelta(itemPath);
if (nameDelta != null) {
if (nameDelta.isReplace()) {
Collection<PrismPropertyValue<Object>> valuesToReplace = nameDelta.getValuesToReplace();
if (valuesToReplace.size() == 1) {
String stringValue = valuesToReplace.iterator().next().getValue().toString();
if (focusContext.getOid().equals(stringValue)) {
// This is OK. It is most likely a correction made by a recompute.
continue;
}
}
}
throw new PolicyViolationException("Cannot change "+itemPath+" in oid bound mode");
}
}
}
}
}
// Deprecated
if (BooleanUtils.isTrue(objectPolicyConfigurationType.isOidNameBoundMode())) {
if (focusDelta != null) {
if (focusDelta.isAdd()) {
PolyStringType namePolyType = focusObjectNew.asObjectable().getName();
if (namePolyType != null) {
// name delta is OK, but it has to match
if (focusObjectNew.getOid() != null) {
if (!focusObjectNew.getOid().equals(namePolyType.getOrig())) {
throw new PolicyViolationException("Cannot set name to a value different than OID in name-oid bound mode");
}
}
}
} else {
PropertyDelta<Object> nameDelta = focusDelta.findPropertyDelta(FocusType.F_NAME);
if (nameDelta != null) {
throw new PolicyViolationException("Cannot change name in name-oid bound mode");
}
}
}
}
}
public static PrismContainer<AssignmentType> createAssignmentSingleValueContainerClone(@NotNull AssignmentType assignmentType) throws SchemaException {
// Make it appear to be single-value. Therefore paths without segment IDs will work.
return assignmentType.asPrismContainerValue().asSingleValuedContainer(SchemaConstantsGenerated.C_ASSIGNMENT);
}
public static AssignmentType getAssignmentType(ItemDeltaItem<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> assignmentIdi, boolean old) {
if (old) {
return assignmentIdi.getItemOld().getValue(0).asContainerable();
} else {
return assignmentIdi.getItemNew().getValue(0).asContainerable();
}
}
public static <F extends ObjectType> String getChannel(LensContext<F> context, Task task) {
if (context != null && context.getChannel() != null){
return context.getChannel();
} else if (task.getChannel() != null){
return task.getChannel();
}
return null;
}
public static <O extends ObjectType> void setDeltaOldValue(LensElementContext<O> ctx, ItemDelta<?,?> itemDelta) {
if (itemDelta.getEstimatedOldValues() != null) {
return;
}
if (ctx.getObjectOld() != null) {
Item<PrismValue, ItemDefinition> itemOld = ctx.getObjectOld().findItem(itemDelta.getPath());
if (itemOld != null) {
itemDelta.setEstimatedOldValues((Collection) PrismValue.cloneCollection(itemOld.getValues()));
} else {
// get the old data from current object. Still better estimate than nothing
if (ctx.getObjectCurrent() != null) {
itemOld = ctx.getObjectCurrent().findItem(itemDelta.getPath());
if (itemOld != null) {
itemDelta.setEstimatedOldValues((Collection) PrismValue.cloneCollection(itemOld.getValues()));
}
}
}
}
}
public static <O extends ObjectType> void setDeltaOldValue(LensElementContext<O> ctx, ObjectDelta<O> objectDelta) {
if (objectDelta == null) {
return;
}
if (!objectDelta.isModify()) {
return;
}
for (ItemDelta<?, ?> modification: objectDelta.getModifications()) {
setDeltaOldValue(ctx, modification);
}
}
public static <F extends ObjectType> LensObjectDeltaOperation<F> createObjectDeltaOperation(ObjectDelta<F> focusDelta, OperationResult result,
LensElementContext<F> focusContext, LensProjectionContext projCtx) {
return createObjectDeltaOperation(focusDelta, result, focusContext, projCtx, null);
}
// projCtx may or may not be present (object itself can be focus or projection)
public static <T extends ObjectType> LensObjectDeltaOperation<T> createObjectDeltaOperation(ObjectDelta<T> objectDelta, OperationResult result,
LensElementContext<T> objectContext,
LensProjectionContext projCtx,
ResourceType resource) {
LensObjectDeltaOperation<T> objectDeltaOp = new LensObjectDeltaOperation<T>(objectDelta.clone());
objectDeltaOp.setExecutionResult(result);
PrismObject<T> object = objectContext.getObjectAny();
if (object != null) {
PolyString name = object.getName();
if (name == null && object.asObjectable() instanceof ShadowType) {
try {
name = ShadowUtil.determineShadowName((PrismObject<ShadowType>) object);
if (name == null) {
LOGGER.debug("No name for shadow:\n{}", object.debugDump());
} else if (name.getNorm() == null) {
name.recompute(new PrismDefaultPolyStringNormalizer());
}
} catch (SchemaException e) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't determine name for shadow -- continuing with no name; shadow:\n{}", e, object.debugDump());
}
}
objectDeltaOp.setObjectName(name);
}
if (resource == null && projCtx != null) {
resource = projCtx.getResource();
}
if (resource != null) {
objectDeltaOp.setResourceOid(resource.getOid());
objectDeltaOp.setResourceName(PolyString.toPolyString(resource.getName()));
} else if (objectContext instanceof LensProjectionContext) {
objectDeltaOp.setResourceOid(((LensProjectionContext) objectContext).getResourceOid());
}
return objectDeltaOp;
}
@Deprecated
public static boolean isDelegationRelation(QName relation) {
return ObjectTypeUtil.isDelegationRelation(relation);
}
public static void triggerConstraint(@Nullable EvaluatedPolicyRule rule, EvaluatedPolicyRuleTrigger trigger,
Collection<String> policySituations) throws PolicyViolationException {
LOGGER.debug("Policy rule {} triggered: {}", rule==null?null:rule.getName(), trigger);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Policy rule {} triggered:\n{}", rule==null?null:rule.getName(), trigger.debugDump(1));
}
if (rule == null) {
// legacy functionality
if (trigger.getConstraint().getEnforcement() == null || trigger.getConstraint().getEnforcement() == PolicyConstraintEnforcementType.ENFORCE) {
throw new PolicyViolationException(trigger.getMessage());
}
} else {
((EvaluatedPolicyRuleImpl)rule).addTrigger(trigger);
String policySituation = rule.getPolicySituation();
if (policySituation != null) {
policySituations.add(policySituation);
}
}
}
public static void processRuleWithException(EvaluatedPolicyRule rule, EvaluatedPolicyRuleTrigger trigger,
Collection<String> policySituations, PolicyExceptionType policyException) {
LOGGER.debug("Policy rule {} would be triggered, but there is an exception for it. Not trigerring", rule==null?null:rule.getName());
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Policy rule {} would be triggered, but there is an exception for it:\nTrigger:\n{}\nException:\n{}",
rule==null?null:rule.getName(), trigger.debugDump(1), policyException);
}
if (rule == null) {
return;
}
((EvaluatedPolicyRuleImpl)rule).addPolicyException(policyException);
}
public static void partialExecute(String componentName, ProjectorComponentRunnable runnable, Supplier<PartialProcessingTypeType> optionSupplier)
throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException,
PolicyViolationException, ExpressionEvaluationException, ObjectAlreadyExistsException {
partialExecute(componentName, runnable, optionSupplier, null);
}
public static void partialExecute(String componentName, ProjectorComponentRunnable runnable,
Supplier<PartialProcessingTypeType> optionSupplier, OperationResult result)
throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException,
PolicyViolationException, ExpressionEvaluationException, ObjectAlreadyExistsException {
PartialProcessingTypeType option = optionSupplier.get();
if (option == PartialProcessingTypeType.SKIP) {
LOGGER.debug("Skipping projector component {} because partial execution option is set to {}", componentName, option);
} else {
LOGGER.trace("Projector component started: {}", componentName);
try {
runnable.run();
LOGGER.trace("Projector component finished: {}", componentName);
} catch (SchemaException | ObjectNotFoundException | CommunicationException | ConfigurationException | SecurityViolationException
| PolicyViolationException | ExpressionEvaluationException | ObjectAlreadyExistsException | RuntimeException | Error e) {
LOGGER.trace("Projector component error: {}: {}: {}", componentName, e.getClass().getSimpleName(), e.getMessage());
if (result != null) {
result.recordFatalError(e);
}
throw e;
}
}
}
public static void checkMaxIterations(int iteration, int maxIterations, String conflictMessage, String humanReadableName)
throws ObjectAlreadyExistsException {
if (iteration > maxIterations) {
StringBuilder sb = new StringBuilder();
if (iteration == 1) {
sb.append("Error processing ");
} else {
sb.append("Too many iterations (").append(iteration).append(") for ");
}
sb.append(humanReadableName);
if (iteration == 1) {
sb.append(": constraint violation: ");
} else {
sb.append(": cannot determine values that satisfy constraints: ");
}
if (conflictMessage != null) {
sb.append(conflictMessage);
}
throw new ObjectAlreadyExistsException(sb.toString());
}
}
public static boolean needsFullShadowForCredentialProcessing(LensProjectionContext projCtx) throws SchemaException {
RefinedObjectClassDefinition refinedProjDef = projCtx.getStructuralObjectClassDefinition();
if (refinedProjDef == null) {
return false;
}
List<MappingType> outboundMappingType = refinedProjDef.getPasswordOutbound();
if (outboundMappingType == null) {
return false;
}
for (MappingType mappingType: outboundMappingType) {
if (mappingType.getStrength() == MappingStrengthType.STRONG || mappingType.getStrength() == MappingStrengthType.WEAK) {
return true;
}
}
return false;
}
public static boolean isPasswordReturnedByDefault(LensProjectionContext projCtx) {
CredentialsCapabilityType credentialsCapabilityType = ResourceTypeUtil.getEffectiveCapability(projCtx.getResource(), CredentialsCapabilityType.class);
return CapabilityUtil.isPasswordReturnedByDefault(credentialsCapabilityType);
}
}