Skip to content

Commit

Permalink
Fix conflicts preview when pruning is involved
Browse files Browse the repository at this point in the history
When "prune" policy action was present, assignments conflicts were not
previewed correctly, because the pruning action itself fixed the
situation behind the scene - hence, user did not see any conflicts.

This is now fixed by introducing new "ignoreAssignmentPruning" option
(marked as "for internal use" for now).

This resolves MID-8243 and MID-8247.
  • Loading branch information
mederly committed Mar 17, 2023
1 parent b61038f commit ce3a831
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -436,11 +436,11 @@ public List<Conflict> computeConflictsForOnePerson(ObjectReferenceType ref, Task

ObjectDelta<UserType> delta = createUserDelta(user);

PartialProcessingOptionsType processing = new PartialProcessingOptionsType();
processing.setInbound(SKIP);
processing.setProjection(SKIP);

ModelExecuteOptions options = ModelExecuteOptions.create().partialProcessing(processing);
ModelExecuteOptions options = ModelExecuteOptions.create()
.partialProcessing(new PartialProcessingOptionsType()
.inbound(SKIP)
.projection(SKIP))
.ignoreAssignmentPruning();

MidPointApplication mp = MidPointApplication.get();
ModelContext<UserType> ctx = mp.getModelInteractionService()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16844,6 +16844,22 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="ignoreAssignmentPruning" type="xsd:boolean" minOccurs="0" default="false">
<xsd:annotation>
<xsd:documentation>
If true, assignments are not pruned even if "prune" policy action is triggered.
To be used when previewing changes only! Main reason is the shopping cart, when we are interested
in assignments conflicts - and if the assignments are pruned, the conflicts disappear.
See e.g. MID-8243.

Experimental. Only for midPoint internal use.
</xsd:documentation>
<xsd:appinfo>
<a:since>4.7</a:since>
<a:experimental>true</a:experimental>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:long"/>
</xsd:complexType>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,21 @@ public ModelExecuteOptions simulationOptions(SimulationOptionsType options) {
content.setSimulation(options);
return this;
}

@SuppressWarnings("WeakerAccess")
public ModelExecuteOptions ignoreAssignmentPruning(Boolean value) {
content.setIgnoreAssignmentPruning(value);
return this;
}

public ModelExecuteOptions ignoreAssignmentPruning() {
return ignoreAssignmentPruning(true);
}

public static boolean isIgnoreAssignmentPruning(ModelExecuteOptions options) {
return is(options, F_IGNORE_ASSIGNMENT_PRUNING);
}

//endregion

public static ModelExecuteOptionsType toModelExecutionOptionsBean(ModelExecuteOptions options) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,21 +203,25 @@ private <AH extends AssignmentHolderType> void processAssignmentsInternal(
// PROCESSING POLICIES

policyRuleProcessor.evaluateAssignmentPolicyRules(focusContext, task, result);
boolean needToReevaluateAssignments = processPruning(context, evaluatedAssignmentTriple, result);

if (needToReevaluateAssignments) {
LOGGER.debug("Re-evaluating assignments because exclusion pruning rule was triggered");
if (ModelExecuteOptions.isIgnoreAssignmentPruning(context.getOptions())) {
LOGGER.debug("Assignment pruning is ignored because of the model execute option");
} else {
boolean needToReevaluateAssignments = processPruning(context, evaluatedAssignmentTriple, result);
if (needToReevaluateAssignments) {
LOGGER.debug("Re-evaluating assignments because exclusion pruning rule was triggered");

assignmentTripleEvaluator.reset(true);
evaluatedAssignmentTriple = assignmentTripleEvaluator.processAllAssignments();
context.setEvaluatedAssignmentTriple((DeltaSetTriple) evaluatedAssignmentTriple);
assignmentTripleEvaluator.reset(true);
evaluatedAssignmentTriple = assignmentTripleEvaluator.processAllAssignments();
context.setEvaluatedAssignmentTriple((DeltaSetTriple) evaluatedAssignmentTriple);

// TODO implement isMemberOf invocation result change check here! MID-5784
// Actually, we should factor out the relevant code to avoid code duplication.
// TODO implement isMemberOf invocation result change check here! MID-5784
// Actually, we should factor out the relevant code to avoid code duplication.

LOGGER.trace("re-evaluatedAssignmentTriple:\n{}", evaluatedAssignmentTriple.debugDumpLazily());
LOGGER.trace("re-evaluatedAssignmentTriple:\n{}", evaluatedAssignmentTriple.debugDumpLazily());

policyRuleProcessor.evaluateAssignmentPolicyRules(focusContext, task, result);
policyRuleProcessor.evaluateAssignmentPolicyRules(focusContext, task, result);
}
}

policyRuleProcessor.recordAssignmentPolicyRules(focusContext, task, result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
*/
package com.evolveum.midpoint.model.intest.rbac;

import static com.evolveum.midpoint.xml.ns._public.common.common_3.PartialProcessingTypeType.SKIP;

import static org.assertj.core.api.Assertions.*;
import static org.testng.AssertJUnit.*;

Expand All @@ -14,6 +16,7 @@
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import com.evolveum.midpoint.test.TestResource;

Expand Down Expand Up @@ -1554,6 +1557,65 @@ public void test835AddApplicationRoleExplicitly() throws Exception {
.assertRole(ROLE_BUSINESS_2.oid);
}


/**
* When previewing assignment addition with pruning action present, the pruning itself should not be executed.
* Otherwise, the information about the conflict would be lost. (There is a special option for this case.)
*
* We test all cases:
*
* . bidirectional definition (`gold` vs `silver`)
* . unidirectional definition (`silver` vs `bronze`), assigned from either side
*
* MID-8243
*
* Intentionally before global SoD approval rules are enabled in {@link #test900ApplyGlobalPolicyRulesSoDApproval()}.
*/
@Test
public void test840PreviewWithPruning() throws Exception {
testPreviewWithPruning("u970-1", ROLE_PRIZE_GOLD_OID, ROLE_PRIZE_SILVER_OID, 2);
testPreviewWithPruning("u970-2", ROLE_PRIZE_SILVER_OID, ROLE_PRIZE_BRONZE_OID, 1);
testPreviewWithPruning("u970-3", ROLE_PRIZE_BRONZE_OID, ROLE_PRIZE_SILVER_OID, 1);
}

private void testPreviewWithPruning(String username, String existingRoleOid, String newRoleOid, int expectedTriggers)
throws Exception {
Task task = getTestTask();
OperationResult result = task.getResult();

given("user with existing role assignment is created");
UserType user = new UserType()
.name(username)
.assignment(roleAssignment(existingRoleOid));
addObject(user, task, result);

when("preview adding of new role assignment");
ObjectDelta<UserType> delta = deltaFor(UserType.class)
.item(UserType.F_ASSIGNMENT)
.add(roleAssignment(newRoleOid))
.asObjectDelta(user.getOid());

ModelExecuteOptions options = ModelExecuteOptions.create()
.partialProcessing(new PartialProcessingOptionsType()
.inbound(SKIP)
.projection(SKIP))
.ignoreAssignmentPruning();

ModelContext<UserType> ctx = modelInteractionService.previewChanges(List.of(delta), options, task, result);

then("the exclusion triggers are there");
var triggers = ctx.getEvaluatedAssignmentTriple().union().stream()
.flatMap(ea -> ea.getAllTargetsPolicyRules().stream())
.flatMap(r -> r.getAllTriggers(EvaluatedExclusionTrigger.class).stream())
.collect(Collectors.toList());
assertThat(triggers).as("exclusion triggers").hasSize(expectedTriggers);
}

private static AssignmentType roleAssignment(String existingRoleOid) {
return new AssignmentType()
.targetRef(existingRoleOid, RoleType.COMPLEX_TYPE);
}

@Test
public void test900ApplyGlobalPolicyRulesSoDApproval() throws Exception {
// GIVEN
Expand Down

0 comments on commit ce3a831

Please sign in to comment.