use of com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType in project midpoint by Evolveum.
the class ProjectionValuesProcessor method processProjections.
private <F extends FocusType> void processProjections(LensContext<F> context, LensProjectionContext projContext, String activityDescription, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, ObjectAlreadyExistsException, CommunicationException, ConfigurationException, SecurityViolationException, PolicyViolationException {
checkSchemaAndPolicies(context, projContext, activityDescription, result);
SynchronizationPolicyDecision policyDecision = projContext.getSynchronizationPolicyDecision();
if (policyDecision != null && policyDecision == SynchronizationPolicyDecision.UNLINK) {
// we cannot skip deleted accounts here as the delete delta will be skipped as well
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Skipping processing of value for {} because the decision is {}", projContext.getHumanReadableName(), policyDecision);
}
return;
}
if (consistencyChecks)
context.checkConsistence();
if (!projContext.hasFullShadow() && hasIterationExpression(projContext)) {
contextLoader.loadFullShadow(context, projContext, "iteration expression", task, result);
if (projContext.getSynchronizationPolicyDecision() == SynchronizationPolicyDecision.BROKEN) {
return;
}
}
int maxIterations = determineMaxIterations(projContext);
int iteration = 0;
String iterationToken = null;
boolean wasResetIterationCounter = false;
PrismObject<ShadowType> shadowCurrent = projContext.getObjectCurrent();
if (shadowCurrent != null) {
Integer shadowIteration = shadowCurrent.asObjectable().getIteration();
if (shadowIteration != null) {
iteration = shadowIteration;
}
iterationToken = shadowCurrent.asObjectable().getIterationToken();
}
boolean skipUniquenessCheck = false;
while (true) {
projContext.setIteration(iteration);
if (iterationToken == null) {
iterationToken = formatIterationToken(context, projContext, iteration, task, result);
}
projContext.setIterationToken(iterationToken);
String conflictMessage;
// These are normally null. But there may be leftover from the previous iteration.
// While that should not affect the algorithm (it should overwrite it) it may confuse
// people during debugging and unecessarily clutter the debug output.
projContext.setOutboundConstruction(null);
projContext.setSqueezedAttributes(null);
projContext.setSqueezedAssociations(null);
LOGGER.trace("Projection values iteration {}, token '{}' for {}", iteration, iterationToken, projContext.getHumanReadableName());
if (!evaluateIterationCondition(context, projContext, iteration, iterationToken, true, task, result)) {
conflictMessage = "pre-iteration condition was false";
LOGGER.debug("Skipping iteration {}, token '{}' for {} because the pre-iteration condition was false", iteration, iterationToken, projContext.getHumanReadableName());
} else {
if (consistencyChecks)
context.checkConsistence();
// Re-evaluates the values in the account constructions (including roles)
assignmentProcessor.processAssignmentsAccountValues(projContext, result);
context.recompute();
if (consistencyChecks)
context.checkConsistence();
// LensUtil.traceContext(LOGGER, activityDescription, "values (assignment account values)", false, context, true);
// Evaluates the values in outbound mappings
outboundProcessor.processOutbound(context, projContext, task, result);
context.recompute();
if (consistencyChecks)
context.checkConsistence();
// LensUtil.traceContext(LOGGER, activityDescription, "values (outbound)", false, context, true);
// Merges the values together, processing exclusions and strong/weak mappings are needed
consolidationProcessor.consolidateValues(context, projContext, task, result);
if (consistencyChecks)
context.checkConsistence();
context.recompute();
if (consistencyChecks)
context.checkConsistence();
// Aux object classes may have changed during consolidation. Make sure we have up-to-date definitions.
context.refreshAuxiliaryObjectClassDefinitions();
// but I don't see any easier way to do it now.
if (iteration != 0 && !wasResetIterationCounter && willResetIterationCounter(projContext)) {
wasResetIterationCounter = true;
iteration = 0;
iterationToken = null;
cleanupContext(projContext);
LOGGER.trace("Resetting iteration counter and token because we have rename");
if (consistencyChecks)
context.checkConsistence();
continue;
}
if (policyDecision != null && policyDecision == SynchronizationPolicyDecision.DELETE) {
// No need to play the iterative game if the account is deleted
break;
}
// Check constraints
boolean conflict = true;
ShadowConstraintsChecker<F> checker = new ShadowConstraintsChecker<F>(projContext);
if (skipUniquenessCheck) {
skipUniquenessCheck = false;
conflict = false;
} else {
checker.setPrismContext(prismContext);
checker.setContext(context);
checker.setProvisioningService(provisioningService);
checker.check(task, result);
if (checker.isSatisfiesConstraints()) {
LOGGER.trace("Current shadow satisfies uniqueness constraints. Iteration {}, token '{}'", iteration, iterationToken);
conflict = false;
} else {
LOGGER.trace("Current shadow does not satisfy constraints. Conflicting shadow exists. Needed to found out what's wrong.");
if (checker.getConflictingShadow() != null) {
PrismObject<ShadowType> fullConflictingShadow = null;
try {
Collection<SelectorOptions<GetOperationOptions>> options = SelectorOptions.createCollection(GetOperationOptions.createPointInTimeType(PointInTimeType.FUTURE));
fullConflictingShadow = provisioningService.getObject(ShadowType.class, checker.getConflictingShadow().getOid(), options, task, result);
} catch (ObjectNotFoundException ex) {
//if object not found exception occurred, its ok..the account was deleted by the discovery, so there esits no more conflicting shadow
LOGGER.trace("Conflicting shadow was deleted by discovery. It does not exist anymore. Continue with adding current shadow.");
conflict = false;
}
result.computeStatus();
// the situation which happend
if (result.isError()) {
result.muteError();
}
if (conflict) {
PrismObject<F> focus = repositoryService.searchShadowOwner(checker.getConflictingShadow().getOid(), SelectorOptions.createCollection(GetOperationOptions.createAllowNotFound()), result);
//the owner of the shadow exist and it is a current user..so the shadow was successfully created, linked etc..no other recompute is needed..
if (focus != null && focus.getOid().equals(context.getFocusContext().getOid())) {
LOGGER.trace("Conflicting projection already linked to the current focus, no recompute needed, continue processing with conflicting projection.");
// accountContext.setSecondaryDelta(null);
cleanupContext(projContext);
projContext.setSynchronizationPolicyDecision(SynchronizationPolicyDecision.KEEP);
projContext.setObjectOld(fullConflictingShadow.clone());
projContext.setObjectCurrent(fullConflictingShadow);
projContext.setFullShadow(true);
ObjectDelta<ShadowType> secondaryDelta = projContext.getSecondaryDelta();
if (secondaryDelta != null && projContext.getOid() != null) {
secondaryDelta.setOid(projContext.getOid());
}
// result.computeStatus();
// // if the result is fatal error, it may mean that the
// // already exists expection occures before..but in this
// // scenario it means, the exception was handled and we
// // can mute the result to give better understanding of
// // the situation which happend
// if (result.isError()){
// result.muteError();
// }
// Re-do this same iteration again (do not increase iteration count).
// It will recompute the values and therefore enforce the user deltas and enable reconciliation
// to avoid endless loop
skipUniquenessCheck = true;
continue;
}
if (focus == null) {
LOGGER.trace("There is no owner linked with the conflicting projection.");
ResourceType resourceType = projContext.getResource();
if (ResourceTypeUtil.isSynchronizationOpportunistic(resourceType)) {
LOGGER.trace("Trying to find owner using correlation expression.");
boolean match = synchronizationService.matchUserCorrelationRule(fullConflictingShadow, context.getFocusContext().getObjectNew(), resourceType, context.getSystemConfiguration(), task, result);
if (match) {
if (projContext.getPrimaryDelta() != null && projContext.getPrimaryDelta().isAdd()) {
PrismObject<ShadowType> shadow = projContext.getPrimaryDelta().getObjectToAdd();
LOGGER.trace("Found primary ADD delta of shadow {}.", shadow);
LensProjectionContext conflictingAccountContext = context.findProjectionContext(projContext.getResourceShadowDiscriminator(), fullConflictingShadow.getOid());
if (conflictingAccountContext == null) {
conflictingAccountContext = LensUtil.createAccountContext(context, projContext.getResourceShadowDiscriminator());
// conflictingAccountContext = context.createProjectionContext(accountContext.getResourceShadowDiscriminator());
conflictingAccountContext.setOid(fullConflictingShadow.getOid());
conflictingAccountContext.setObjectOld(fullConflictingShadow.clone());
conflictingAccountContext.setObjectCurrent(fullConflictingShadow);
conflictingAccountContext.setFullShadow(true);
conflictingAccountContext.setSynchronizationPolicyDecision(SynchronizationPolicyDecision.KEEP);
conflictingAccountContext.setResource(projContext.getResource());
conflictingAccountContext.setDoReconciliation(true);
conflictingAccountContext.getDependencies().clear();
conflictingAccountContext.getDependencies().addAll(projContext.getDependencies());
conflictingAccountContext.setWave(projContext.getWave());
context.addConflictingProjectionContext(conflictingAccountContext);
}
projContext.setSynchronizationPolicyDecision(SynchronizationPolicyDecision.BROKEN);
result.recordFatalError("Could not add account " + projContext.getObjectNew() + ", because the account with the same identifier already exists on the resource. ");
LOGGER.error("Could not add account {}, because the account with the same identifier already exists on the resource. ", projContext.getObjectNew());
// to avoid endless loop
skipUniquenessCheck = true;
continue;
}
//found shadow belongs to the current user..need to link it and replace current shadow with the found shadow..
cleanupContext(projContext);
projContext.setObjectOld(fullConflictingShadow.clone());
projContext.setObjectCurrent(fullConflictingShadow);
projContext.setFullShadow(true);
projContext.setSynchronizationPolicyDecision(SynchronizationPolicyDecision.KEEP);
ObjectDelta<ShadowType> secondaryDelta = projContext.getSecondaryDelta();
if (secondaryDelta != null && projContext.getOid() != null) {
secondaryDelta.setOid(projContext.getOid());
}
LOGGER.trace("User {} satisfies correlation rules.", context.getFocusContext().getObjectNew());
// Re-do this same iteration again (do not increase iteration count).
// It will recompute the values and therefore enforce the user deltas and enable reconciliation
// to avoid endless loop
skipUniquenessCheck = true;
continue;
} else {
LOGGER.trace("User {} does not satisfy correlation rules.", context.getFocusContext().getObjectNew());
}
}
} else {
LOGGER.trace("Recomputing shadow identifier, because shadow with the some identifier exists and it belongs to other user.");
}
}
}
}
}
if (!conflict) {
if (evaluateIterationCondition(context, projContext, iteration, iterationToken, false, task, result)) {
// stop the iterations
break;
} else {
conflictMessage = "post-iteration condition was false";
LOGGER.debug("Skipping iteration {}, token '{}' for {} because the post-iteration condition was false", new Object[] { iteration, iterationToken, projContext.getHumanReadableName() });
}
} else {
conflictMessage = checker.getMessages();
}
}
iteration++;
iterationToken = null;
LensUtil.checkMaxIterations(iteration, maxIterations, conflictMessage, projContext.getHumanReadableName());
cleanupContext(projContext);
if (consistencyChecks)
context.checkConsistence();
}
addIterationTokenDeltas(projContext);
result.cleanupResult();
if (consistencyChecks)
context.checkConsistence();
}
use of com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType in project midpoint by Evolveum.
the class ProjectionValuesProcessor method checkSchemaAndPolicies.
/**
* Check that the primary deltas do not violate schema and policies
* TODO: implement schema check
*/
public <F extends ObjectType> void checkSchemaAndPolicies(LensContext<F> context, LensProjectionContext accountContext, String activityDescription, OperationResult result) throws SchemaException, PolicyViolationException {
ObjectDelta<ShadowType> primaryDelta = accountContext.getPrimaryDelta();
if (primaryDelta == null || primaryDelta.isDelete()) {
return;
}
RefinedObjectClassDefinition rAccountDef = accountContext.getCompositeObjectClassDefinition();
if (rAccountDef == null) {
throw new SchemaException("No definition for account type '" + accountContext.getResourceShadowDiscriminator() + "' in " + accountContext.getResource());
}
if (primaryDelta.isAdd()) {
PrismObject<ShadowType> accountToAdd = primaryDelta.getObjectToAdd();
ResourceAttributeContainer attributesContainer = ShadowUtil.getAttributesContainer(accountToAdd);
if (attributesContainer != null) {
for (ResourceAttribute<?> attribute : attributesContainer.getAttributes()) {
RefinedAttributeDefinition rAttrDef = rAccountDef.findAttributeDefinition(attribute.getElementName());
if (!rAttrDef.isTolerant()) {
throw new PolicyViolationException("Attempt to add object with non-tolerant attribute " + attribute.getElementName() + " in " + "account " + accountContext.getResourceShadowDiscriminator() + " during " + activityDescription);
}
}
}
} else if (primaryDelta.isModify()) {
for (ItemDelta<?, ?> modification : primaryDelta.getModifications()) {
if (modification.getParentPath().equivalent(SchemaConstants.PATH_ATTRIBUTES)) {
PropertyDelta<?> attrDelta = (PropertyDelta<?>) modification;
RefinedAttributeDefinition rAttrDef = rAccountDef.findAttributeDefinition(attrDelta.getElementName());
if (!rAttrDef.isTolerant()) {
throw new PolicyViolationException("Attempt to modify non-tolerant attribute " + attrDelta.getElementName() + " in " + "account " + accountContext.getResourceShadowDiscriminator() + " during " + activityDescription);
}
}
}
} else {
throw new IllegalStateException("Whoops!");
}
}
use of com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType in project midpoint by Evolveum.
the class ProjectionValuesProcessor method cleanupContext.
/**
* Remove the intermediate results of values processing such as secondary deltas.
*/
private void cleanupContext(LensProjectionContext accountContext) throws SchemaException {
// We must NOT clean up activation computation. This has happened before, it will not happen again
// and it does not depend on iteration
ObjectDelta<ShadowType> secondaryDelta = accountContext.getSecondaryDelta();
if (secondaryDelta != null) {
Collection<? extends ItemDelta> modifications = secondaryDelta.getModifications();
if (modifications != null) {
Iterator<? extends ItemDelta> iterator = modifications.iterator();
while (iterator.hasNext()) {
ItemDelta modification = iterator.next();
if (!new ItemPath(FocusType.F_ACTIVATION).equivalent(modification.getParentPath())) {
iterator.remove();
}
}
}
if (secondaryDelta.isEmpty()) {
accountContext.setSecondaryDelta(null);
}
}
accountContext.clearIntermediateResults();
accountContext.recompute();
}
use of com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType in project midpoint by Evolveum.
the class ReconciliationProcessor method getIdentifiersForAssociationTarget.
@NotNull
private ResourceAttributeContainer getIdentifiersForAssociationTarget(PrismContainerValue<ShadowAssociationType> isCValue, Task task, OperationResult result) throws CommunicationException, SchemaException, ConfigurationException, SecurityViolationException, ObjectNotFoundException, ExpressionEvaluationException {
ResourceAttributeContainer identifiersContainer = ShadowUtil.getAttributesContainer(isCValue, ShadowAssociationType.F_IDENTIFIERS);
if (identifiersContainer != null) {
return identifiersContainer;
}
String oid = isCValue.asContainerable().getShadowRef() != null ? isCValue.asContainerable().getShadowRef().getOid() : null;
if (oid == null) {
// TODO maybe warn/error log would suffice?
throw new IllegalStateException("Couldn't evaluate tolerant/intolerant values for association " + isCValue + ", because there are no identifiers and no shadow reference present");
}
PrismObject<ShadowType> target;
try {
GetOperationOptions rootOpt = GetOperationOptions.createPointInTimeType(PointInTimeType.FUTURE);
rootOpt.setNoFetch(true);
target = provisioningService.getObject(ShadowType.class, oid, SelectorOptions.createCollection(rootOpt), task, result);
} catch (ObjectNotFoundException e) {
// TODO maybe warn/error log would suffice (also for other exceptions?)
throw new ObjectNotFoundException("Couldn't evaluate tolerant/intolerant values for association " + isCValue + ", because the association target object does not exist: " + e.getMessage(), e);
}
identifiersContainer = ShadowUtil.getAttributesContainer(target);
if (identifiersContainer == null) {
// TODO maybe warn/error log would suffice?
throw new IllegalStateException("Couldn't evaluate tolerant/intolerant values for association " + isCValue + ", because there are no identifiers present, even in the repository object for association target");
}
return identifiersContainer;
}
use of com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType in project midpoint by Evolveum.
the class ReconciliationProcessor method reconcileProjectionAttribute.
private <T> void reconcileProjectionAttribute(QName attrName, LensProjectionContext projCtx, Map<QName, DeltaSetTriple<ItemValueWithOrigin<PrismPropertyValue<?>, PrismPropertyDefinition<?>>>> squeezedAttributes, RefinedObjectClassDefinition rOcDef, PrismObject<ShadowType> shadowNew, PrismContainer attributesContainer) throws SchemaException {
// LOGGER.trace("Attribute reconciliation processing attribute {}",attrName);
RefinedAttributeDefinition<T> attributeDefinition = projCtx.findAttributeDefinition(attrName);
if (attributeDefinition == null) {
String msg = "No definition for attribute " + attrName + " in " + projCtx.getResourceShadowDiscriminator();
throw new SchemaException(msg);
}
DeltaSetTriple<ItemValueWithOrigin<PrismPropertyValue<T>, PrismPropertyDefinition<T>>> pvwoTriple = squeezedAttributes != null ? (DeltaSetTriple) squeezedAttributes.get(attrName) : null;
if (attributeDefinition.isIgnored(LayerType.MODEL)) {
LOGGER.trace("Skipping reconciliation of attribute {} because it is ignored", attrName);
return;
}
PropertyLimitations limitations = attributeDefinition.getLimitations(LayerType.MODEL);
if (limitations != null) {
PropertyAccessType access = limitations.getAccess();
if (access != null) {
if (projCtx.isAdd() && (access.isAdd() == null || !access.isAdd())) {
LOGGER.trace("Skipping reconciliation of attribute {} because it is non-createable", attrName);
return;
}
if (projCtx.isModify() && (access.isModify() == null || !access.isModify())) {
LOGGER.trace("Skipping reconciliation of attribute {} because it is non-updateable", attrName);
return;
}
}
}
Collection<ItemValueWithOrigin<PrismPropertyValue<T>, PrismPropertyDefinition<T>>> shouldBePValues;
if (pvwoTriple == null) {
shouldBePValues = new HashSet<>();
} else {
shouldBePValues = new HashSet<>(pvwoTriple.getNonNegativeValues());
}
// We consider values explicitly requested by user to be among "should be values".
addPropValuesFromDelta(shouldBePValues, projCtx.getPrimaryDelta(), attrName);
// But we DO NOT take values from sync delta (because they just reflect what's on the resource),
// nor from secondary delta (because these got there from mappings).
boolean hasStrongShouldBePValue = false;
for (ItemValueWithOrigin<? extends PrismPropertyValue<T>, PrismPropertyDefinition<T>> shouldBePValue : shouldBePValues) {
if (shouldBePValue.getMapping() != null && shouldBePValue.getMapping().getStrength() == MappingStrengthType.STRONG) {
hasStrongShouldBePValue = true;
break;
}
}
PrismProperty<T> attribute = attributesContainer.findProperty(attrName);
Collection<PrismPropertyValue<T>> arePValues;
if (attribute != null) {
arePValues = attribute.getValues();
} else {
arePValues = new HashSet<>();
}
// Too loud :-)
// if (LOGGER.isTraceEnabled()) {
// StringBuilder sb = new StringBuilder();
// sb.append("Reconciliation\nATTR: ").append(PrettyPrinter.prettyPrint(attrName));
// sb.append("\n Should be:");
// for (ItemValueWithOrigin<?,?> shouldBePValue : shouldBePValues) {
// sb.append("\n ");
// sb.append(shouldBePValue.getItemValue());
// PrismValueDeltaSetTripleProducer<?, ?> shouldBeMapping = shouldBePValue.getMapping();
// if (shouldBeMapping.getStrength() == MappingStrengthType.STRONG) {
// sb.append(" STRONG");
// }
// if (shouldBeMapping.getStrength() == MappingStrengthType.WEAK) {
// sb.append(" WEAK");
// }
// if (!shouldBePValue.isValid()) {
// sb.append(" INVALID");
// }
// }
// sb.append("\n Is:");
// for (PrismPropertyValue<Object> isPVal : arePValues) {
// sb.append("\n ");
// sb.append(isPVal);
// }
// LOGGER.trace("{}", sb.toString());
// }
ValueMatcher<T> valueMatcher = ValueMatcher.createMatcher(attributeDefinition, matchingRuleRegistry);
boolean hasValue = false;
for (ItemValueWithOrigin<? extends PrismPropertyValue<T>, PrismPropertyDefinition<T>> shouldBePvwo : shouldBePValues) {
PrismValueDeltaSetTripleProducer<?, ?> shouldBeMapping = shouldBePvwo.getMapping();
if (shouldBeMapping == null) {
continue;
}
T shouldBeRealValue = shouldBePvwo.getItemValue().getValue();
if (shouldBeMapping.getStrength() != MappingStrengthType.STRONG && (!arePValues.isEmpty() || hasStrongShouldBePValue)) {
// weak or normal value and the attribute already has a
// value. Skip it.
// we cannot override it as it might have been legally
// changed directly on the projection resource object
LOGGER.trace("Skipping reconciliation of value {} of the attribute {}: the mapping is not strong", shouldBeRealValue, attributeDefinition.getName().getLocalPart());
continue;
}
if (!isInValues(valueMatcher, shouldBeRealValue, arePValues)) {
if (attributeDefinition.isSingleValue()) {
if (hasValue) {
throw new SchemaException("Attempt to set more than one value for single-valued attribute " + attrName + " in " + projCtx.getResourceShadowDiscriminator());
}
recordDelta(valueMatcher, projCtx, SchemaConstants.PATH_ATTRIBUTES, attributeDefinition, ModificationType.REPLACE, shouldBeRealValue, shouldBePvwo.getSource(), "it is given by a mapping");
} else {
recordDelta(valueMatcher, projCtx, SchemaConstants.PATH_ATTRIBUTES, attributeDefinition, ModificationType.ADD, shouldBeRealValue, shouldBePvwo.getSource(), "it is given by a mapping");
}
hasValue = true;
}
}
decideIfTolerate(projCtx, attributeDefinition, arePValues, shouldBePValues, valueMatcher);
}
Aggregations