diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/PolicyRuleTypeUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/PolicyRuleTypeUtil.java index 829d4679689..0911365bf7b 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/PolicyRuleTypeUtil.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/PolicyRuleTypeUtil.java @@ -85,6 +85,12 @@ public static String toShortString(PolicyRuleType rule) { } } + public static String toShortString(JAXBElement constraint) { + StringBuilder sb = new StringBuilder(); + toShortString(sb, constraint, ' '); + return sb.toString(); + } + public static String toShortString(PolicyConstraintsType constraints) { return toShortString(constraints, JOIN_AND); } @@ -96,34 +102,7 @@ public static String toShortString(PolicyConstraintsType constraints, char join) StringBuilder sb = new StringBuilder(); // we ignore refs to be able to dump even unresolved policy rules for (JAXBElement constraint : toConstraintsList(constraints, false, true)) { - QName name = constraint.getName(); - String abbreviation = CONSTRAINT_NAMES.get(name.getLocalPart()); - if (sb.length() > 0) { - sb.append(join); - } - if (QNameUtil.match(name, PolicyConstraintsType.F_AND)) { - sb.append('('); - sb.append(toShortString((PolicyConstraintsType) constraint.getValue(), JOIN_AND)); - sb.append(')'); - } else if (QNameUtil.match(name, PolicyConstraintsType.F_OR)) { - sb.append('('); - sb.append(toShortString((PolicyConstraintsType) constraint.getValue(), JOIN_OR)); - sb.append(')'); - } else if (QNameUtil.match(name, PolicyConstraintsType.F_NOT)) { - sb.append('('); - sb.append(toShortString((PolicyConstraintsType) constraint.getValue(), JOIN_AND)); - sb.append(')'); - } else if (QNameUtil.match(name, PolicyConstraintsType.F_TRANSITION)) { - TransitionPolicyConstraintType trans = (TransitionPolicyConstraintType) constraint.getValue(); - sb.append(SYMBOL_TRANSITION); - sb.append(toTransSymbol(trans.isStateBefore())); - sb.append(toTransSymbol(trans.isStateAfter())); - sb.append('('); - sb.append(toShortString(trans.getConstraints(), JOIN_AND)); - sb.append(')'); - } else { - sb.append(abbreviation != null ? abbreviation : name.getLocalPart()); - } + toShortString(sb, constraint, join); } for (PolicyConstraintReferenceType ref : constraints.getRef()) { if (sb.length() > 0) { @@ -134,6 +113,37 @@ public static String toShortString(PolicyConstraintsType constraints, char join) return sb.toString(); } + private static void toShortString(StringBuilder sb, JAXBElement constraint, char join) { + QName name = constraint.getName(); + String abbreviation = CONSTRAINT_NAMES.get(name.getLocalPart()); + if (sb.length() > 0) { + sb.append(join); + } + if (QNameUtil.match(name, PolicyConstraintsType.F_AND)) { + sb.append('('); + sb.append(toShortString((PolicyConstraintsType) constraint.getValue(), JOIN_AND)); + sb.append(')'); + } else if (QNameUtil.match(name, PolicyConstraintsType.F_OR)) { + sb.append('('); + sb.append(toShortString((PolicyConstraintsType) constraint.getValue(), JOIN_OR)); + sb.append(')'); + } else if (QNameUtil.match(name, PolicyConstraintsType.F_NOT)) { + sb.append('('); + sb.append(toShortString((PolicyConstraintsType) constraint.getValue(), JOIN_AND)); + sb.append(')'); + } else if (QNameUtil.match(name, PolicyConstraintsType.F_TRANSITION)) { + TransitionPolicyConstraintType trans = (TransitionPolicyConstraintType) constraint.getValue(); + sb.append(SYMBOL_TRANSITION); + sb.append(toTransSymbol(trans.isStateBefore())); + sb.append(toTransSymbol(trans.isStateAfter())); + sb.append('('); + sb.append(toShortString(trans.getConstraints(), JOIN_AND)); + sb.append(')'); + } else { + sb.append(abbreviation != null ? abbreviation : name.getLocalPart()); + } + } + private static String toTransSymbol(Boolean state) { if (state != null) { return state ? "1" : "0"; diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/DebugUtil.java b/infra/util/src/main/java/com/evolveum/midpoint/util/DebugUtil.java index e75d8a8dad2..440b9ada6e9 100644 --- a/infra/util/src/main/java/com/evolveum/midpoint/util/DebugUtil.java +++ b/infra/util/src/main/java/com/evolveum/midpoint/util/DebugUtil.java @@ -24,6 +24,8 @@ import javax.xml.namespace.QName; +import com.evolveum.midpoint.util.annotation.Experimental; + import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils.FieldCallback; @@ -768,4 +770,26 @@ public static void shortDumpCollectionPrettyPrintOptionalBrackets(StringBuil } sb.append("]"); } + + @Experimental + public static class LazilyDumpable { + private final Supplier supplier; + private String stringified; + + private LazilyDumpable(Supplier supplier) { + this.supplier = supplier; + } + + public static LazilyDumpable of(Supplier supplier) { + return new LazilyDumpable(supplier); + } + + @Override + public String toString() { + if (stringified == null) { + stringified = supplier.toString(); + } + return stringified; + } + } } diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/EvaluatedPolicyRule.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/EvaluatedPolicyRule.java index 6a379f34c98..24920c36df3 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/EvaluatedPolicyRule.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/EvaluatedPolicyRule.java @@ -38,6 +38,12 @@ default boolean isTriggered() { @NotNull Collection> getAllTriggers(); + /** + * Returns all triggers of given type, stepping down to situation policy rules and composite triggers. + * An exception are composite "not" triggers: it is usually of no use to collect negated triggers. + */ + > Collection getAllTriggers(Class type); + String getName(); PolicyRuleType getPolicyRule(); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluatedPolicyRuleImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluatedPolicyRuleImpl.java index d7ff06af3fb..1faef8f6c8f 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluatedPolicyRuleImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluatedPolicyRuleImpl.java @@ -23,7 +23,6 @@ import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.schema.util.PolicyRuleTypeUtil; import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.LocalizableMessage; import com.evolveum.midpoint.util.TreeNode; import com.evolveum.midpoint.util.exception.CommunicationException; @@ -47,6 +46,7 @@ import java.util.stream.Collectors; import static com.evolveum.midpoint.schema.constants.ExpressionConstants.VAR_RULE_EVALUATION_CONTEXT; +import static com.evolveum.midpoint.util.DebugUtil.*; import static com.evolveum.midpoint.xml.ns._public.common.common_3.TriggeredPolicyRulesStorageStrategyType.FULL; /** @@ -179,6 +179,31 @@ public Collection> getAllTriggers() { return rv; } + @Override + public > Collection getAllTriggers(Class type) { + List selectedTriggers = new ArrayList<>(); + collectTriggers(selectedTriggers, getAllTriggers(), type); + return selectedTriggers; + } + + private > void collectTriggers(Collection collected, + Collection> all, Class type) { + for (EvaluatedPolicyRuleTrigger trigger : all) { + if (type.isAssignableFrom(trigger.getClass())) { + //noinspection unchecked + collected.add((T) trigger); + } + if (trigger instanceof EvaluatedCompositeTrigger) { + EvaluatedCompositeTrigger compositeTrigger = (EvaluatedCompositeTrigger) trigger; + if (compositeTrigger.getConstraintKind() != PolicyConstraintKindType.NOT) { + collectTriggers(collected, compositeTrigger.getInnerTriggers(), type); + } else { + // there is no use in collecting "negated" triggers + } + } + } + } + void addTriggers(Collection> triggers) { this.triggers.addAll(triggers); } @@ -281,16 +306,16 @@ private String getSituationFromConstraints(PolicyConstraintsType policyConstrain @Override public String debugDump(int indent) { StringBuilder sb = new StringBuilder(); - DebugUtil.debugDumpLabelLn(sb, "EvaluatedPolicyRule " + (getName() != null ? getName() + " " : "") + "(triggers: " + triggers.size() + ")", indent); - DebugUtil.debugDumpWithLabelLn(sb, "name", getName(), indent + 1); - DebugUtil.debugDumpLabelLn(sb, "policyRuleType", indent + 1); - DebugUtil.indentDebugDump(sb, indent + 2); + debugDumpLabelLn(sb, "EvaluatedPolicyRule " + (getName() != null ? getName() + " " : "") + "(triggers: " + triggers.size() + ")", indent); + debugDumpWithLabelLn(sb, "name", getName(), indent + 1); + debugDumpLabelLn(sb, "policyRuleType", indent + 1); + indentDebugDump(sb, indent + 2); PrismPrettyPrinter.debugDumpValue(sb, indent + 2, policyRuleType, prismContextForDebugDump, PolicyRuleType.COMPLEX_TYPE, PrismContext.LANG_XML); sb.append('\n'); - DebugUtil.debugDumpWithLabelLn(sb, "assignmentPath", assignmentPath, indent + 1); - DebugUtil.debugDumpWithLabelLn(sb, "triggers", triggers, indent + 1); - DebugUtil.debugDumpWithLabelLn(sb, "directOwner", ObjectTypeUtil.toShortString(directOwner), indent + 1); - DebugUtil.debugDumpWithLabel(sb, "rootObjects", assignmentPath != null ? String.valueOf(assignmentPath.getFirstOrderChain()) : null, indent + 1); + debugDumpWithLabelLn(sb, "assignmentPath", assignmentPath, indent + 1); + debugDumpWithLabelLn(sb, "triggers", triggers, indent + 1); + debugDumpWithLabelLn(sb, "directOwner", ObjectTypeUtil.toShortString(directOwner), indent + 1); + debugDumpWithLabel(sb, "rootObjects", assignmentPath != null ? String.valueOf(assignmentPath.getFirstOrderChain()) : null, indent + 1); return sb.toString(); } @@ -465,7 +490,7 @@ public void computeEnabledActions(@Nullable Po LOGGER.trace("Accepting action {} ({}) because the condition evaluated to true", action.getName(), action.getClass().getSimpleName()); } } - LOGGER.trace("Adding action {} into the enabled action list.", action); + LOGGER.trace("Adding action {} ({}) into the enabled action list.", action, action.getClass().getSimpleName()); enabledActions.add(action); } enabledActionsComputed = true; diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/assignments/EvaluatedAssignmentTargetImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/assignments/EvaluatedAssignmentTargetImpl.java index c7d92242884..07bb0d022dc 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/assignments/EvaluatedAssignmentTargetImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/assignments/EvaluatedAssignmentTargetImpl.java @@ -26,7 +26,7 @@ public class EvaluatedAssignmentTargetImpl implements EvaluatedAssignmentTarget @NotNull private final PrismObject target; private final boolean evaluateConstructions; - @NotNull private final AssignmentPathImpl assignmentPath; // TODO reconsider (maybe we should store only some lightweight information here) + @NotNull private final AssignmentPathImpl assignmentPath; // TODO reconsider (maybe we should store only some lightweight information here) private final AssignmentType assignment; private Collection exclusions = null; private final boolean isValid; @@ -130,4 +130,11 @@ public String debugDump(int indent) { return sb.toString(); } + @Override + public String toString() { + return "EvaluatedAssignmentTargetImpl{" + + "target=" + target + + ", isValid=" + isValid + + '}'; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/AssignmentProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/AssignmentProcessor.java index b060eac4e81..3459aea68fd 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/AssignmentProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/AssignmentProcessor.java @@ -203,7 +203,7 @@ private void processAssig // PROCESSING POLICIES policyRuleProcessor.evaluateAssignmentPolicyRules(context, evaluatedAssignmentTriple, task, result); - boolean needToReevaluateAssignments = processPruning(context, evaluatedAssignmentTriple); + boolean needToReevaluateAssignments = processPruning(context, evaluatedAssignmentTriple, result); if (needToReevaluateAssignments) { LOGGER.debug("Re-evaluating assignments because exclusion pruning rule was triggered"); @@ -236,9 +236,9 @@ private void processAssig } private boolean processPruning(LensContext context, - DeltaSetTriple> evaluatedAssignmentTriple) throws SchemaException { + DeltaSetTriple> evaluatedAssignmentTriple, OperationResult result) throws SchemaException { PruningOperation pruningOperation = new PruningOperation<>(context, evaluatedAssignmentTriple, beans); - return pruningOperation.execute(); + return pruningOperation.execute(result); } private void processProjections(LensContext context, diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/PruningOperation.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/PruningOperation.java index 17f8806e036..09634dd4ffc 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/PruningOperation.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/PruningOperation.java @@ -17,6 +17,7 @@ import com.evolveum.midpoint.prism.PrismObjectDefinition; import com.evolveum.midpoint.prism.delta.ContainerDelta; import com.evolveum.midpoint.prism.delta.DeltaSetTriple; +import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.util.LocalizableMessageBuilder; import com.evolveum.midpoint.util.SingleLocalizableMessage; @@ -29,12 +30,14 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PrunePolicyActionType; -import java.util.ArrayList; +import java.util.Collection; public class PruningOperation { private static final Trace LOGGER = TraceManager.getTrace(PruningOperation.class); + private static final String OP_EXECUTE = PruningOperation.class.getName() + ".execute"; + private final LensContext context; private final DeltaSetTriple> evaluatedAssignmentTriple; private final ModelBeans beans; @@ -65,26 +68,39 @@ public PruningOperation(LensContext context, DeltaSetTriple existingOrNewAssignment: evaluatedAssignmentTriple.getNonNegativeValues()) { - // Note that we take assignments on "being added" condition i.e. ones which are added since objectOld. - // Taking simple "plus set" is not sufficient because of situations after wave 0 when all assignments - // look like being in zero set. - if (existingOrNewAssignment.getOrigin().isBeingAdded()) { - pruneNewAssignment(existingOrNewAssignment); + public boolean execute(OperationResult parentResult) throws SchemaException { + OperationResult result = parentResult.createMinorSubresult(OP_EXECUTE); + try { + for (EvaluatedAssignmentImpl existingOrNewAssignment : evaluatedAssignmentTriple.getNonNegativeValues()) { + // Note that we take assignments on "being added" condition i.e. ones which are added since objectOld. + // Taking simple "plus set" is not sufficient because of situations after wave 0 when all assignments + // look like being in zero set. + if (existingOrNewAssignment.getOrigin().isBeingAdded()) { + pruneNewAssignment(existingOrNewAssignment); + } } - } - return prunedViaSecondaryDelta && !enforcementOverrideGenerated; + return prunedViaSecondaryDelta && !enforcementOverrideGenerated; + } catch (Throwable t) { + result.recordFatalError(t); + throw t; + } finally { + result.computeStatusIfUnknown(); + } } private void pruneNewAssignment(EvaluatedAssignmentImpl newAssignment) { - newAssignment.getAllTargetsPolicyRules().stream() - .filter(rule -> rule.containsEnabledAction(PrunePolicyActionType.class)) - .forEach(rule -> new ArrayList<>(rule.getTriggers()).stream() // cloning the list because it is modified inside - .filter(trigger -> trigger instanceof EvaluatedExclusionTrigger) - .map(trigger -> (EvaluatedExclusionTrigger) trigger) - .forEach(exclusionTrigger -> processPruneRuleExclusionTrigger(newAssignment, rule, exclusionTrigger))); + LOGGER.trace("Pruning new assignment: {}", newAssignment); + for (EvaluatedPolicyRuleImpl newAssignmentRule : newAssignment.getAllTargetsPolicyRules()) { + if (newAssignmentRule.containsEnabledAction(PrunePolicyActionType.class)) { + Collection exclusionTriggers = + newAssignmentRule.getAllTriggers(EvaluatedExclusionTrigger.class); + LOGGER.trace("Exclusion triggers: {}", exclusionTriggers); + for (EvaluatedExclusionTrigger exclusionTrigger : exclusionTriggers) { + processPruneRuleExclusionTrigger(newAssignment, newAssignmentRule, exclusionTrigger); + } + } + } } private void processPruneRuleExclusionTrigger(EvaluatedAssignmentImpl newAssignment, EvaluatedPolicyRuleImpl pruneRule, diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/AssignmentPolicyRuleEvaluationContext.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/AssignmentPolicyRuleEvaluationContext.java index f78b9642071..04a7575e847 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/AssignmentPolicyRuleEvaluationContext.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/AssignmentPolicyRuleEvaluationContext.java @@ -11,6 +11,7 @@ import com.evolveum.midpoint.model.api.context.EvaluatedPolicyRuleTrigger; import com.evolveum.midpoint.model.impl.lens.assignments.EvaluatedAssignmentImpl; import com.evolveum.midpoint.model.impl.lens.LensContext; +import com.evolveum.midpoint.model.impl.lens.projector.AssignmentOrigin; import com.evolveum.midpoint.prism.delta.DeltaSetTriple; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.task.api.Task; @@ -22,38 +23,37 @@ public class AssignmentPolicyRuleEvaluationContext extends PolicyRuleEvaluationContext { @NotNull public final EvaluatedAssignmentImpl evaluatedAssignment; - public final boolean inPlus; - public final boolean inZero; - public final boolean inMinus; + public final boolean isAdded; + public final boolean isKept; + public final boolean isDeleted; public final boolean isDirect; public final DeltaSetTriple> evaluatedAssignmentTriple; AssignmentPolicyRuleEvaluationContext(@NotNull EvaluatedPolicyRule policyRule, - @NotNull EvaluatedAssignmentImpl evaluatedAssignment, boolean inPlus, boolean inZero, - boolean inMinus, boolean isDirect, LensContext context, + @NotNull EvaluatedAssignmentImpl evaluatedAssignment, boolean isDirect, LensContext context, DeltaSetTriple> evaluatedAssignmentTriple, Task task, RulesEvaluationContext globalCtx) { - this(policyRule, evaluatedAssignment, inPlus, inZero, inMinus, isDirect, context, evaluatedAssignmentTriple, + this(policyRule, evaluatedAssignment, isDirect, context, evaluatedAssignmentTriple, task, ObjectState.AFTER, globalCtx); } private AssignmentPolicyRuleEvaluationContext(@NotNull EvaluatedPolicyRule policyRule, - @NotNull EvaluatedAssignmentImpl evaluatedAssignment, boolean inPlus, boolean inZero, - boolean inMinus, boolean isDirect, LensContext context, + @NotNull EvaluatedAssignmentImpl evaluatedAssignment, boolean isDirect, LensContext context, DeltaSetTriple> evaluatedAssignmentTriple, Task task, ObjectState state, RulesEvaluationContext globalCtx) { super(policyRule, context, task, globalCtx, state); this.evaluatedAssignment = evaluatedAssignment; - this.inPlus = inPlus; - this.inZero = inZero; - this.inMinus = inMinus; + AssignmentOrigin origin = evaluatedAssignment.getOrigin(); + this.isAdded = origin.isBeingAdded(); + this.isKept = origin.isBeingKept(); + this.isDeleted = origin.isBeingDeleted(); this.isDirect = isDirect; this.evaluatedAssignmentTriple = evaluatedAssignmentTriple; } @Override public AssignmentPolicyRuleEvaluationContext cloneWithStateConstraints(ObjectState state) { - return new AssignmentPolicyRuleEvaluationContext<>(policyRule, evaluatedAssignment, inPlus, inZero, inMinus, isDirect, lensContext, evaluatedAssignmentTriple, task, state, + return new AssignmentPolicyRuleEvaluationContext<>(policyRule, evaluatedAssignment, isDirect, lensContext, evaluatedAssignmentTriple, task, state, globalCtx); } @@ -69,18 +69,18 @@ public boolean isApplicableToState() { private boolean isAssignmentApplicable() { if (state == ObjectState.BEFORE) { - return inMinus || inZero; + return isDeleted || isKept; } else { - return inZero || inPlus; + return isKept || isAdded; } } @Override public String getShortDescription() { return evaluatedAssignment.getTarget() + " (" + - (inPlus ? "+":"") + - (inMinus ? "-":"") + - (inZero ? "0":"") + + (isAdded ? "+":"") + + (isDeleted ? "-":"") + + (isKept ? "0":"") + ") " + (isDirect ? "directly":"indirectly") + " in " + ObjectTypeUtil.toShortString(focusContext.getObjectAny()) + " / " + state; @@ -89,7 +89,7 @@ public String getShortDescription() { @SuppressWarnings("MethodDoesntCallSuperMethod") @Override public AssignmentPolicyRuleEvaluationContext clone() { - return new AssignmentPolicyRuleEvaluationContext<>(policyRule, evaluatedAssignment, inPlus, inZero, inMinus, + return new AssignmentPolicyRuleEvaluationContext<>(policyRule, evaluatedAssignment, isDirect, lensContext, evaluatedAssignmentTriple, task, globalCtx); } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/PolicyRuleProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/PolicyRuleProcessor.java index 4146632aa9f..a3a0ee579d8 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/PolicyRuleProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/PolicyRuleProcessor.java @@ -13,7 +13,6 @@ import com.evolveum.midpoint.model.impl.lens.*; import com.evolveum.midpoint.model.impl.lens.assignments.EvaluatedAssignmentImpl; import com.evolveum.midpoint.model.impl.lens.assignments.EvaluatedAssignmentTargetImpl; -import com.evolveum.midpoint.model.impl.lens.projector.AssignmentOrigin; import com.evolveum.midpoint.model.impl.lens.projector.ProjectorProcessor; import com.evolveum.midpoint.model.impl.lens.projector.util.ProcessorExecution; import com.evolveum.midpoint.model.impl.lens.projector.mappings.MappingEvaluator; @@ -92,23 +91,11 @@ public class PolicyRuleProcessor implements ProjectorProcessor { public void evaluateAssignmentPolicyRules(LensContext context, DeltaSetTriple> evaluatedAssignmentTriple, Task task, OperationResult result) - throws PolicyViolationException, SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { + throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { for (EvaluatedAssignmentImpl evaluatedAssignment : evaluatedAssignmentTriple.union()) { RulesEvaluationContext globalCtx = new RulesEvaluationContext(); - /* - * We need to determine if the assignment is being added, deleted or simply modified. This is mainly - * to decide with regard of "operation" qualifier on Assignment modification constraint. - * - * The code below should be relatively OK, even when regarding waves greater than zero. - * The only thing to reconsider is what if the assignment is being replaced? - */ - AssignmentOrigin origin = evaluatedAssignment.getOrigin(); - boolean inPlus = origin.isBeingAdded(); - boolean inMinus = origin.isBeingDeleted(); - boolean inZero = origin.isBeingKept(); - resolveReferences(evaluatedAssignment.getAllTargetsPolicyRules(), getAllGlobalRules(context)); /* @@ -123,7 +110,7 @@ public void evaluateAssignmentPolicyRules(LensC if (!hasSituationConstraint(policyRule)) { if (checkApplicabilityToAssignment(policyRule)) { evaluateRule(new AssignmentPolicyRuleEvaluationContext<>(policyRule, - evaluatedAssignment, inPlus, inZero, inMinus, true, context, + evaluatedAssignment, true, context, evaluatedAssignmentTriple, task, globalCtx), result); } } @@ -132,7 +119,7 @@ public void evaluateAssignmentPolicyRules(LensC if (!hasSituationConstraint(policyRule)) { if (checkApplicabilityToAssignment(policyRule)) { evaluateRule(new AssignmentPolicyRuleEvaluationContext<>(policyRule, - evaluatedAssignment, inPlus, inZero, inMinus, false, context, + evaluatedAssignment, false, context, evaluatedAssignmentTriple, task, globalCtx), result); } } @@ -141,7 +128,7 @@ public void evaluateAssignmentPolicyRules(LensC if (hasSituationConstraint(policyRule)) { if (checkApplicabilityToAssignment(policyRule)) { evaluateRule(new AssignmentPolicyRuleEvaluationContext<>(policyRule, - evaluatedAssignment, inPlus, inZero, inMinus, true, context, + evaluatedAssignment, true, context, evaluatedAssignmentTriple, task, globalCtx), result); } } @@ -150,13 +137,13 @@ public void evaluateAssignmentPolicyRules(LensC if (hasSituationConstraint(policyRule)) { if (checkApplicabilityToAssignment(policyRule)) { evaluateRule(new AssignmentPolicyRuleEvaluationContext<>(policyRule, - evaluatedAssignment, inPlus, inZero, inMinus, false, context, + evaluatedAssignment, false, context, evaluatedAssignmentTriple, task, globalCtx), result); } } } // a bit of hack, but hopefully it will work - PlusMinusZero mode = inMinus ? PlusMinusZero.MINUS : evaluatedAssignment.getMode(); + PlusMinusZero mode = evaluatedAssignment.getOrigin().isBeingDeleted() ? PlusMinusZero.MINUS : evaluatedAssignment.getMode(); policyStateRecorder.applyAssignmentState(context, evaluatedAssignment, mode, globalCtx.rulesToRecord); } @@ -171,8 +158,8 @@ private boolean checkApplicabilityToAssignment(EvaluatedPolicyRule policyRule) { } } - private void resolveReferences(Collection evaluatedRules, Collection otherRules) - throws SchemaException, ObjectNotFoundException { + private void resolveReferences(Collection evaluatedRules, + Collection otherRules) { List rules = evaluatedRules.stream().map(EvaluatedPolicyRule::getPolicyRule).collect(Collectors.toList()); PolicyRuleTypeUtil.resolveReferences(rules, otherRules, prismContext); } @@ -235,8 +222,8 @@ private void resolveReferences(Collection evaluat //region ------------------------------------------------------------------ Focus policy rules @ProcessorMethod public void evaluateObjectPolicyRules(LensContext context, - XMLGregorianCalendar now, Task task, OperationResult result) - throws PolicyViolationException, SchemaException, ExpressionEvaluationException, ObjectNotFoundException, SecurityViolationException, ConfigurationException, CommunicationException { + @SuppressWarnings("unused") XMLGregorianCalendar now, Task task, OperationResult result) + throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, SecurityViolationException, ConfigurationException, CommunicationException { RulesEvaluationContext globalCtx = new RulesEvaluationContext(); List rules = new ArrayList<>(); @@ -294,7 +281,7 @@ private void collectFocusRulesFromAssignments( private void collectGlobalObjectRules(List rules, LensContext context, Task task, OperationResult result) - throws SchemaException, PolicyViolationException, ExpressionEvaluationException, ObjectNotFoundException, SecurityViolationException, ConfigurationException, CommunicationException { + throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, SecurityViolationException, ConfigurationException, CommunicationException { PrismObject systemConfiguration = context.getSystemConfiguration(); if (systemConfiguration == null) { return; diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/evaluators/AssignmentModificationConstraintEvaluator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/evaluators/AssignmentModificationConstraintEvaluator.java index e9b98e5c353..380a3e51ec2 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/evaluators/AssignmentModificationConstraintEvaluator.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/evaluators/AssignmentModificationConstraintEvaluator.java @@ -70,7 +70,7 @@ public EvaluatedModificationTrigger evaluate( return null; } AssignmentModificationPolicyConstraintType constraint = constraintElement.getValue(); - if (!operationMatches(constraint, ctx.inPlus, ctx.inZero, ctx.inMinus) || + if (!operationMatches(constraint, ctx.isAdded, ctx.isKept, ctx.isDeleted) || !relationMatches(constraint, ctx) || !pathsMatch(constraint, ctx) || !expressionPasses(constraintElement, ctx, result)) { @@ -108,9 +108,9 @@ private LocalizableMessage createMessage(JAXBE @NotNull private String createOperationKey(AssignmentPolicyRuleEvaluationContext ctx) { - if (ctx.inPlus) { + if (ctx.isAdded) { return "Added"; - } else if (ctx.inMinus) { + } else if (ctx.isDeleted) { return "Deleted"; } else { return "Modified"; @@ -179,8 +179,8 @@ private boolean pathsMatch(AssignmentModificat // hope this is correctly filled in if (constraint.getItem().isEmpty()) { - if (ctx.inPlus || ctx.inMinus) { - LOGGER.trace("pathsMatch: returns true because no items are configured and inPlus||inMinus"); + if (ctx.isAdded || ctx.isDeleted) { + LOGGER.trace("pathsMatch: returns true because no items are configured and isAdded||isDeleted"); return true; } else { Collection> subItemDeltas = ctx.evaluatedAssignment.getAssignmentIdi().getSubItemDeltas(); @@ -191,16 +191,16 @@ private boolean pathsMatch(AssignmentModificat } else { for (ItemPathType path : constraint.getItem()) { ItemPath itemPath = prismContext.toPath(path); - if (ctx.inPlus && pathDoesNotMatch(ctx.evaluatedAssignment.getAssignmentType(false), itemPath)) { - LOGGER.trace("pathsMatch: returns false because inPlus and path {} does not match new assignment", itemPath); + if (ctx.isAdded && pathDoesNotMatch(ctx.evaluatedAssignment.getAssignmentType(false), itemPath)) { + LOGGER.trace("pathsMatch: returns false because isAdded and path {} does not match new assignment", itemPath); return false; - } else if (ctx.inMinus && pathDoesNotMatch(ctx.evaluatedAssignment.getAssignmentType(true), itemPath)) { - LOGGER.trace("pathsMatch: returns false because inMinus and path {} does not match old assignment", itemPath); + } else if (ctx.isDeleted && pathDoesNotMatch(ctx.evaluatedAssignment.getAssignmentType(true), itemPath)) { + LOGGER.trace("pathsMatch: returns false because isDeleted and path {} does not match old assignment", itemPath); return false; } else { Collection> subItemDeltas = ctx.evaluatedAssignment.getAssignmentIdi().getSubItemDeltas(); - if (ctx.inZero && pathDoesNotMatch(subItemDeltas, itemPath, exactMatch)) { - LOGGER.trace("pathsMatch: returns false because inZero and path {} does not match new assignment; " + if (ctx.isKept && pathDoesNotMatch(subItemDeltas, itemPath, exactMatch)) { + LOGGER.trace("pathsMatch: returns false because isKept and path {} does not match new assignment; " + "exact={}; sub item deltas={}", itemPath, exactMatch, subItemDeltas); return false; } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/evaluators/ExclusionConstraintEvaluator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/evaluators/ExclusionConstraintEvaluator.java index 83b726343bf..d1820dfc4d9 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/evaluators/ExclusionConstraintEvaluator.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/evaluators/ExclusionConstraintEvaluator.java @@ -8,7 +8,20 @@ package com.evolveum.midpoint.model.impl.lens.projector.policy.evaluators; -import com.evolveum.midpoint.common.LocalizationService; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.xml.bind.JAXBElement; +import javax.xml.namespace.QName; + +import com.evolveum.midpoint.model.api.context.EvaluationOrder; +import com.evolveum.midpoint.schema.util.PolicyRuleTypeUtil; + +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.model.api.context.AssignmentPath; import com.evolveum.midpoint.model.api.context.EvaluatedExclusionTrigger; import com.evolveum.midpoint.model.api.context.EvaluatedPolicyRule; @@ -29,26 +42,22 @@ import com.evolveum.midpoint.util.LocalizableMessage; import com.evolveum.midpoint.util.LocalizableMessageBuilder; import com.evolveum.midpoint.util.exception.*; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; import com.evolveum.prism.xml.ns._public.types_3.EvaluationTimeType; -import org.jetbrains.annotations.NotNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import javax.xml.bind.JAXBElement; -import javax.xml.namespace.QName; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; +import static com.evolveum.midpoint.util.DebugUtil.lazy; /** - * @author semancik - * @author mederly + * Evaluates exclusion policy constraints. */ @Component public class ExclusionConstraintEvaluator implements PolicyConstraintEvaluator { + private static final Trace LOGGER = TraceManager.getTrace(ExclusionConstraintEvaluator.class); + private static final String OP_EVALUATE = ExclusionConstraintEvaluator.class.getName() + ".evaluate"; private static final String CONSTRAINT_KEY = "exclusion"; @@ -57,7 +66,6 @@ public class ExclusionConstraintEvaluator implements PolicyConstraintEvaluator EvaluatedExclusionTrigger evaluate(@NotNull JAXBElement constraint, @@ -67,64 +75,52 @@ public EvaluatedExclusionTrigger evaluate(@Not .setMinor() .build(); try { + LOGGER.trace("Evaluating exclusion constraint {} on {}", + lazy(() -> PolicyRuleTypeUtil.toShortString(constraint)), rctx); if (!(rctx instanceof AssignmentPolicyRuleEvaluationContext)) { return null; } + AssignmentPolicyRuleEvaluationContext ctx = (AssignmentPolicyRuleEvaluationContext) rctx; - if (!ctx.inPlus && !ctx.inZero) { + if (!ctx.isAdded && !ctx.isKept) { + LOGGER.trace("Assignment not being added nor kept, skipping evaluation."); return null; } - List sourceOrderConstraints = defaultIfEmpty(constraint.getValue().getOrderConstraint()); - List targetOrderConstraints = defaultIfEmpty(constraint.getValue().getTargetOrderConstraint()); - if (ctx.policyRule.isGlobal()) { - if (!pathMatches(ctx.policyRule.getAssignmentPath(), sourceOrderConstraints)) { - System.out.println("[global] Source assignment path does not match: " + ctx.policyRule.getAssignmentPath()); - return null; - } - } else { - // It is not clear how to match orderConstraint with assignment path of the constraint. - // Let us try the following test: we consider it matching if there's at least one segment - // on the path that matches the constraint. - boolean found = ctx.policyRule.getAssignmentPath().getSegments().stream() - .anyMatch(segment -> segment.matches(sourceOrderConstraints)); - if (!found) { - // System.out.println("Source assignment path does not match: constraints=" + sourceOrderConstraints + ", whole path=" + ctx.policyRule.getAssignmentPath()); - return null; - } + if (sourceOrderConstraintsDoNotMatch(constraint, ctx)) { + // logged in the called method body + return null; } - // We consider all policy rules, i.e. also from induced targets. (It is not possible to collect local - // rules for individual targets in the chain - rules are computed only for directly evaluated assignments.) - - // // In order to avoid false positives, we consider all targets from the current assignment as "allowed" - // Set allowedTargetOids = ctx.evaluatedAssignment.getNonNegativeTargets().stream() - // .filter(t -> t.appliesToFocus()) - // .map(t -> t.getOid()) - // .collect(Collectors.toSet()); + /* + * Now let us check the exclusions. + * + * Assignment A is the current evaluated assignment. It has directly or indirectly attached the exclusion policy rule. + * We now go through all other assignments B and check the exclusions. + */ + List targetOrderConstraints = defaultIfEmpty(constraint.getValue().getTargetOrderConstraint()); List nonNegativeTargetsA = ctx.evaluatedAssignment.getNonNegativeTargets(); for (EvaluatedAssignmentImpl assignmentB : ctx.evaluatedAssignmentTriple.getNonNegativeValues()) { - if (assignmentB.equals(ctx.evaluatedAssignment)) { // TODO (value instead of reference equality?) + if (assignmentB == ctx.evaluatedAssignment) { // currently there is no other way of comparing the evaluated assignments continue; } targetB: for (EvaluatedAssignmentTargetImpl targetB : assignmentB.getNonNegativeTargets()) { if (!pathMatches(targetB.getAssignmentPath(), targetOrderConstraints)) { - // System.out.println("Target assignment path does not match: constraints=" + targetOrderConstraints + ", whole path=" + targetB.getAssignmentPath()); + LOGGER.trace("Skipping considering exclusion target {} because it does not match target path constraints." + + " Path={}, constraints={}", targetB, targetB.getAssignmentPath(), targetOrderConstraints); continue; } if (!oidMatches(constraint.getValue().getTargetRef(), targetB, prismContext, matchingRuleRegistry, "exclusion constraint")) { + LOGGER.trace("Target {} OID does not match exclusion filter", targetB); continue; } // To avoid false positives let us check if this target is not already covered by assignment being evaluated - // (is this really needed?) for (EvaluatedAssignmentTargetImpl targetA : nonNegativeTargetsA) { - if (targetA.appliesToFocusWithAnyRelation(relationRegistry) - && targetA.getOid() != null && targetA.getOid().equals(targetB.getOid()) - && targetA.getAssignmentPath().equivalent(targetB.getAssignmentPath())) { + if (targetIsAlreadyCovered(targetB, targetA)) { continue targetB; } } @@ -143,13 +139,74 @@ public EvaluatedExclusionTrigger evaluate(@Not } } + private boolean targetIsAlreadyCovered(EvaluatedAssignmentTargetImpl targetB, EvaluatedAssignmentTargetImpl targetA) { + if (!targetA.appliesToFocus()) { + return false; + } + if (targetA.getOid() == null || !targetA.getOid().equals(targetB.getOid())) { + return false; + } + EvaluationOrder orderA = targetA.getAssignmentPath().last().getEvaluationOrder(); + EvaluationOrder orderB = targetB.getAssignmentPath().last().getEvaluationOrder(); + if (ordersAreCompatible(orderA, orderB)) { + LOGGER.trace("Target {} is covered by the assignment considered, skipping", targetB); + return true; + } else { + LOGGER.trace("Target B of {} is covered by assignment A considered, but with different order (A={} vs B={})." + + " Not skipping.", targetB, orderA, orderB); + return false; + } + } + + private boolean ordersAreCompatible(EvaluationOrder orderA, EvaluationOrder orderB) { + Map diff = orderA.diff(orderB); + for (Map.Entry diffEntry : diff.entrySet()) { + QName relation = diffEntry.getKey(); + if (relationRegistry.isDelegation(relation)) { + continue; + } + if (diffEntry.getValue() == 0) { + continue; + } + LOGGER.trace("Difference in relation: {}", diffEntry); + return false; + } + return true; + } + + private boolean sourceOrderConstraintsDoNotMatch( + @NotNull JAXBElement constraint, AssignmentPolicyRuleEvaluationContext ctx) { + List sourceOrderConstraints = defaultIfEmpty(constraint.getValue().getOrderConstraint()); + if (ctx.policyRule.isGlobal()) { + if (!pathMatches(ctx.policyRule.getAssignmentPath(), sourceOrderConstraints)) { + LOGGER.trace("Assignment path to the global policy rule does not match source order constraints," + + " not triggering. Path={}, source order constraints={}", + ctx.policyRule.getAssignmentPath(), sourceOrderConstraints); + return true; + } + } else { + // It is not clear how to match orderConstraint with assignment path of the constraint. + // Let us try the following test: we consider it matching if there's at least one segment + // on the path that matches the constraint. + boolean found = ctx.policyRule.getAssignmentPath().getSegments().stream() + .anyMatch(segment -> segment.matches(sourceOrderConstraints)); + if (!found) { + LOGGER.trace("No segment in assignment path to the assigned policy rule does not match source order " + + "constraints, not triggering. Whole path={}, constraints={}", ctx.policyRule.getAssignmentPath(), + sourceOrderConstraints); + return true; + } + } + return false; + } + @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean pathMatches(AssignmentPath assignmentPath, List definedOrderConstraints) { if (assignmentPath == null) { - throw new IllegalStateException("Check this. Assignment path is null."); + throw new IllegalStateException("Assignment path is null."); } if (assignmentPath.isEmpty()) { - throw new IllegalStateException("Check this. Assignment path is empty."); + throw new IllegalStateException("Assignment path is empty."); } return assignmentPath.matches(definedOrderConstraints); } @@ -166,7 +223,7 @@ private List defaultOrderConstraints() { static boolean oidMatches(ObjectReferenceType targetRef, EvaluatedAssignmentTargetImpl assignmentTarget, PrismContext prismContext, MatchingRuleRegistry matchingRuleRegistry, String context) throws SchemaException { if (targetRef == null) { - return true; // this means we rely on comparing relations + return true; // this means we rely on comparing relations } if (assignmentTarget.getOid() == null) { return false; // shouldn't occur diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/evaluators/MultiplicityConstraintEvaluator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/evaluators/MultiplicityConstraintEvaluator.java index a46a1358a77..18dca792118 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/evaluators/MultiplicityConstraintEvaluator.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/policy/evaluators/MultiplicityConstraintEvaluator.java @@ -158,11 +158,11 @@ private EvaluatedMultiplicityTrigger evaluateF if (!ctx.isDirect) { return null; } - if (ctx.inPlus) { + if (ctx.isAdded) { if (!ctx.evaluatedAssignment.isPresentInCurrentObject()) { return checkAssigneeConstraints(constraint, ctx.lensContext, ctx.evaluatedAssignment, PLUS, ctx, result); // only really new assignments } - } else if (ctx.inMinus) { + } else if (ctx.isDeleted) { if (ctx.evaluatedAssignment.isPresentInCurrentObject()) { return checkAssigneeConstraints(constraint, ctx.lensContext, ctx.evaluatedAssignment, PlusMinusZero.MINUS, ctx, result); // only assignments that are really deleted } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/trigger/TriggerScannerTaskHandler.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/trigger/TriggerScannerTaskHandler.java index 69b2698b327..96ab55ac9a8 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/trigger/TriggerScannerTaskHandler.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/trigger/TriggerScannerTaskHandler.java @@ -67,8 +67,8 @@ public TriggerScannerTaskHandler() { } // task OID -> handlerUri -> OID+TriggerID; cleared on task start - // we use plain map, as it is much easier to synchronize explicitly than to play with ConcurrentMap methods - private Map>> processedTriggersMap = new HashMap<>(); + // we use plain map with explicit synchronization + private final Map>> processedTriggersMap = new HashMap<>(); private synchronized void initProcessedTriggers(Task coordinatorTask) { Validate.notNull(coordinatorTask.getOid(), "Task OID is null"); diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestTriggerTask.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestTriggerTask.java index dc0db608551..38ce0acbda8 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestTriggerTask.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestTriggerTask.java @@ -39,7 +39,7 @@ /** * @author Radovan Semancik * - * PMed: In theory, the assertions counting # of invocations could fail even if trigger task handler works well + * In theory, the assertions counting # of invocations could fail even if trigger task handler works well * - if the scanner task would run more than once. If that occurs in reality, we'll deal with it. * */ diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/rbac/TestSegregationOfDuties.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/rbac/TestSegregationOfDuties.java index 6cf57a9ba17..7f309d6dbb2 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/rbac/TestSegregationOfDuties.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/rbac/TestSegregationOfDuties.java @@ -14,6 +14,8 @@ import java.util.List; import java.util.function.Consumer; +import com.evolveum.midpoint.test.TestResource; + import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; @@ -129,6 +131,14 @@ public class TestSegregationOfDuties extends AbstractInitializedModelIntegration private static final String GLOBAL_POLICY_RULE_SOD_APPROVAL_NAME = "exclusion-global-sod-approval"; + private static final TestResource METAROLE_EXCLUSION_APPLICATION = new TestResource<>(TEST_DIR, "metarole-exclusion-application.xml", "420e5734-3c9b-4a13-8c29-00e745701225"); + private static final TestResource ROLE_APPLICATION_1 = new TestResource<>(TEST_DIR, "role-application1.xml", "67a972f6-a399-48e3-ac8d-9140d3323259"); + private static final TestResource ROLE_APPLICATION_2 = new TestResource<>(TEST_DIR, "role-application2.xml", "7e468bbf-a1c3-4c6c-a327-39b720c27930"); + private static final TestResource ROLE_BUSINESS_1 = new TestResource<>(TEST_DIR, "role-business1.xml", "0a75e61a-d5d8-422b-aae5-2f8ec4426df5"); + private static final TestResource ROLE_BUSINESS_2 = new TestResource<>(TEST_DIR, "role-business2.xml", "a6bd00fd-2fd4-48b1-8a4f-6edd038beea3"); + private static final TestResource USER_PETR = new TestResource<>(TEST_DIR, "user-petr.xml", "16a61473-9542-4068-98be-3380802afbfe"); + private static final TestResource USER_MARTIN = new TestResource<>(TEST_DIR, "user-martin.xml", "1bf090da-b070-4049-a10e-ba4a7c8430cd"); + @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { super.initSystem(initTask, initResult); @@ -160,6 +170,15 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti repoAddObjectFromFile(ROLE_SELF_EXCLUSION_FILE, initResult); repoAddObjectFromFile(ROLE_SELF_EXCLUSION_MANAGER_MEMBER_FILE, initResult); + repoAdd(METAROLE_EXCLUSION_APPLICATION, initResult); + repoAdd(ROLE_APPLICATION_1, initResult); + repoAdd(ROLE_APPLICATION_2, initResult); + repoAdd(ROLE_BUSINESS_1, initResult); + repoAdd(ROLE_BUSINESS_2, initResult); + + addObject(USER_PETR, initTask, initResult); + addObject(USER_MARTIN, initTask, initResult); + // setGlobalTracingOverride(createModelLoggingTracingProfile()); } @@ -1445,6 +1464,52 @@ public void test829GuybrushUnassignRoleMinister() throws Exception { assertNotAssignedRole(userAfter, ROLE_MINISTER_OID); } + /** + * Petr has business1 (inducing application1 with application exclusion). + * Now we assign him application1 explicitly. Pruning should NOT be carried out. + * + * MID-6268. + */ + @Test + public void test830AddApplicationRoleExplicitly() throws Exception { + given(); + Task task = getTestTask(); + OperationResult result = task.getResult(); + + when(); + assignRole(USER_PETR.oid, ROLE_APPLICATION_1.oid, task, result); + + then(); + assertUserAfter(USER_PETR.oid) + .assignments() + .assertAssignments(2) + .assertRole(ROLE_APPLICATION_1.oid) + .assertRole(ROLE_BUSINESS_1.oid); + } + + /** + * Martin has application2. Now we assign him business2 (inducing application2 with application + * exclusion). Pruning should NOT be carried out. + * + * MID-6268. + */ + @Test + public void test835AddApplicationRoleExplicitly() throws Exception { + given(); + Task task = getTestTask(); + OperationResult result = task.getResult(); + + when(); + assignRole(USER_MARTIN.oid, ROLE_BUSINESS_2.oid, task, result); + + then(); + assertUserAfter(USER_MARTIN.oid) + .assignments() + .assertAssignments(2) + .assertRole(ROLE_APPLICATION_2.oid) + .assertRole(ROLE_BUSINESS_2.oid); + } + @Test public void test900ApplyGlobalPolicyRulesSoDApproval() throws Exception { // GIVEN @@ -1544,31 +1609,6 @@ public void test922GuybrushPreviewAssignRoleMinister() throws Exception { assertPolicyActionApproval(evaluatedSodPolicyRule); } - private void assertPolicyActionApproval(EvaluatedPolicyRule evaluatedPolicyRule) { - PolicyActionsType actions = evaluatedPolicyRule.getActions(); - assertNotNull("No policy actions in " + evaluatedPolicyRule, actions); - assertFalse("No approval action in " + evaluatedPolicyRule, actions.getApproval().isEmpty()); - } - - private void assertEvaluatedPolicyRuleTriggers(EvaluatedPolicyRule evaluatedPolicyRule, - Collection> triggers, int expectedNumberOfTriggers) { - assertEquals("Wrong number of triggers in evaluated policy rule " + evaluatedPolicyRule.getName(), expectedNumberOfTriggers, triggers.size()); - } - - private EvaluatedPolicyRuleTrigger getSinglePolicyRuleTrigger(EvaluatedPolicyRule evaluatedPolicyRule, Collection> triggers) { - assertEvaluatedPolicyRuleTriggers(evaluatedPolicyRule, triggers, 1); - return triggers.iterator().next(); - } - - private EvaluatedPolicyRuleTrigger getEvaluatedPolicyRuleTrigger( - Collection> triggers, PolicyConstraintKindType expectedConstraintType) { - return triggers.stream().filter(trigger -> expectedConstraintType.equals(trigger.getConstraintKind())).findFirst().get(); - } - - private EvaluatedPolicyRule getEvaluatedPolicyRule(Collection evaluatedPolicyRules, String ruleName) { - return evaluatedPolicyRules.stream().filter(rule -> ruleName.equals(rule.getName())).findFirst().get(); - } - @Test public void test929GuybrushUnassignRoleCriminal() throws Exception { // GIVEN @@ -1685,6 +1725,31 @@ public void test960JimGoldByMapping() throws Exception { assertNotAssignedRole(userAfter, ROLE_PRIZE_BRONZE_OID); } + private void assertPolicyActionApproval(EvaluatedPolicyRule evaluatedPolicyRule) { + PolicyActionsType actions = evaluatedPolicyRule.getActions(); + assertNotNull("No policy actions in " + evaluatedPolicyRule, actions); + assertFalse("No approval action in " + evaluatedPolicyRule, actions.getApproval().isEmpty()); + } + + private void assertEvaluatedPolicyRuleTriggers(EvaluatedPolicyRule evaluatedPolicyRule, + Collection> triggers, int expectedNumberOfTriggers) { + assertEquals("Wrong number of triggers in evaluated policy rule " + evaluatedPolicyRule.getName(), expectedNumberOfTriggers, triggers.size()); + } + + private EvaluatedPolicyRuleTrigger getSinglePolicyRuleTrigger(EvaluatedPolicyRule evaluatedPolicyRule, Collection> triggers) { + assertEvaluatedPolicyRuleTriggers(evaluatedPolicyRule, triggers, 1); + return triggers.iterator().next(); + } + + private EvaluatedPolicyRuleTrigger getEvaluatedPolicyRuleTrigger( + Collection> triggers, PolicyConstraintKindType expectedConstraintType) { + return triggers.stream().filter(trigger -> expectedConstraintType.equals(trigger.getConstraintKind())).findFirst().get(); + } + + private EvaluatedPolicyRule getEvaluatedPolicyRule(Collection evaluatedPolicyRules, String ruleName) { + return evaluatedPolicyRules.stream().filter(rule -> ruleName.equals(rule.getName())).findFirst().get(); + } + private PrismObject assignRolePolicyFailure( String userOid, String roleOid, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException, diff --git a/model/model-intest/src/test/resources/rbac/sod/metarole-exclusion-application.xml b/model/model-intest/src/test/resources/rbac/sod/metarole-exclusion-application.xml new file mode 100644 index 00000000000..b57ea6b482d --- /dev/null +++ b/model/model-intest/src/test/resources/rbac/sod/metarole-exclusion-application.xml @@ -0,0 +1,28 @@ + + + + metarole-exclusion-application + + + + + + + + + + + + + + + + + + diff --git a/model/model-intest/src/test/resources/rbac/sod/role-application1.xml b/model/model-intest/src/test/resources/rbac/sod/role-application1.xml new file mode 100644 index 00000000000..6385ce4c043 --- /dev/null +++ b/model/model-intest/src/test/resources/rbac/sod/role-application1.xml @@ -0,0 +1,14 @@ + + + + application1 + + + + diff --git a/model/model-intest/src/test/resources/rbac/sod/role-application2.xml b/model/model-intest/src/test/resources/rbac/sod/role-application2.xml new file mode 100644 index 00000000000..c995ae06b19 --- /dev/null +++ b/model/model-intest/src/test/resources/rbac/sod/role-application2.xml @@ -0,0 +1,14 @@ + + + + application2 + + + + diff --git a/model/model-intest/src/test/resources/rbac/sod/role-business1.xml b/model/model-intest/src/test/resources/rbac/sod/role-business1.xml new file mode 100644 index 00000000000..4f0fd4e562c --- /dev/null +++ b/model/model-intest/src/test/resources/rbac/sod/role-business1.xml @@ -0,0 +1,14 @@ + + + + business1 + + + + diff --git a/model/model-intest/src/test/resources/rbac/sod/role-business2.xml b/model/model-intest/src/test/resources/rbac/sod/role-business2.xml new file mode 100644 index 00000000000..9a24bb01901 --- /dev/null +++ b/model/model-intest/src/test/resources/rbac/sod/role-business2.xml @@ -0,0 +1,14 @@ + + + + business2 + + + + diff --git a/model/model-intest/src/test/resources/rbac/sod/user-martin.xml b/model/model-intest/src/test/resources/rbac/sod/user-martin.xml new file mode 100644 index 00000000000..769330fbab9 --- /dev/null +++ b/model/model-intest/src/test/resources/rbac/sod/user-martin.xml @@ -0,0 +1,14 @@ + + + + martin + + + + diff --git a/model/model-intest/src/test/resources/rbac/sod/user-petr.xml b/model/model-intest/src/test/resources/rbac/sod/user-petr.xml new file mode 100644 index 00000000000..1b3172c96f2 --- /dev/null +++ b/model/model-intest/src/test/resources/rbac/sod/user-petr.xml @@ -0,0 +1,14 @@ + + + + petr + + + + diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/task/AbstractSearchIterativeTaskHandler.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/task/AbstractSearchIterativeTaskHandler.java index 87c3ea84d1b..cd2419a4ed0 100644 --- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/task/AbstractSearchIterativeTaskHandler.java +++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/task/AbstractSearchIterativeTaskHandler.java @@ -148,6 +148,10 @@ public void setPreserveStatistics(boolean preserveStatistics) { this.preserveStatistics = preserveStatistics; } + public void setCountObjectsOnStart(boolean countObjectsOnStart) { + this.countObjectsOnStart = countObjectsOnStart; + } + public void setLogFinishInfo(boolean logFinishInfo) { this.logFinishInfo = logFinishInfo; } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryConfiguration.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryConfiguration.java index c76d0fef1fe..8b326e47eb2 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryConfiguration.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryConfiguration.java @@ -688,7 +688,9 @@ private void computeDefaultConcurrencyParameters() { defaultTransactionIsolation = TransactionIsolation.READ_COMMITTED; defaultLockForUpdateViaHibernate = false; defaultLockForUpdateViaSql = true; - defaultReadOnlyTransactionStatement = "SET TRANSACTION READ ONLY"; + // Technically supported but causes rather random ORA-01466 errors + defaultReadOnlyTransactionStatement = null; +// defaultReadOnlyTransactionStatement = "SET TRANSACTION READ ONLY"; } else if (isUsingPostgreSQL()) { defaultTransactionIsolation = TransactionIsolation.SERIALIZABLE; defaultLockForUpdateViaHibernate = false;