From 89648de5a6b5eb28e2f6f85effea0ca247491c42 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Sat, 25 Mar 2017 00:22:23 +0100 Subject: [PATCH] (Approximate) fix for MID-3815: Indirect relation=manager assignment of Authorization (MP only) role --- .../model/impl/lens/AssignmentEvaluator.java | 11 +- .../impl/lens/AssignmentPathSegmentImpl.java | 38 ++++- .../impl/lens/TestAssignmentProcessor2.java | 144 +++++++++++++++++- 3 files changed, 184 insertions(+), 9 deletions(-) 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 64c0a471c96..c490d38a958 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 @@ -639,8 +639,8 @@ private void evaluateSegmentTarget(AssignmentPathSegmentImpl segment, PlusMinusZ evaluateAssignment(segment, mode, isValid, ctx, targetType, relation, roleAssignment); } - boolean matchesOrder = AssignmentPathSegmentImpl.computeMatchingOrder(segment.getEvaluationOrder(), 1, Collections.emptyList()); - if (matchesOrder && targetType instanceof AbstractRoleType && isNonNegative(mode)) { + //boolean matchesOrder = AssignmentPathSegmentImpl.computeMatchingOrder(segment.getEvaluationOrder(), 1, Collections.emptyList()); + if (segment.isMatchingOrder() && targetType instanceof AbstractRoleType && isNonNegative(mode)) { for (AuthorizationType authorizationType: ((AbstractRoleType)targetType).getAuthorization()) { Authorization authorization = createAuthorization(authorizationType, targetType.toString()); if (!ctx.evalAssignment.getAuthorizations().contains(authorization)) { @@ -755,11 +755,14 @@ private void evaluateInducement(AssignmentPathSegmentImpl segment, PlusMinusZero nextEvaluationOrder = EvaluationOrderImpl.UNDEFINED; nextEvaluationOrderForTarget = EvaluationOrderImpl.UNDEFINED; } - } else { + } else if (inducement.getOrderConstraint().isEmpty()) { + // i.e. order is null or 1 nextEvaluationOrder = segment.getEvaluationOrder(); nextEvaluationOrderForTarget = segment.getEvaluationOrderForTarget(); + } else { + nextEvaluationOrder = EvaluationOrderImpl.UNDEFINED; + nextEvaluationOrderForTarget = EvaluationOrderImpl.UNDEFINED; } - // TODO undefined if intervals nextSegment.setEvaluationOrder(nextEvaluationOrder, nextIsMatchingOrder); nextSegment.setEvaluationOrderForTarget(nextEvaluationOrderForTarget, nextIsMatchingOrderForTarget); 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 e4d65ddae80..3de0da8c37d 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 @@ -94,7 +94,7 @@ public class AssignmentPathSegmentImpl implements AssignmentPathSegment { * it with the order (or, more generally, order constraints) of the inducement. If they match, we say that inducement * has matching order. * - * As for #2: It is not usual that inducements have targets with another assignments, i.e. that evaluation continues + * As for #2: It is not usual that inducements have targets (roles) with another assignments, i.e. that evaluation continues * after inducement segments. But it definitely could happen. We can look at it this way: inducement is something like * a "shortcut" that creates an assignment where no assignment was before. E.g. if we have R1 -A-> MR1 -I-> MR2, * the "newly created" assignment is R1 -A-> MR2. I.e. as if the " -A-> MR1 -I-> " part was just replaced by " -A-> ". @@ -106,12 +106,42 @@ public class AssignmentPathSegmentImpl implements AssignmentPathSegment { * TODO think this through, perhaps based on concrete examples * * It is almost certain that for some inducements we would not be able to determine the resulting order. - * Such problematic inducements are those that do not have strict order, but an interval of orders instead. + * Such problematic inducements are those that do not have strict (scalar) order constraint, but something + * more complex, e.g. interval of orders. * * Until no better algorithm is devised, we will do an approximation: when "traditional" inducement order is given, * the we will compute the resulting order as "previous - (N-1)", where N is the order of the inducement - * (unspecified means 1). But beware, we will not manipulate evaluation order parts that are specific to relations. - * So, it is not safe to combine "higher-order inducements with targets" with non-scalar order constraints. + * (unspecified means 1). But beware, we will not decrease evaluation order for non-default relations! + * Also, if the traditional inducement order is not given, but orderConstraints are present instead, we will + * simply stop evaluation of further path segments altogether. + * + * So, it is not supported to combine inducements with complex (non-scalar) order constraints with further + * targets (assignments/inducements). Only membership, authorizations and GUI config from such inducement targets + * will be collected. See test500/test510 in TestAssignmentProcessor2. + * + * Unfortunately, this is by no means a rare case. It can easily occur when org structures are used. + * As depicted in that test, imagine this: + * + * Org1 -----I----+ Org2 -----I----+ + * ^ | (orderConstraints 1..N) ^ | (orderConstraints: manager: 1) + * | | | | + * | V | V + * Org11 Admin Org21 Admin + * ^ ^ + * | (manager) + * | | + * jack jack + * + * So, we are in trouble when we try to attach an inducement to a organization hierarchy top; to be applied to + * users that are part of the organizational structure (at any level). The reason is that the inducement has + * to have non-scalar constraint. + * + * TODO think this through. + * + * Maybe something like "continue with specified evaluation order" could be provided in OrgX->Admin inducement. + * But this would be quite complicated. + * + * ---- * * Because evaluation order can "increase" and "decrease", it is possible that it goes to zero or below, and then * increase back to positive numbers. Is that OK? Imagine this: 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 372a82b3e1f..48257b99b3e 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 @@ -99,6 +99,7 @@ public class TestAssignmentProcessor2 extends AbstractLensTest { private static final boolean FIRST_PART = true; private static final boolean SECOND_PART = true; private static final boolean THIRD_PART = true; + private static final boolean FOURTH_PART = true; private static final File RESOURCE_DUMMY_EMPTY_FILE = new File(TEST_DIR, "resource-dummy-empty.xml"); private static final String RESOURCE_DUMMY_EMPTY_OID = "10000000-0000-0000-0000-00000000EEE4"; @@ -122,6 +123,10 @@ public class TestAssignmentProcessor2 extends AbstractLensTest { private RoleType rolePirate, roleSailor, roleMan, roleWoman, roleHuman; private RoleType metaroleCrewMember, metarolePerson; + // fourth part + private OrgType org1, org11, org2, org21; + private RoleType roleAdmin; + private List objects; private static final String ROLE_R1_OID = getRoleOid("R1"); @@ -129,6 +134,8 @@ public class TestAssignmentProcessor2 extends AbstractLensTest { private static final String ROLE_MR1_OID = getRoleOid("MR1"); private static final String ROLE_PIRATE_OID = getRoleOid("Pirate"); private static final String ROLE_MAN_OID = getRoleOid("Man"); + private static final String ORG11_OID = getRoleOid("org11"); + private static final String ORG21_OID = getRoleOid("org21"); @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { @@ -1399,6 +1406,111 @@ public static void finishCallback(String desc) { } } + /** + * Testing non-scalar constraints (MID-3815) + * + * Org1 -----I----+ Org2 -----I----+ + * ^ | (orderConstraints 1..N) ^ | (orderConstraints: manager: 1) + * | | | | + * | V | V + * Org11 Admin Org21 Admin + * ^ ^ + * | (manager) + * | | + * jack jack + * + * Authorizations and GUI configuration from role Admin should be given to jack. + */ + + @Test(enabled = FOURTH_PART) + public void test500AssignJackOrg11() throws Exception { + final String TEST_NAME = "test500AssignJackOrg11"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestAssignmentProcessor.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + createObjectsInFourthPart(false, task, result, null); + + LensContext context = createContextForAssignment(UserType.class, USER_JACK_OID, OrgType.class, ORG11_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()); + + assertTargets(evaluatedAssignment, true, "org11 Admin", null, null, null, null, null); + assertTargets(evaluatedAssignment, false, "org1", null, null, null, null, null); + assertMembershipRef(evaluatedAssignment, "org11 Admin"); + assertOrgRef(evaluatedAssignment, "org11"); + assertDelegation(evaluatedAssignment, ""); + + String expectedItems = "org11-1 org1-2"; + assertConstructions(evaluatedAssignment, expectedItems, null, null, null, null, null); + assertFocusMappings(evaluatedAssignment, expectedItems); + assertFocusPolicyRules(evaluatedAssignment, expectedItems); + + assertTargetPolicyRules(evaluatedAssignment, + "org11-0 org1-1", + ""); + assertAuthorizations(evaluatedAssignment, "org11 Admin"); + assertGuiConfig(evaluatedAssignment, "org11 Admin"); + } + + @Test(enabled = FOURTH_PART) + public void test510AssignJackOrg21() throws Exception { + final String TEST_NAME = "test510AssignJackOrg21"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestAssignmentProcessor.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + LensContext context = createContextForAssignment(UserType.class, USER_JACK_OID, OrgType.class, ORG21_OID, + new QName("manager"), null, result); // intentionally unqualified + + // 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()); + + assertTargets(evaluatedAssignment, true, "org21 Admin", null, null, null, null, null); + assertTargets(evaluatedAssignment, false, "org2", null, null, null, null, null); + assertMembershipRef(evaluatedAssignment, "org21 Admin"); + assertOrgRef(evaluatedAssignment, "org21"); + assertDelegation(evaluatedAssignment, ""); + + String expectedItems = "org21-1 org2-2"; + assertConstructions(evaluatedAssignment, expectedItems, null, null, null, null, null); + assertFocusMappings(evaluatedAssignment, expectedItems); + assertFocusPolicyRules(evaluatedAssignment, expectedItems); + + assertTargetPolicyRules(evaluatedAssignment, + "org21-0 org2-1", + ""); + assertAuthorizations(evaluatedAssignment, "org21 Admin"); + assertGuiConfig(evaluatedAssignment, "org21 Admin"); + } + //region ============================================================= helper methods (preparing scenarios) private void createObjectsInFirstPart(boolean deleteFirst, Task task, OperationResult result, Runnable adjustment) throws Exception { @@ -1478,6 +1590,36 @@ private void createObjectsInThirdPart(boolean deleteFirst, Task task, OperationR createObjects(deleteFirst, task, result, adjustment); } + private void createObjectsInFourthPart(boolean deleteFirst, Task task, OperationResult result, Runnable adjustment) throws Exception { + org1 = createOrg("org1"); + org11 = createOrg("org11"); + org2 = createOrg("org2"); + org21 = createOrg("org21"); + roleAdmin = createRole("Admin"); + + assign(org11, org1); + assign(org21, org2); + // org1->roleAdmin + AssignmentType inducement = ObjectTypeUtil.createAssignmentTo(roleAdmin.asPrismObject()) + .beginOrderConstraint() + .orderMin("1") + .orderMax("unbounded") + .end(); + org1.getInducement().add(inducement); + + // org2->roleAdmin + AssignmentType inducement2 = ObjectTypeUtil.createAssignmentTo(roleAdmin.asPrismObject()) + .beginOrderConstraint() + .order(1) + .relation(SchemaConstants.ORG_MANAGER) + .end(); + org2.getInducement().add(inducement2); + + objects = new ArrayList<>(Arrays.asList(roleAdmin, org1, org11, org2, org21)); + + createObjects(deleteFirst, task, result, adjustment); + } + private void createCustomConstruction(RoleType role, String name, int order) { ConstructionType c = new ConstructionType(prismContext); c.setDescription(name); @@ -1597,7 +1739,7 @@ private void induce(AbstractRoleType source, AbstractRoleType target, int induce source.getInducement().add(inducement); } - private void assign(RoleType source, RoleType target) { + private void assign(AbstractRoleType source, AbstractRoleType target) { AssignmentType assignment = ObjectTypeUtil.createAssignmentTo(target.asPrismObject()); source.getAssignment().add(assignment); }