Skip to content

Commit

Permalink
Collecting policy rules even for non-member/non-deputy relations (to …
Browse files Browse the repository at this point in the history
…support MID-3799:1).
  • Loading branch information
mederly committed Mar 13, 2017
1 parent a980ed0 commit 46d3938
Show file tree
Hide file tree
Showing 6 changed files with 314 additions and 217 deletions.
Expand Up @@ -19,6 +19,7 @@
import com.evolveum.midpoint.util.DebugDumpable;

import javax.xml.namespace.QName;
import java.util.Collection;

/**
* @author semancik
Expand All @@ -39,4 +40,6 @@ public interface EvaluationOrder extends DebugDumpable {
int getMatchingRelationOrder(QName relation);

String shortDump();

Collection<QName> getExtraRelations();
}

Large diffs are not rendered by default.

Expand Up @@ -27,11 +27,15 @@
import com.evolveum.midpoint.prism.xml.XsdTypeMapper;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.QNameUtil;
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 org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

/**
* Primary duty of this class is to be a part of assignment path. (This is what is visible through its interface,
* AssignmentPathSegment.) However, it also serves as a place where auxiliary information about assignment evaluation
Expand Down Expand Up @@ -153,7 +157,6 @@ public class AssignmentPathSegmentImpl implements AssignmentPathSegment {
* by "other" relations is not collected.
*
* Both of this can be overridden by using specific orderConstraints on particular inducement.
* (TODO this is just an idea, not implemented yet)
* Set of order constraint is considered to match evaluation order with "other" relations, if for each such "other"
* relation it contains related constraint. So, if one explicitly wants an inducement to be applied when
* "approver" relation is encountered, he may do so.
Expand All @@ -180,15 +183,15 @@ public class AssignmentPathSegmentImpl implements AssignmentPathSegment {
*
* When evaluating jack->Pirate assignment, rule1 would not be normally taken into account, because its assignment
* (Pirate->rule1) has an order of 2. However, we want to collect it - but not as an item related to focus, but
* as an item related to evaluated assignment's target. Therefore besides isMatchingOrder we maintain isMatchingOrderPlusOne
* as an item related to evaluated assignment's target. Therefore besides isMatchingOrder we maintain isMatchingOrderForTarget
* that marks all segments (assignments/inducements) that contain policy rules relevant to the evaluated assignment's target.
*
* TODO how exactly do we compute it
*/
private Boolean isMatchingOrder = null;
private EvaluationOrder evaluationOrder;

private Boolean isMatchingOrderPlusOne = null;
private Boolean isMatchingOrderForTarget = null;
private EvaluationOrder evaluationOrderForTarget;

private boolean processMembership = false;

Expand Down Expand Up @@ -268,13 +271,25 @@ public EvaluationOrder getEvaluationOrder() {
}

public void setEvaluationOrder(EvaluationOrder evaluationOrder) {
setEvaluationOrder(evaluationOrder, null, null);
setEvaluationOrder(evaluationOrder, null);
}

public void setEvaluationOrder(EvaluationOrder evaluationOrder, Boolean matchingOrder, Boolean matchingOrderPlusOne) {
public void setEvaluationOrder(EvaluationOrder evaluationOrder, Boolean matchingOrder) {
this.evaluationOrder = evaluationOrder;
this.isMatchingOrder = matchingOrder;
this.isMatchingOrderPlusOne = matchingOrderPlusOne;
}

public EvaluationOrder getEvaluationOrderForTarget() {
return evaluationOrderForTarget;
}

public void setEvaluationOrderForTarget(EvaluationOrder evaluationOrder) {
setEvaluationOrderForTarget(evaluationOrder, null);
}

public void setEvaluationOrderForTarget(EvaluationOrder evaluationOrderForTarget, Boolean matching) {
this.evaluationOrderForTarget = evaluationOrderForTarget;
this.isMatchingOrderForTarget = matching;
}

public ObjectType getOrderOneObject() {
Expand All @@ -299,44 +314,54 @@ public void setProcessMembership(boolean processMembership) {
*/
public boolean isMatchingOrder() {
if (isMatchingOrder == null) {
isMatchingOrder = computeMatchingOrder(getAssignment(), evaluationOrder, 0);
isMatchingOrder = computeMatchingOrder(evaluationOrder, getAssignment());
}
return isMatchingOrder;
}

public boolean isMatchingOrderPlusOne() {
if (isMatchingOrderPlusOne == null) {
isMatchingOrderPlusOne = computeMatchingOrder(getAssignment(), evaluationOrder, 1);
public boolean isMatchingOrderForTarget() {
if (isMatchingOrderForTarget == null) {
isMatchingOrderForTarget = computeMatchingOrder(evaluationOrderForTarget, getAssignment());
}
return isMatchingOrderPlusOne;
return isMatchingOrderForTarget;
}

static boolean computeMatchingOrder(AssignmentType assignmentType, EvaluationOrder evaluationOrder, int offset) {
static boolean computeMatchingOrder(EvaluationOrder evaluationOrder, AssignmentType assignmentType) {
return computeMatchingOrder(evaluationOrder, assignmentType.getOrder(), assignmentType.getOrderConstraint());
}

static boolean computeMatchingOrder(EvaluationOrder evaluationOrder, Integer assignmentOrder,
List<OrderConstraintsType> assignmentOrderConstraint) {
boolean rv;
if (assignmentType.getOrder() == null && assignmentType.getOrderConstraint().isEmpty()) {
List<QName> extraRelations = new ArrayList<>(evaluationOrder.getExtraRelations());
if (assignmentOrder == null && assignmentOrderConstraint.isEmpty()) {
// compatibility
rv = evaluationOrder.getSummaryOrder() - offset == 1;
rv = evaluationOrder.getSummaryOrder() == 1;
} else {
rv = true;
if (assignmentType.getOrder() != null) {
if (evaluationOrder.getSummaryOrder() - offset != assignmentType.getOrder()) {
if (assignmentOrder != null) {
if (evaluationOrder.getSummaryOrder() != assignmentOrder) {
rv = false;
}
}
for (OrderConstraintsType orderConstraint : assignmentType.getOrderConstraint()) {
if (!isMatchingConstraint(orderConstraint, evaluationOrder, offset)) {
for (OrderConstraintsType orderConstraint : assignmentOrderConstraint) {
if (!isMatchingConstraint(orderConstraint, evaluationOrder)) {
rv = false;
break;
}
extraRelations.removeIf(r -> QNameUtil.match(r, orderConstraint.getRelation()));
}
}
LOGGER.trace("computeMatchingOrder => {}, for offset={}; assignment.order={}, assignment.orderConstraint={}, evaluationOrder={} ... assignment = {}",
rv, offset, assignmentType.getOrder(), assignmentType.getOrderConstraint(), evaluationOrder);
if (!extraRelations.isEmpty()) {
rv = false;
}
LOGGER.trace("computeMatchingOrder => {}, for offset={}; assignment.order={}, assignment.orderConstraint={}, evaluationOrder={}, remainingExtraRelations={}",
rv, assignmentOrder, assignmentOrderConstraint, evaluationOrder, extraRelations);
return rv;
}

private static boolean isMatchingConstraint(OrderConstraintsType orderConstraint, EvaluationOrder evaluationOrder, int offset) {
int evaluationOrderInt = evaluationOrder.getMatchingRelationOrder(orderConstraint.getRelation()) - offset;
private static boolean isMatchingConstraint(OrderConstraintsType orderConstraint, EvaluationOrder evaluationOrder) {
int evaluationOrderInt = evaluationOrder.getMatchingRelationOrder(orderConstraint.getRelation());
if (orderConstraint.getOrder() != null) {
return orderConstraint.getOrder() == evaluationOrderInt;
} else {
Expand Down Expand Up @@ -401,8 +426,8 @@ public String toString() {
if (isMatchingOrder()) { // here is a side effect but most probably it's harmless
sb.append("(match)");
}
if (isMatchingOrderPlusOne()) { // the same here
sb.append("(match+1)");
if (isMatchingOrderForTarget()) { // the same here
sb.append("(match-target)");
}
sb.append(": ");
sb.append(source).append(" ");
Expand Down Expand Up @@ -459,7 +484,7 @@ public String debugDump(int indent) {
DebugUtil.debugDumpWithLabelLn(sb, "target", target==null?"null":target.toString(), indent + 1);
DebugUtil.debugDumpWithLabelLn(sb, "evaluationOrder", evaluationOrder, indent + 1);
DebugUtil.debugDumpWithLabelLn(sb, "isMatchingOrder", isMatchingOrder, indent + 1);
DebugUtil.debugDumpWithLabelLn(sb, "isMatchingOrderPlusOne", isMatchingOrderPlusOne, indent + 1);
DebugUtil.debugDumpWithLabelLn(sb, "isMatchingOrderForTarget", isMatchingOrderForTarget, indent + 1);
DebugUtil.debugDumpWithLabelLn(sb, "relation", relation, indent + 1);
DebugUtil.debugDumpWithLabelLn(sb, "pathToSourceValid", pathToSourceValid, indent + 1);
DebugUtil.debugDumpWithLabelLn(sb, "validityOverride", validityOverride, indent + 1);
Expand Down
Expand Up @@ -125,7 +125,9 @@ public String debugDump(int indent) {
sb.append("\n");
DebugUtil.debugDumpWithLabel(sb, "Assignment", String.valueOf(assignment), indent + 1);
sb.append("\n");
DebugUtil.debugDumpWithLabel(sb, "isValid", isValid, indent + 1);
DebugUtil.debugDumpWithLabel(sb, "EvaluateConstructions", evaluateConstructions, indent + 1);
sb.append("\n");
DebugUtil.debugDumpWithLabel(sb, "Valid", isValid, indent + 1);
return sb.toString();
}

Expand Down
Expand Up @@ -15,8 +15,10 @@
*/
package com.evolveum.midpoint.model.impl.lens;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import javax.xml.namespace.QName;

Expand Down Expand Up @@ -168,4 +170,11 @@ public String shortDump() {
sb.append("=").append(summaryOrder);
return sb.toString();
}

@Override
public Collection<QName> getExtraRelations() {
return orderMap.keySet().stream()
.filter(r -> !DeputyUtils.isMembershipRelation(r) && !DeputyUtils.isDelegationRelation(r))
.collect(Collectors.toSet());
}
}
Expand Up @@ -23,6 +23,7 @@
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.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ActivationUtil;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
Expand Down Expand Up @@ -139,7 +140,8 @@ public void test010AssignR1ToJack() throws Exception {
EvaluatedAssignmentImpl<UserType> evaluatedAssignment = evaluatedAssignments.iterator().next();
assertEquals("Wrong evaluatedAssignment.isValid", true, evaluatedAssignment.isValid());

assertTargets(evaluatedAssignment, "R1 R2 O3 R4 R5 R6 MR1 MR2 MR3 MR4 MMR1", null, null, null, null, null);
assertTargets(evaluatedAssignment, true, "R1 R2 O3 R4 R5 R6", null, null, null, null, null);
assertTargets(evaluatedAssignment, false, "MR1 MR2 MR3 MR4 MMR1", null, null, null, null, null);
assertMembershipRef(evaluatedAssignment, "R1 R2 O3 R4 R5 R6");
assertOrgRef(evaluatedAssignment, "O3");
assertDelegation(evaluatedAssignment, null);
Expand Down Expand Up @@ -189,6 +191,53 @@ public void test020AssignR1ToJackProjectorDisabled() throws Exception {
context.getFocusContext().getObjectNew().asObjectable().getRoleMembershipRef().size());
}

/**
* As R1 is assigned with the relation=approver, jack will "see" only this role.
* However, we must collect all relevant target policy rules.
*/
@Test
public void test030AssignR1ToJackAsApprover() throws Exception {
final String TEST_NAME = "test030AssignR1ToJackAsApprover";
TestUtil.displayTestTile(this, TEST_NAME);

// GIVEN
Task task = taskManager.createTaskInstance(TestAssignmentProcessor.class.getName() + "." + TEST_NAME);
OperationResult result = task.getResult();

LensContext<UserType> context = createContextForRoleAssignment(USER_JACK_OID, R1_OID, SchemaConstants.ORG_APPROVER, 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<EvaluatedAssignmentImpl> evaluatedAssignments = assertAssignmentTripleSetSize(context, 0, 1, 0);
EvaluatedAssignmentImpl<UserType> evaluatedAssignment = evaluatedAssignments.iterator().next();
assertEquals("Wrong evaluatedAssignment.isValid", true, evaluatedAssignment.isValid());

assertTargets(evaluatedAssignment, false, "R1 R2 O3 R4 R5 R6 MR1 MR2 MR3 MR4 MMR1", null, null, null, null, null);
assertMembershipRef(evaluatedAssignment, "R1");
assertOrgRef(evaluatedAssignment, null);
assertDelegation(evaluatedAssignment, null);

assertConstructions(evaluatedAssignment, "", null, null, null, null, null);
assertFocusMappings(evaluatedAssignment, "");
assertFocusPolicyRules(evaluatedAssignment, "");

// TODO why R4-0 R5-0 R6-0 ?
String expectedThisTargetRules = "R1-0 R4-0 R5-0 R6-0 MR1-1 MR3-1 MR4-1 MMR1-2";
String expectedTargetRules = expectedThisTargetRules + " R2-0 O3-0 MR2-1";
assertTargetPolicyRules(evaluatedAssignment, getList(expectedTargetRules), getList(expectedThisTargetRules));
assertAuthorizations(evaluatedAssignment, "");
assertGuiConfig(evaluatedAssignment, "");
}


/**
* Now disable some roles. Their administrative status is simply set to DISABLED.
*
Expand Down Expand Up @@ -249,7 +298,8 @@ public void test110AssignR1ToJack() throws Exception {
EvaluatedAssignmentImpl<UserType> evaluatedAssignment = evaluatedAssignments.iterator().next();
assertEquals("Wrong evaluatedAssignment.isValid", true, evaluatedAssignment.isValid());

assertTargets(evaluatedAssignment, "R1 MR1", null, null, null, null, null);
assertTargets(evaluatedAssignment, true, "R1", null, null, null, null, null);
assertTargets(evaluatedAssignment, false, "MR1", null, null, null, null, null);
assertMembershipRef(evaluatedAssignment, "R1");
assertOrgRef(evaluatedAssignment, null);
assertDelegation(evaluatedAssignment, null);
Expand Down Expand Up @@ -326,7 +376,8 @@ public void test160AssignR1ToJack() throws Exception {
EvaluatedAssignmentImpl<UserType> evaluatedAssignment = evaluatedAssignments.iterator().next();
assertEquals("Wrong evaluatedAssignment.isValid", true, evaluatedAssignment.isValid());

assertTargets(evaluatedAssignment, "R1 MR1 MMR1 MR4 R4", null, null, null, null, null);
assertTargets(evaluatedAssignment, true, "R1 R4", null, null, null, null, null);
assertTargets(evaluatedAssignment, false, "MR1 MMR1 MR4", null, null, null, null, null);
assertMembershipRef(evaluatedAssignment, "R1 R4");
assertOrgRef(evaluatedAssignment, null);
assertDelegation(evaluatedAssignment, null);
Expand Down Expand Up @@ -414,7 +465,8 @@ public void test210AssignR1ToJack() throws Exception {
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);
assertTargets(evaluatedAssignment, true, "R1", null, "", null, "R2", null);
assertTargets(evaluatedAssignment, false, "", null, "MR1 MMR1", null, "MR2", null);
assertMembershipRef(evaluatedAssignment, "R1");
assertOrgRef(evaluatedAssignment, null);
assertDelegation(evaluatedAssignment, null);
Expand Down Expand Up @@ -712,35 +764,44 @@ private void assertTargetPolicyRules(EvaluatedAssignmentImpl<UserType> evaluated
}

private void assertTargets(EvaluatedAssignmentImpl<UserType> evaluatedAssignment,
Boolean evaluateConstructions,
String zeroValid, String zeroInvalid,
String plusValid, String plusInvalid,
String minusValid, String minusInvalid) {
assertTargets(evaluatedAssignment, getList(zeroValid), getList(zeroInvalid),
assertTargets(evaluatedAssignment, evaluateConstructions, getList(zeroValid), getList(zeroInvalid),
getList(plusValid), getList(plusInvalid), getList(minusValid), getList(minusInvalid));
}

private void assertTargets(EvaluatedAssignmentImpl<UserType> evaluatedAssignment,
Boolean evaluateConstructions,
List<String> zeroValid, List<String> zeroInvalid,
List<String> plusValid, List<String> plusInvalid,
List<String> minusValid, List<String> minusInvalid) {
assertTargets("zero", evaluatedAssignment.getRoles().getZeroSet(), zeroValid, zeroInvalid);
assertTargets("plus", evaluatedAssignment.getRoles().getPlusSet(), plusValid, plusInvalid);
assertTargets("minus", evaluatedAssignment.getRoles().getMinusSet(), minusValid, minusInvalid);
assertTargets("zero", evaluatedAssignment.getRoles().getZeroSet(), evaluateConstructions, zeroValid, zeroInvalid);
assertTargets("plus", evaluatedAssignment.getRoles().getPlusSet(), evaluateConstructions, plusValid, plusInvalid);
assertTargets("minus", evaluatedAssignment.getRoles().getMinusSet(), evaluateConstructions, minusValid, minusInvalid);
}

private void assertTargets(String type, Collection<EvaluatedAssignmentTargetImpl> targets, List<String> expectedValid,
List<String> expectedInvalid) {
private void assertTargets(String type, Collection<EvaluatedAssignmentTargetImpl> targets, Boolean evaluateConstructions,
List<String> expectedValid, List<String> expectedInvalid) {
targets = CollectionUtils.emptyIfNull(targets);
Collection<EvaluatedAssignmentTargetImpl> realValid = targets.stream().filter(t -> t.isValid()).collect(Collectors.toList());
Collection<EvaluatedAssignmentTargetImpl> 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),
Collection<EvaluatedAssignmentTargetImpl> realValid = targets.stream()
.filter(t -> t.isValid() && matchesConstructions(t, evaluateConstructions)).collect(Collectors.toList());
Collection<EvaluatedAssignmentTargetImpl> realInvalid = targets.stream()
.filter(t -> !t.isValid() && matchesConstructions(t, evaluateConstructions)).collect(Collectors.toList());
String ec = evaluateConstructions != null ? " (evaluateConstructions: " + evaluateConstructions + ")" : "";
assertEquals("Wrong # of valid targets in " + type + " set" + ec, expectedValid.size(), realValid.size());
assertEquals("Wrong # of invalid targets in " + type + " set" + ec, expectedInvalid.size(), realInvalid.size());
assertEquals("Wrong valid targets in " + type + " set" + ec, new HashSet<>(expectedValid),
realValid.stream().map(t -> t.getTarget().getName().getOrig()).collect(Collectors.toSet()));
assertEquals("Wrong invalid targets in " + type + " set", new HashSet<>(expectedInvalid),
assertEquals("Wrong invalid targets in " + type + " set" + ec, new HashSet<>(expectedInvalid),
realInvalid.stream().map(t -> t.getTarget().getName().getOrig()).collect(Collectors.toSet()));
}

private boolean matchesConstructions(EvaluatedAssignmentTargetImpl t, Boolean evaluateConstructions) {
return evaluateConstructions == null || t.isEvaluateConstructions() == evaluateConstructions;
}

private void assertConstructions(EvaluatedAssignmentImpl<UserType> evaluatedAssignment,
String zeroValid, String zeroInvalid,
String plusValid, String plusInvalid,
Expand Down

0 comments on commit 46d3938

Please sign in to comment.