From 87328eeff22fd2cc91b6beea6572f169811693df Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Mon, 13 Mar 2017 16:30:53 +0100 Subject: [PATCH] First round of real AssignmentEvaluator fixes; with some new tests added. MID-3679 (roleMembershipRef / parentOrgRef contains also roles/orgs from disabled assignments) NPE in construction evaluation with mode of null gathering focus mappings, authorizations, GUI config and policy rules when mode is ZERO or null thrown out outdated algorithm for parentOrgRef maintenance --- .../midpoint/prism/PrismReferenceValue.java | 14 +- .../context/EvaluatedAssignmentTarget.java | 2 + .../model/impl/lens/AssignmentEvaluator.java | 46 +- .../impl/lens/AssignmentPathSegmentImpl.java | 6 + .../lens/EvaluatedAssignmentTargetImpl.java | 65 +- .../lens/projector/AssignmentProcessor.java | 173 +----- .../impl/lens/TestAssignmentProcessor2.java | 583 ++++++++++++------ 7 files changed, 501 insertions(+), 388 deletions(-) diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismReferenceValue.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismReferenceValue.java index 19d269bdccc..a2666b03acb 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismReferenceValue.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismReferenceValue.java @@ -363,10 +363,10 @@ public boolean isEmpty() { } /** - * Returns a version of this value that is cannonical, that means it has the minimal form. + * Returns a version of this value that is canonical, that means it has the minimal form. * E.g. it will have only OID and no object. */ - public PrismReferenceValue toCannonical() { + public PrismReferenceValue toCanonical() { PrismReferenceValue can = new PrismReferenceValue(); can.setOid(getOid()); // do NOT copy object @@ -597,15 +597,19 @@ public String debugDump(int indent, boolean expandObject) { @Override public PrismReferenceValue clone() { + return clone(true); + } + + public PrismReferenceValue clone(boolean copyFullObject) { PrismReferenceValue clone = new PrismReferenceValue(getOid(), getOriginType(), getOriginObject()); - copyValues(clone); + copyValues(clone, copyFullObject); return clone; } - protected void copyValues(PrismReferenceValue clone) { + protected void copyValues(PrismReferenceValue clone, boolean copyFullObject) { super.copyValues(clone); clone.targetType = this.targetType; - if (this.object != null) { + if (this.object != null && copyFullObject) { clone.object = this.object.clone(); } clone.description = this.description; diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/EvaluatedAssignmentTarget.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/EvaluatedAssignmentTarget.java index d48da5818f4..d4914e4d60a 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/EvaluatedAssignmentTarget.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/EvaluatedAssignmentTarget.java @@ -47,4 +47,6 @@ public interface EvaluatedAssignmentTarget extends DebugDumpable { AssignmentType getAssignment(); AssignmentPath getAssignmentPath(); + + boolean isValid(); } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java index 5c74eb79a75..41e320575be 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java @@ -326,21 +326,19 @@ private boolean evaluateSegmentContent(AssignmentPathSegm // Here we ignore "reallyValid". It is OK, because reallyValid can be false here only when // evaluating direct assignments; and invalid ones are marked as such via EvaluatedAssignment.isValid. // (This is currently ignored by downstream processing, but that's another story. Will be fixed soon.) - // --- - // But we also ignore "mode". This is because focus mappings are not categorized into PLUS/MINUS/ZERO sets. - // They are simply evaluated as they are: skipped only if both condOld and condNew is false. - // This is less sophisticated than constructions, but for the time being it is perhaps OK. - // TODO But shouldn't we skip focus mapping with mode==null? And with mode=MINUS? - evaluateFocusMappings(segment, ctx); + if (isNonNegative(mode)) { + evaluateFocusMappings(segment, ctx); + } } if (assignmentType.getPolicyRule() != null && !loginMode) { // We can ignore "reallyValid" for the same reason as for focus mappings. - // TODO but what if mode is null or MINUS? - if (segment.isMatchingOrder()) { - collectPolicyRule(true, segment, ctx); - } - if (segment.isMatchingOrderPlusOne()) { - collectPolicyRule(false, segment, ctx); + if (isNonNegative(mode)) { + if (segment.isMatchingOrder()) { + collectPolicyRule(true, segment, ctx); + } + if (segment.isMatchingOrderPlusOne()) { + collectPolicyRule(false, segment, ctx); + } } } if (assignmentType.getTarget() != null || assignmentType.getTargetRef() != null) { @@ -422,6 +420,9 @@ private void collectConstruction(AssignmentPathSegmentImpl segment, PlusMinusZer construction.setValid(isValid); // Do not evaluate the construction here. We will do it in the second pass. Just prepare everything to be evaluated. + if (mode == null) { + return; // null mode (i.e. plus + minus) means 'ignore the payload' + } switch (mode) { case PLUS: ctx.evalAssignment.addConstructionPlus(construction); @@ -625,15 +626,12 @@ private void evaluateSegmentTarget(AssignmentPathSegmentImpl segment, } } - EvaluatedAssignmentTargetImpl evalAssignmentTarget = new EvaluatedAssignmentTargetImpl(); - evalAssignmentTarget.setTarget(targetType.asPrismObject()); - evalAssignmentTarget.setEvaluateConstructions(segment.isMatchingOrder()); - evalAssignmentTarget.setAssignment(segment.getAssignment()); - evalAssignmentTarget.setDirectlyAssigned(ctx.assignmentPath.size() == 1); - evalAssignmentTarget.setAssignmentPath(ctx.assignmentPath.clone()); + EvaluatedAssignmentTargetImpl evalAssignmentTarget = new EvaluatedAssignmentTargetImpl( + targetType.asPrismObject(), segment.isMatchingOrder(), + ctx.assignmentPath.clone(), segment.getAssignment(), isValid); ctx.evalAssignment.addRole(evalAssignmentTarget, mode); - if (mode != PlusMinusZero.MINUS && segment.isProcessMembership()) { + if ((isNonNegative(mode)) && segment.isProcessMembership()) { PrismReferenceValue refVal = new PrismReferenceValue(); refVal.setObject(targetType.asPrismObject()); refVal.setTargetType(ObjectTypes.getObjectType(targetType.getClass()).getTypeQName()); @@ -769,9 +767,9 @@ private void evaluateSegmentTarget(AssignmentPathSegmentImpl segment, evaluateFromSegment(subAssignmentPathSegment, mode, ctx); } - if (evaluationOrder.getSummaryOrder() == 1 && targetType instanceof AbstractRoleType) { + if (evaluationOrder.getSummaryOrder() == 1 && targetType instanceof AbstractRoleType && isNonNegative(mode)) { - for(AuthorizationType authorizationType: ((AbstractRoleType)targetType).getAuthorization()) { + for (AuthorizationType authorizationType: ((AbstractRoleType)targetType).getAuthorization()) { Authorization authorization = createAuthorization(authorizationType, targetType.toString()); ctx.evalAssignment.addAuthorization(authorization); } @@ -788,6 +786,12 @@ private void evaluateSegmentTarget(AssignmentPathSegmentImpl segment, LOGGER.trace("Evaluating segment target DONE for {}", segment); } + private boolean isNonNegative(PlusMinusZero mode) { + // mode == null is also considered negative, because it is a combination of PLUS and MINUS; + // so the net result is that for both old and new state there exists an unsatisfied condition on the path. + return mode == PlusMinusZero.ZERO || mode == PlusMinusZero.PLUS; + } + private void checkRelationWithTarget(AssignmentPathSegmentImpl segment, FocusType targetType, QName relation) throws SchemaException { if (targetType instanceof AbstractRoleType) { diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java index 7e8684022a7..019e4ba8499 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java @@ -413,6 +413,12 @@ public String toString() { if (assignmentType.getConstruction() != null) { sb.append("Constr '").append(assignmentType.getConstruction().getDescription()).append("' "); } + if (assignmentType.getFocusMappings() != null) { + sb.append("FMappings (").append(assignmentType.getFocusMappings().getMapping().size()).append(") "); + } + if (assignmentType.getPolicyRule() != null) { + sb.append("Rule '").append(assignmentType.getPolicyRule().getName()).append("' "); + } } ObjectReferenceType targetRef = assignmentType != null ? assignmentType.getTargetRef() : null; if (target != null || targetRef != null) { diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluatedAssignmentTargetImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluatedAssignmentTargetImpl.java index d1dfde3c962..e222f7d9270 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluatedAssignmentTargetImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluatedAssignmentTargetImpl.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2015-2016 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,17 +15,13 @@ */ package com.evolveum.midpoint.model.impl.lens; -import java.util.ArrayList; -import java.util.Collection; - import com.evolveum.midpoint.model.api.context.EvaluatedAssignmentTarget; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractRoleType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ExclusionPolicyConstraintType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.PolicyConstraintsType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; + +import java.util.ArrayList; +import java.util.Collection; /** * @author semancik @@ -33,29 +29,32 @@ */ public class EvaluatedAssignmentTargetImpl implements EvaluatedAssignmentTarget { - PrismObject target; - private boolean directlyAssigned; - private boolean evaluateConstructions; - private AssignmentPathImpl assignmentPath; // TODO reconsider (maybe we should store only some lightweight information here) - private AssignmentType assignment; + final PrismObject target; + private final boolean evaluateConstructions; + 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; + + EvaluatedAssignmentTargetImpl( + PrismObject target, boolean evaluateConstructions, + AssignmentPathImpl assignmentPath, AssignmentType assignment, + boolean isValid) { + this.target = target; + this.evaluateConstructions = evaluateConstructions; + this.assignmentPath = assignmentPath; + this.assignment = assignment; + this.isValid = isValid; + } @Override public PrismObject getTarget() { return target; } - public void setTarget(PrismObject target) { - this.target = target; - } - @Override public boolean isDirectlyAssigned() { - return directlyAssigned; - } - - public void setDirectlyAssigned(boolean directlyAssigned) { - this.directlyAssigned = directlyAssigned; + return assignmentPath.size() == 1; } @Override @@ -68,32 +67,25 @@ public boolean isEvaluateConstructions() { return evaluateConstructions; } - public void setEvaluateConstructions(boolean evaluateConstructions) { - this.evaluateConstructions = evaluateConstructions; - } - @Override public AssignmentType getAssignment() { return assignment; } - public void setAssignment(AssignmentType assignment) { - this.assignment = assignment; - } - @Override public AssignmentPathImpl getAssignmentPath() { return assignmentPath; } - public void setAssignmentPath(AssignmentPathImpl assignmentPath) { - this.assignmentPath = assignmentPath; - } - public String getOid() { return target.getOid(); } + @Override + public boolean isValid() { + return isValid; + } + public Collection getExclusions() { if (exclusions == null) { exclusions = new ArrayList<>(); @@ -132,8 +124,9 @@ public String debugDump(int indent) { DebugUtil.debugDumpWithLabel(sb, "Target", target, indent + 1); sb.append("\n"); DebugUtil.debugDumpWithLabel(sb, "Assignment", String.valueOf(assignment), indent + 1); + sb.append("\n"); + DebugUtil.debugDumpWithLabel(sb, "isValid", isValid, indent + 1); return sb.toString(); } - } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/AssignmentProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/AssignmentProcessor.java index 8e77cb78681..8d377f9a0ee 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/AssignmentProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/AssignmentProcessor.java @@ -50,7 +50,6 @@ import com.evolveum.midpoint.prism.PrismContainerDefinition; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismObjectDefinition; import com.evolveum.midpoint.prism.PrismPropertyValue; import com.evolveum.midpoint.prism.PrismReference; import com.evolveum.midpoint.prism.PrismReferenceDefinition; @@ -334,15 +333,15 @@ private void processAssignmentsProjectionsWithFocus(LensCo LOGGER.trace("ZERO construction pack: null"); } else { LOGGER.trace("ZERO construction pack (hasValidAssignment={}, hasStrongConstruction={})\n{}", - new Object[]{zeroConstructionPack.hasValidAssignment(), zeroConstructionPack.hasStrongConstruction(), - zeroConstructionPack.debugDump(1)}); + zeroConstructionPack.hasValidAssignment(), zeroConstructionPack.hasStrongConstruction(), + zeroConstructionPack.debugDump(1)); } if (plusConstructionPack == null) { LOGGER.trace("PLUS construction pack: null"); } else { LOGGER.trace("PLUS construction pack (hasValidAssignment={}, hasStrongConstruction={})\n{}", - new Object[]{plusConstructionPack.hasValidAssignment(), plusConstructionPack.hasStrongConstruction(), - plusConstructionPack.debugDump(1)}); + plusConstructionPack.hasValidAssignment(), plusConstructionPack.hasStrongConstruction(), + plusConstructionPack.debugDump(1)); } } @@ -705,14 +704,14 @@ private void collectToConstructionMapFromEvaluatedConstruc ResourceType resource = LensUtil.getResourceReadOnly(context, resourceOid, provisioningService, task, result); intent = LensUtil.refineProjectionIntent(kind, intent, resource, prismContext); ResourceShadowDiscriminator rat = new ResourceShadowDiscriminator(resourceOid, kind, intent); - ConstructionPack constructionPack = null; + ConstructionPack constructionPack; if (constructionMap.containsKey(rat)) { constructionPack = constructionMap.get(rat); } else { constructionPack = new ConstructionPack(); constructionMap.put(rat, constructionPack); } - constructionPack.add(new PrismPropertyValue(construction)); + constructionPack.add(new PrismPropertyValue<>(construction)); if (evaluatedAssignment.isValid()) { constructionPack.setHasValidAssignment(true); } @@ -852,7 +851,7 @@ private void propagateLegalDecisionToHigherOrders( } } - private void createAssignmentDelta(LensContext context, LensProjectionContext accountContext) throws SchemaException{ + private void createAssignmentDelta(LensContext context, LensProjectionContext accountContext) throws SchemaException{ Class focusClass = context.getFocusClass(); ContainerDelta assignmentDelta = ContainerDelta.createDelta(FocusType.F_ASSIGNMENT, focusClass, prismContext); AssignmentType assignment = new AssignmentType(); @@ -866,144 +865,28 @@ private void createAssignmentDelta(L } - public void processOrgAssignments(LensContext context, - OperationResult result) throws SchemaException { + public void processOrgAssignments(LensContext context, OperationResult result) throws SchemaException { + LensFocusContext focusContext = context.getFocusContext(); - DeltaSetTriple evaluatedAssignmentTriple = context.getEvaluatedAssignmentTriple(); - if (focusContext == null || evaluatedAssignmentTriple == null) { + if (focusContext == null) { return; } - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Starting processing org assignments into parentOrgRef delta(s); evaluatedAssignmentTriple is:\n{}", - evaluatedAssignmentTriple.debugDump()); - } - - Class focusClass = focusContext.getObjectTypeClass(); - PrismObjectDefinition userDef = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(focusClass); - PrismReferenceDefinition orgRefDef = userDef.findReferenceDefinition(FocusType.F_PARENT_ORG_REF); - ItemPath orgRefPath = new ItemPath(FocusType.F_PARENT_ORG_REF); + Collection shouldBeParentOrgRefs = new ArrayList<>(); - // check if parentOrgRef recon is needed - it is when something inside OrgType assignment has changed - boolean forceRecon = context.isReconcileFocus(); // this is directly influenced by Reconcile model execution option - if (!forceRecon) { - for (EvaluatedAssignmentImpl assignment : evaluatedAssignmentTriple.getAllValues()) { - if (assignment.isForceRecon() && - assignment.getAssignmentType() != null && - assignment.getAssignmentType().getTargetRef() != null && - OrgType.COMPLEX_TYPE.equals(assignment.getAssignmentType().getTargetRef().getType())) { - forceRecon = true; - break; + DeltaSetTriple evaluatedAssignmentTriple = context.getEvaluatedAssignmentTriple(); + if (evaluatedAssignmentTriple != null) { + for (EvaluatedAssignmentImpl evalAssignment: evaluatedAssignmentTriple.getNonNegativeValues()) { + if (evalAssignment.isValid()) { + addReferences(shouldBeParentOrgRefs, evalAssignment.getOrgRefVals()); } } } - // for zero and minus sets we check isForceRecon for all non-construction-related assignments (MID-2242) - // TODO why "non-construction-related" ones only? - if (!forceRecon) { - for (EvaluatedAssignmentImpl assignment: evaluatedAssignmentTriple.getNonPositiveValues()) { - if (assignment.isForceRecon() && assignment.getConstructions().isEmpty()) { - forceRecon = true; - break; - } - } - } - - if (!forceRecon) { // if no recon, we simply add/delete values as needed - - LOGGER.trace("No reconciliation requested, processing plus and minus sets"); - - // A list of values that are _not_ to be removed - these are all the values from zero set, - // as well as values from plus set. - // - // Contrary to existing standard delta merge algorithm (where add+delete means "keep the current state"), - // we ignore any delete of values that should be existing or added. - - Collection notToBeDeletedCanonical = new HashSet<>(); - for (EvaluatedAssignmentImpl assignment : evaluatedAssignmentTriple.getZeroSet()) { - Collection orgs = assignment.getOrgRefVals(); - for (PrismReferenceValue org : orgs) { - notToBeDeletedCanonical.add(org.toCannonical()); - } - } - - // Plus - for (EvaluatedAssignmentImpl assignment : evaluatedAssignmentTriple.getPlusSet()) { - Collection orgs = assignment.getOrgRefVals(); - for (PrismReferenceValue org : orgs) { - ItemDelta orgRefDelta = orgRefDef.createEmptyDelta(orgRefPath); - PrismReferenceValue orgCanonical = org.toCannonical(); - orgRefDelta.addValueToAdd(orgCanonical); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Created parentOrgRef delta:\n{}", orgRefDelta.debugDump()); - } - focusContext.swallowToProjectionWaveSecondaryDelta(orgRefDelta); - - notToBeDeletedCanonical.add(orgCanonical); - } - } - - // Minus (except for these that are also in zero set) - for (EvaluatedAssignmentImpl assignment : evaluatedAssignmentTriple.getMinusSet()) { - Collection orgs = assignment.getOrgRefVals(); - for (PrismReferenceValue org : orgs) { - ItemDelta orgRefDelta = orgRefDef.createEmptyDelta(orgRefPath); - PrismReferenceValue orgCanonical = org.toCannonical(); - if (notToBeDeletedCanonical.contains(orgCanonical)) { - LOGGER.trace("Not removing {} because it is in the zero or plus set", orgCanonical); - } else { - orgRefDelta.addValueToDelete(orgCanonical); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Created parentOrgRef delta:\n{}", orgRefDelta.debugDump()); - } - focusContext.swallowToProjectionWaveSecondaryDelta(orgRefDelta); - } - } - } - - } else { // if reconciliation is requested, we recreate parentOrgRef from scratch - - LOGGER.trace("Reconciliation requested, collecting all non-negative values"); - - Set valuesToReplace = new HashSet<>(); - - for (EvaluatedAssignmentImpl assignment : evaluatedAssignmentTriple.getNonNegativeValues()) { - Collection orgs = assignment.getOrgRefVals(); - for (PrismReferenceValue org : orgs) { - PrismReferenceValue canonical = org.toCannonical(); - valuesToReplace.add(canonical); // if valuesToReplace would be a list, we should check for duplicates! - } - } - PrismObject objectNew = focusContext.getObjectNew(); - if (parentOrgRefDiffers(objectNew, valuesToReplace)) { - ItemDelta orgRefDelta = orgRefDef.createEmptyDelta(orgRefPath); - orgRefDelta.setValuesToReplace(valuesToReplace); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Created parentOrgRef delta:\n{}", orgRefDelta.debugDump()); - } - focusContext.swallowToProjectionWaveSecondaryDelta(orgRefDelta); - } else { - LOGGER.trace("Computed parentOrgRef is the same as the value in objectNew -- skipping application of the delta"); - } - } - LOGGER.trace("Processing org assignments into parentOrgRef delta(s) done."); - } - private boolean parentOrgRefDiffers(PrismObject objectNew, Set valuesToReplace) { - if (objectNew == null) { - return true; // the result is probably irrelevant, as the object is going to be deleted - but it's safer not to skip parentOrgRef delta - } - PrismReference parentOrgRef = objectNew.findReference(ObjectType.F_PARENT_ORG_REF); - List existingValues; - if (parentOrgRef != null) { - existingValues = parentOrgRef.getValues(); - } else { - existingValues = new ArrayList<>(); - } - boolean equal = MiscUtil.unorderedCollectionEquals(existingValues, valuesToReplace); - return !equal; + setReferences(focusContext, ObjectType.F_PARENT_ORG_REF, shouldBeParentOrgRefs); } - public void checkForAssignmentConflicts(LensContext context, + public void checkForAssignmentConflicts(LensContext context, OperationResult result) throws PolicyViolationException { for(LensProjectionContext projectionContext: context.getProjectionContexts()) { if (AssignmentPolicyEnforcementType.NONE == projectionContext.getAssignmentPolicyEnforcementType()){ @@ -1078,13 +961,13 @@ public void removeIgnoredContexts(LensContext context) } private XMLGregorianCalendar collectFocusTripleFromMappings( - Collection> evaluatedAssignmnents, + Collection> evaluatedAssignments, Map>> outputTripleMap, PlusMinusZero plusMinusZero) throws SchemaException { XMLGregorianCalendar nextRecomputeTime = null; - for (EvaluatedAssignmentImpl ea: evaluatedAssignmnents) { + for (EvaluatedAssignmentImpl ea: evaluatedAssignments) { Collection> focusMappings = (Collection)ea.getFocusMappings(); for (Mapping mapping: focusMappings) { @@ -1124,13 +1007,13 @@ public void processMembershipAndDelegatedRefs(LensContext Collection shouldBeDelegatedRefs = new ArrayList<>(); DeltaSetTriple evaluatedAssignmentTriple = context.getEvaluatedAssignmentTriple(); - if (evaluatedAssignmentTriple == null) { - return; - } - - for (EvaluatedAssignmentImpl evalAssignment: evaluatedAssignmentTriple.getNonNegativeValues()) { - addReferences(shouldBeRoleRefs, evalAssignment.getMembershipRefVals()); - addReferences(shouldBeDelegatedRefs, evalAssignment.getDelegationRefVals()); + if (evaluatedAssignmentTriple != null) { + for (EvaluatedAssignmentImpl evalAssignment : evaluatedAssignmentTriple.getNonNegativeValues()) { + if (evalAssignment.isValid()) { + addReferences(shouldBeRoleRefs, evalAssignment.getMembershipRefVals()); + addReferences(shouldBeDelegatedRefs, evalAssignment.getDelegationRefVals()); + } + } } setReferences(focusContext, FocusType.F_ROLE_MEMBERSHIP_REF, shouldBeRoleRefs); @@ -1180,7 +1063,7 @@ private void addReferences(Collection extractedReferences, } } if (!found) { - PrismReferenceValue ref = reference.clone(); + PrismReferenceValue ref = reference.clone(false); // using copyObject=false instead of calling canonicalize() if (ref.getRelation() != null && QNameUtil.isUnqualified(ref.getRelation())) { ref.setRelation(new QName(SchemaConstants.NS_ORG, ref.getRelation().getLocalPart(), SchemaConstants.PREFIX_NS_ORG)); } diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java index dbe99739263..e82ab93bb00 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java @@ -19,7 +19,9 @@ import com.evolveum.midpoint.model.api.context.EvaluatedPolicyRule; import com.evolveum.midpoint.model.impl.lens.projector.AssignmentProcessor; import com.evolveum.midpoint.prism.delta.PlusMinusZero; +import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder; import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.schema.constants.ObjectTypes; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ActivationUtil; @@ -41,6 +43,7 @@ import java.util.*; import java.util.function.Consumer; import java.util.stream.Collectors; +import java.util.stream.Stream; import static com.evolveum.midpoint.test.IntegrationTestTools.display; import static com.evolveum.midpoint.test.IntegrationTestTools.displayObjectTypeCollection; @@ -75,7 +78,7 @@ * - inducement MRx->Ry is of order 2 * - inducement MMRx->MRy is of order 1 * - * Each role has authorization, construction, focus mapping, focus policy rule and target policy rule. + * Each role has an authorization, GUI config, constructions, focus mappings, focus policy rules and target policy rules. * * Each assignment and each role can be selectively enabled/disabled (via activation) and has its condition matched (none/old/new/old+new). * @@ -114,142 +117,6 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti createObjects(false, initTask, initResult, null); } - private void createObjects(boolean deleteFirst, Task task, OperationResult result, Runnable adjustment) throws Exception { - role1 = createRole(1, 1); - role2 = createRole(1, 2); - org3 = createOrg(3); - role4 = createRole(1, 4); - role5 = createRole(1, 5); - role6 = createRole(1, 6); - metarole1 = createRole(2, 1); - metarole2 = createRole(2, 2); - metarole3 = createRole(2, 3); - metarole4 = createRole(2, 4); - metametarole1 = createRole(3, 1); - assign(role1, metarole1); - assign(role2, metarole2); - assign(metarole1, metametarole1); - induce(role1, role2, 1); - induce(metarole1, metarole3, 1); - induce(metarole1, role4, 2); - induce(metarole2, org3, 2); - induce(metarole3, role5, 2); - induce(metarole4, role6, 2); - induce(metametarole1, metarole4, 2); - - roles = Arrays - .asList(role1, role2, org3, role4, role5, role6, metarole1, metarole2, metarole3, metarole4, metametarole1); - - if (adjustment != null) { - adjustment.run(); - } - -// for (ObjectType role : roles) { -// System.out.println(prismContext.xmlSerializer().serialize(role.asPrismObject())); -// } - - // TODO implement repoAddObjects with overwrite option - if (deleteFirst) { - for (ObjectType role : roles) { - repositoryService.deleteObject(role.getClass(), role.getOid(), result); - } - } - - repoAddObjects(roles, result); - recomputeAndRefreshObjects(roles, task, result); - displayObjectTypeCollection("objects", roles); - } - - private void induce(AbstractRoleType source, AbstractRoleType target, int inducementOrder) { - AssignmentType inducement = ObjectTypeUtil.createAssignmentTo(target.asPrismObject()); - if (inducementOrder > 1) { - inducement.setOrder(inducementOrder); - } - source.getInducement().add(inducement); - } - - private void assign(RoleType source, RoleType target) { - AssignmentType assignment = ObjectTypeUtil.createAssignmentTo(target.asPrismObject()); - source.getAssignment().add(assignment); - } - - private RoleType createRole(int level, int number) { - return prepareAbstractRole(new RoleType(prismContext), level, number, "R"); - } - - private OrgType createOrg(int number) { - return prepareAbstractRole(new OrgType(prismContext), 1, number, "O"); - } - - private R prepareAbstractRole(R abstractRole, int level, int number, String nameSymbol) { - String name = StringUtils.repeat('M', level-1) + nameSymbol + number; - String oid = getRoleOid(name); - - abstractRole.setName(PolyStringType.fromOrig(name)); - abstractRole.setOid(oid); - - // constructions - for (int i = 0; i <= CONSTRUCTION_LEVELS; i++) { - ConstructionType c = new ConstructionType(prismContext); - c.setDescription(name + "-" + i); - c.setResourceRef(ObjectTypeUtil.createObjectRef(RESOURCE_DUMMY_EMPTY_OID, ObjectTypes.RESOURCE)); - AssignmentType a = new AssignmentType(prismContext); - a.setDescription("Assignment for " + c.getDescription()); - a.setConstruction(c); - addAssignmentOrInducement(abstractRole, i, a); - } - - // focus mappings - for (int i = 0; i <= FOCUS_MAPPING_LEVELS; i++) { - MappingType mapping = new MappingType(); - mapping.setName(name + "-" + i); - VariableBindingDefinitionType source = new VariableBindingDefinitionType(); - source.setPath(new ItemPath(UserType.F_NAME).asItemPathType()); - mapping.getSource().add(source); - VariableBindingDefinitionType target = new VariableBindingDefinitionType(); - target.setPath(new ItemPath(UserType.F_DESCRIPTION).asItemPathType()); - mapping.setTarget(target); - MappingsType mappings = new MappingsType(prismContext); - mappings.getMapping().add(mapping); - AssignmentType a = new AssignmentType(prismContext); - a.setFocusMappings(mappings); - addAssignmentOrInducement(abstractRole, i, a); - } - - // policy rules - for (int i = 0; i <= POLICY_RULES_LEVELS; i++) { - PolicyRuleType rule = new PolicyRuleType(prismContext); - rule.setName(name + "-" + i); - AssignmentType a = new AssignmentType(prismContext); - a.setPolicyRule(rule); - addAssignmentOrInducement(abstractRole, i, a); - } - - // authorization - AuthorizationType authorization = new AuthorizationType(prismContext); - authorization.getAction().add(name); - abstractRole.getAuthorization().add(authorization); - - // admin gui config - AdminGuiConfigurationType guiConfig = new AdminGuiConfigurationType(); - guiConfig.setPreferredDataLanguage(name); - abstractRole.setAdminGuiConfiguration(guiConfig); - return abstractRole; - } - - private void addAssignmentOrInducement(R abstractRole, int order, AssignmentType assignment) { - if (order == 0) { - abstractRole.getAssignment().add(assignment); - } else { - assignment.setOrder(order); - abstractRole.getInducement().add(assignment); - } - } - - private static String getRoleOid(String name) { - return "99999999-0000-0000-0000-" + StringUtils.repeat('0', 12-name.length()) + name; - } - @Test public void test010AssignR1ToJack() throws Exception { final String TEST_NAME = "test010AssignR1ToJack"; @@ -372,6 +239,10 @@ private void assertFocusPolicyRules(EvaluatedAssignmentImpl evaluatedA evaluatedAssignment.getFocusPolicyRules().stream().map(r -> r.getName()).collect(Collectors.toSet())); } + private void assertTargetPolicyRules(EvaluatedAssignmentImpl evaluatedAssignment, String expectedTargetItems, String expectedThisTargetItems) { + assertTargetPolicyRules(evaluatedAssignment, getList(expectedTargetItems), getList(expectedThisTargetItems)); + } + private void assertTargetPolicyRules(EvaluatedAssignmentImpl evaluatedAssignment, Collection expectedTargetItems, Collection expectedThisTargetItems) { expectedTargetItems = CollectionUtils.emptyIfNull(expectedTargetItems); expectedThisTargetItems = CollectionUtils.emptyIfNull(expectedThisTargetItems); @@ -393,6 +264,37 @@ private void assertTargetPolicyRules(EvaluatedAssignmentImpl evaluated } } + private void assertTargets(EvaluatedAssignmentImpl evaluatedAssignment, + String zeroValid, String zeroInvalid, + String plusValid, String plusInvalid, + String minusValid, String minusInvalid) { + assertTargets(evaluatedAssignment, getList(zeroValid), getList(zeroInvalid), + getList(plusValid), getList(plusInvalid), getList(minusValid), getList(minusInvalid)); + } + + private void assertTargets(EvaluatedAssignmentImpl evaluatedAssignment, + List zeroValid, List zeroInvalid, + List plusValid, List plusInvalid, + List minusValid, List minusInvalid) { + assertTargets("zero", evaluatedAssignment.getRoles().getZeroSet(), zeroValid, zeroInvalid); + assertTargets("plus", evaluatedAssignment.getRoles().getPlusSet(), plusValid, plusInvalid); + assertTargets("minus", evaluatedAssignment.getRoles().getMinusSet(), minusValid, minusInvalid); + } + + private void assertTargets(String type, Collection targets, List expectedValid, + List expectedInvalid) { + targets = CollectionUtils.emptyIfNull(targets); + Collection realValid = targets.stream().filter(t -> t.isValid()).collect(Collectors.toList()); + Collection realInvalid = targets.stream().filter(t -> !t.isValid()).collect(Collectors.toList()); + assertEquals("Wrong # of valid targets in " + type + " set", expectedValid.size(), realValid.size()); + assertEquals("Wrong # of invalid targets in " + type + " set", expectedInvalid.size(), realInvalid.size()); + assertEquals("Wrong valid targets in " + type + " set", new HashSet<>(expectedValid), + realValid.stream().map(t -> t.getTarget().getName().getOrig()).collect(Collectors.toSet())); + assertEquals("Wrong invalid targets in " + type + " set", new HashSet<>(expectedInvalid), + realInvalid.stream().map(t -> t.getTarget().getName().getOrig()).collect(Collectors.toSet())); + } + + private void assertConstructions(EvaluatedAssignmentImpl evaluatedAssignment, String zeroValid, String zeroInvalid, String plusValid, String plusInvalid, @@ -400,7 +302,7 @@ private void assertConstructions(EvaluatedAssignmentImpl evaluatedAssi assertConstructions(evaluatedAssignment, getList(zeroValid), getList(zeroInvalid), getList(plusValid), getList(plusInvalid), getList(minusValid), getList(minusInvalid)); } - + private void assertConstructions(EvaluatedAssignmentImpl evaluatedAssignment, List zeroValid, List zeroInvalid, List plusValid, List plusInvalid, @@ -432,7 +334,7 @@ private Collection assertAssignmentTripleSetSize(LensCo return context.getEvaluatedAssignmentTriple().getAllValues(); } - @Test(enabled = false) + @Test public void test020AssignR1ToJackProjectorDisabled() throws Exception { final String TEST_NAME = "test020AssignR1ToJackProjectorDisabled"; TestUtil.displayTestTile(this, TEST_NAME); @@ -475,6 +377,8 @@ private LensContext createContextForRoleAssignment(String userOid, Str } /** + * Now disable some roles. Their administrative status is simply set to DISABLED. + * * MMR1(D)---------I------------------------------* * ^ | * | I @@ -500,29 +404,12 @@ public void test100DisableSomeRoles() throws Exception { OperationResult result = task.getResult(); // WHEN - createObjects(true, task, result, () -> { - disableRoles("MMR1 R2 MR3 R4"); - }); + createObjects(true, task, result, () -> disableRoles("MMR1 R2 MR3 R4")); // THEN - - } - - private void disableRoles(String rolesText) { - List roleNames = getList(rolesText); - for (String name : roleNames) { - AbstractRoleType role = findRole(name); - if (role.getActivation() == null) { - role.setActivation(new ActivationType(prismContext)); - } - role.getActivation().setAdministrativeStatus(ActivationStatusType.DISABLED); - } + // TODO check e.g. membershipRef for roles } - private AbstractRoleType findRole(String name) { - return (AbstractRoleType) roles.stream().filter(r -> name.equals(r.getName().getOrig())).findFirst() - .orElseThrow(() -> new IllegalStateException("No role " + name)); - } @Test public void test110AssignR1ToJack() throws Exception { @@ -559,10 +446,6 @@ public void test110AssignR1ToJack() throws Exception { assertFocusMappings(evaluatedAssignment, expectedItems); assertFocusPolicyRules(evaluatedAssignment, expectedItems); - // TODO why R4-0 R5-0 R6-0 ? Sounds not good: when we are adding R1 assignment, we are not interested - // in approval rules residing in induced roles, even if they are induced through a higher levels - // (in the same way as we are not interested in R2-0 and R3-0) - // MR3-1 MR4-1 seems to be OK; these are induced in a quite intuitive way (via MR1) String expectedThisTargetRules = "R1-0 MR1-1"; String expectedTargetRules = expectedThisTargetRules; assertTargetPolicyRules(evaluatedAssignment, getList(expectedTargetRules), getList(expectedThisTargetRules)); @@ -571,41 +454,103 @@ public void test110AssignR1ToJack() throws Exception { } - private void assertDelegation(EvaluatedAssignmentImpl evaluatedAssignment, String text) { - assertPrismRefValues("delegationRef", evaluatedAssignment.getDelegationRefVals(), findRoles(text)); - } + /** + * In a similar way, let's disable some assignments. Their administrative status is simply set to DISABLED. + * + * MMR1 -----------I------------------------------* + * ^ | + * | I + * | V + * MR1 -----------I-------------*-(D)-> MR3 MR4 + * ^ MR2 --I---* | | | + * | ^ I I I I(D) + * | | V V V V + * R1-I(D)-> R2 O3 R4 R5 R6 + * ^ + * | + * | + * jack + */ - private void assertOrgRef(EvaluatedAssignmentImpl evaluatedAssignment, String text) { - assertPrismRefValues("orgRef", evaluatedAssignment.getOrgRefVals(), findRoles(text)); - } + @Test + public void test150DisableSomeAssignments() throws Exception { + final String TEST_NAME = "test150DisableSomeAssignments"; + TestUtil.displayTestTile(this, TEST_NAME); - private void assertMembershipRef(EvaluatedAssignmentImpl evaluatedAssignment, String text) { - assertPrismRefValues("membershipRef", evaluatedAssignment.getMembershipRefVals(), findRoles(text)); + // GIVEN + Task task = taskManager.createTaskInstance(TestAssignmentProcessor.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + createObjects(true, task, result, () -> disableAssignments("MR4-R6 MR1-MR3 R1-R2")); + + // THEN } - private List findRoles(String text) { - return getList(text).stream().map(n -> findRole(n)).collect(Collectors.toList()); + @Test + public void test160AssignR1ToJack() throws Exception { + final String TEST_NAME = "test160AssignR1ToJack"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestAssignmentProcessor.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + LensContext context = createContextForRoleAssignment(USER_JACK_OID, R1_OID, null, null, result); + + // WHEN + assignmentProcessor.processAssignmentsProjections(context, clock.currentTimeXMLGregorianCalendar(), task, result); + + // THEN + display("Output context", context); + display("Evaluated assignment triple", context.getEvaluatedAssignmentTriple()); + + result.computeStatus(); + assertSuccess("Assignment processor failed (result)", result); + + Collection evaluatedAssignments = assertAssignmentTripleSetSize(context, 0, 1, 0); + EvaluatedAssignmentImpl evaluatedAssignment = evaluatedAssignments.iterator().next(); + assertEquals("Wrong evaluatedAssignment.isValid", true, evaluatedAssignment.isValid()); + + assertMembershipRef(evaluatedAssignment, "R1 R4"); + assertOrgRef(evaluatedAssignment, null); + assertDelegation(evaluatedAssignment, null); + + String expectedItems = "R1-1 MR1-2 MMR1-3 MR4-2 R4-1"; + assertConstructions(evaluatedAssignment, expectedItems, null, null, null, null, null); + assertFocusMappings(evaluatedAssignment, expectedItems); + assertFocusPolicyRules(evaluatedAssignment, expectedItems); + + String expectedThisTargetRules = "R1-0 MR1-1 MMR1-2 MR4-1 R4-0"; // TODO why R4-0 ? + String expectedTargetRules = expectedThisTargetRules; + assertTargetPolicyRules(evaluatedAssignment, getList(expectedTargetRules), getList(expectedThisTargetRules)); + assertAuthorizations(evaluatedAssignment, "R1 R4"); + assertGuiConfig(evaluatedAssignment, "R1 R4"); + } /** - * MMR1 -----------I------------------------------* + * Let's attach some conditions to assignments and roles. "+" condition means that it will be satisfied only in jack's new state. + * "-" condition will be satisfied only in jack's old state. "0" condition will be never satisfied. + * + * MMR1------------I------------------------------* * ^ | - * | I + * (+) I * | V - * MR1 -----------I-------------*-----> MR3 MR4 + * (+)MR1 -----------I-------------*-----> MR3(0) MR4(-) * ^ MR2 --I---* | | | - * | ^ I I I I(D) + * (+) ^ (+) I I I I * | | V V V V - * R1 --I--> R2 O3 R4 R5 R6 - * ^ + * R1 --I--> R2 O3 R4(D) R5 R6 + * ^ (-) * | * | * jack */ @Test - public void test200DisableSomeAssignments() throws Exception { - final String TEST_NAME = "test200DisableSomeAssignments"; + public void test200AddConditions() throws Exception { + final String TEST_NAME = "test200AddConditions"; TestUtil.displayTestTile(this, TEST_NAME); // GIVEN @@ -614,11 +559,287 @@ public void test200DisableSomeAssignments() throws Exception { // WHEN createObjects(true, task, result, () -> { - disableRoles("MMR1 R2 MR3 R4"); + disableRoles("R4"); + addConditionToRoles("MR1+ MR30 MR4-"); + addConditionToAssignments("R1-MR1+ MR1-MMR1+ R1-R2- MR2-O3+"); }); // THEN + // TODO check e.g. membershipRef for roles + } + + + @Test + public void test210AssignR1ToJack() throws Exception { + final String TEST_NAME = "test210AssignR1ToJack"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestAssignmentProcessor.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + LensContext context = createContextForRoleAssignment(USER_JACK_OID, R1_OID, null, null, result); + context.getFocusContext().swallowToPrimaryDelta( + DeltaBuilder.deltaFor(UserType.class, prismContext) + .item(UserType.F_NAME).replace(PolyString.fromOrig("jack1")) + .asItemDelta()); + + // WHEN + assignmentProcessor.processAssignmentsProjections(context, clock.currentTimeXMLGregorianCalendar(), task, result); + + // THEN + display("Output context", context); + display("Evaluated assignment triple", context.getEvaluatedAssignmentTriple()); + + result.computeStatus(); + assertSuccess("Assignment processor failed (result)", result); + + Collection evaluatedAssignments = assertAssignmentTripleSetSize(context, 0, 1, 0); + EvaluatedAssignmentImpl evaluatedAssignment = evaluatedAssignments.iterator().next(); + assertEquals("Wrong evaluatedAssignment.isValid", true, evaluatedAssignment.isValid()); + + // R4 is not in plusInvalid, because only directly assigned targets are listed among targets (see validityOverride) + assertTargets(evaluatedAssignment, "R1", null, "MR1 MMR1", null, "R2 MR2", null); + assertMembershipRef(evaluatedAssignment, "R1"); + assertOrgRef(evaluatedAssignment, null); + assertDelegation(evaluatedAssignment, null); + + // R4-1 is not in plusInvalid (see above) + assertConstructions(evaluatedAssignment, "R1-1", null, "MR1-2 MMR1-3", null, "R2-1 MR2-2", null); + assertFocusMappings(evaluatedAssignment, "R1-1 MR1-2 MMR1-3"); + assertFocusPolicyRules(evaluatedAssignment, "R1-1 MR1-2 MMR1-3"); + + assertTargetPolicyRules(evaluatedAssignment, "R1-0 MR1-1 MMR1-2", "R1-0 MR1-1 MMR1-2"); + assertAuthorizations(evaluatedAssignment, "R1"); + assertGuiConfig(evaluatedAssignment, "R1"); + } + + + private void disableRoles(String text) { + for (String name : getList(text)) { + AbstractRoleType role = findRole(name); + if (role.getActivation() == null) { + role.setActivation(new ActivationType(prismContext)); + } + role.getActivation().setAdministrativeStatus(ActivationStatusType.DISABLED); + } + } + + private void addConditionToRoles(String text) { + for (String item : getList(text)) { + String name = StringUtils.substring(item, 0, -1); + char conditionType = item.charAt(item.length() - 1); + AbstractRoleType role = findRole(name); + role.setCondition(createCondition(conditionType)); + } + } + + private void disableAssignments(String text) { + for (String assignmentText : getList(text)) { + AssignmentType assignment = findAssignmentOrInducement(assignmentText); + if (assignment.getActivation() == null) { + assignment.setActivation(new ActivationType(prismContext)); + } + assignment.getActivation().setAdministrativeStatus(ActivationStatusType.DISABLED); + } + } + + private AssignmentType findAssignmentOrInducement(String assignmentText) { + String[] split = StringUtils.split(assignmentText, "-"); + AbstractRoleType source = findRole(split[0]); + AbstractRoleType target = findRole(split[1]); + return findAssignmentOrInducement(source, target); + } + + private void addConditionToAssignments(String text) { + for (String item : getList(text)) { + String assignmentText = StringUtils.substring(item, 0,-1); + char conditionType = item.charAt(item.length() - 1); + AssignmentType assignment = findAssignmentOrInducement(assignmentText); + assignment.setCondition(createCondition(conditionType)); + } + } + + private MappingType createCondition(char conditionType) { + ScriptExpressionEvaluatorType script = new ScriptExpressionEvaluatorType(); + switch (conditionType) { + case '+': script.setCode("basic.stringify(name) == 'jack1'"); break; + case '-': script.setCode("basic.stringify(name) == 'jack'"); break; + case '0': script.setCode("basic.stringify(name) == 'never there'"); break; + default: throw new AssertionError(conditionType); + } + ExpressionType expression = new ExpressionType(); + expression.getExpressionEvaluator().add(new ObjectFactory().createScript(script)); + VariableBindingDefinitionType source = new VariableBindingDefinitionType(); + source.setPath(new ItemPath(UserType.F_NAME).asItemPathType()); + MappingType rv = new MappingType(); + rv.setExpression(expression); + rv.getSource().add(source); + return rv; + } + + private AssignmentType findAssignmentOrInducement(AbstractRoleType source, AbstractRoleType target) { + return Stream.concat(source.getAssignment().stream(), source.getInducement().stream()) + .filter(a -> a.getTargetRef() != null && target.getOid().equals(a.getTargetRef().getOid())) + .findFirst() + .orElseThrow(() -> new IllegalStateException(source + " contains no assignment/inducement to " + target)); + } + + private AbstractRoleType findRole(String name) { + return (AbstractRoleType) roles.stream().filter(r -> name.equals(r.getName().getOrig())).findFirst() + .orElseThrow(() -> new IllegalStateException("No role " + name)); + } + + private void assertDelegation(EvaluatedAssignmentImpl evaluatedAssignment, String text) { + assertPrismRefValues("delegationRef", evaluatedAssignment.getDelegationRefVals(), findRoles(text)); + } + + private void assertOrgRef(EvaluatedAssignmentImpl evaluatedAssignment, String text) { + assertPrismRefValues("orgRef", evaluatedAssignment.getOrgRefVals(), findRoles(text)); + } + + private void assertMembershipRef(EvaluatedAssignmentImpl evaluatedAssignment, String text) { + assertPrismRefValues("membershipRef", evaluatedAssignment.getMembershipRefVals(), findRoles(text)); + } + + private List findRoles(String text) { + return getList(text).stream().map(n -> findRole(n)).collect(Collectors.toList()); + } + + private void induce(AbstractRoleType source, AbstractRoleType target, int inducementOrder) { + AssignmentType inducement = ObjectTypeUtil.createAssignmentTo(target.asPrismObject()); + if (inducementOrder > 1) { + inducement.setOrder(inducementOrder); + } + source.getInducement().add(inducement); + } + + private void assign(RoleType source, RoleType target) { + AssignmentType assignment = ObjectTypeUtil.createAssignmentTo(target.asPrismObject()); + source.getAssignment().add(assignment); + } + + private RoleType createRole(int level, int number) { + return prepareAbstractRole(new RoleType(prismContext), level, number, "R"); + } + + private OrgType createOrg(int number) { + return prepareAbstractRole(new OrgType(prismContext), 1, number, "O"); + } + + private R prepareAbstractRole(R abstractRole, int level, int number, String nameSymbol) { + String name = StringUtils.repeat('M', level-1) + nameSymbol + number; + String oid = getRoleOid(name); + + abstractRole.setName(PolyStringType.fromOrig(name)); + abstractRole.setOid(oid); + + // constructions + for (int i = 0; i <= CONSTRUCTION_LEVELS; i++) { + ConstructionType c = new ConstructionType(prismContext); + c.setDescription(name + "-" + i); + c.setResourceRef(ObjectTypeUtil.createObjectRef(RESOURCE_DUMMY_EMPTY_OID, ObjectTypes.RESOURCE)); + AssignmentType a = new AssignmentType(prismContext); + a.setDescription("Assignment for " + c.getDescription()); + a.setConstruction(c); + addAssignmentOrInducement(abstractRole, i, a); + } + + // focus mappings + for (int i = 0; i <= FOCUS_MAPPING_LEVELS; i++) { + MappingType mapping = new MappingType(); + mapping.setName(name + "-" + i); + VariableBindingDefinitionType source = new VariableBindingDefinitionType(); + source.setPath(new ItemPath(UserType.F_NAME).asItemPathType()); + mapping.getSource().add(source); + VariableBindingDefinitionType target = new VariableBindingDefinitionType(); + target.setPath(new ItemPath(UserType.F_DESCRIPTION).asItemPathType()); + mapping.setTarget(target); + MappingsType mappings = new MappingsType(prismContext); + mappings.getMapping().add(mapping); + AssignmentType a = new AssignmentType(prismContext); + a.setFocusMappings(mappings); + addAssignmentOrInducement(abstractRole, i, a); + } + + // policy rules + for (int i = 0; i <= POLICY_RULES_LEVELS; i++) { + PolicyRuleType rule = new PolicyRuleType(prismContext); + rule.setName(name + "-" + i); + AssignmentType a = new AssignmentType(prismContext); + a.setPolicyRule(rule); + addAssignmentOrInducement(abstractRole, i, a); + } + + // authorization + AuthorizationType authorization = new AuthorizationType(prismContext); + authorization.getAction().add(name); + abstractRole.getAuthorization().add(authorization); + // admin gui config + AdminGuiConfigurationType guiConfig = new AdminGuiConfigurationType(); + guiConfig.setPreferredDataLanguage(name); + abstractRole.setAdminGuiConfiguration(guiConfig); + return abstractRole; + } + + private void addAssignmentOrInducement(R abstractRole, int order, AssignmentType assignment) { + if (order == 0) { + abstractRole.getAssignment().add(assignment); + } else { + assignment.setOrder(order); + abstractRole.getInducement().add(assignment); + } + } + + private static String getRoleOid(String name) { + return "99999999-0000-0000-0000-" + StringUtils.repeat('0', 12-name.length()) + name; + } + + private void createObjects(boolean deleteFirst, Task task, OperationResult result, Runnable adjustment) throws Exception { + role1 = createRole(1, 1); + role2 = createRole(1, 2); + org3 = createOrg(3); + role4 = createRole(1, 4); + role5 = createRole(1, 5); + role6 = createRole(1, 6); + metarole1 = createRole(2, 1); + metarole2 = createRole(2, 2); + metarole3 = createRole(2, 3); + metarole4 = createRole(2, 4); + metametarole1 = createRole(3, 1); + assign(role1, metarole1); + assign(role2, metarole2); + assign(metarole1, metametarole1); + induce(role1, role2, 1); + induce(metarole1, metarole3, 1); + induce(metarole1, role4, 2); + induce(metarole2, org3, 2); + induce(metarole3, role5, 2); + induce(metarole4, role6, 2); + induce(metametarole1, metarole4, 2); + + roles = Arrays + .asList(role1, role2, org3, role4, role5, role6, metarole1, metarole2, metarole3, metarole4, metametarole1); + + if (adjustment != null) { + adjustment.run(); + } + + // for (ObjectType role : roles) { + // System.out.println(prismContext.xmlSerializer().serialize(role.asPrismObject())); + // } + + // TODO implement repoAddObjects with overwrite option + if (deleteFirst) { + for (ObjectType role : roles) { + repositoryService.deleteObject(role.getClass(), role.getOid(), result); + } + } + + repoAddObjects(roles, result); + recomputeAndRefreshObjects(roles, task, result); + displayObjectTypeCollection("objects", roles); } }