Skip to content

Commit

Permalink
MID-8582 fix: approver assignments now produce value metadata for refs
Browse files Browse the repository at this point in the history
Previously, "non-membership" assignments failed to populate value
metadata for roleMembershipRefs which also broke Indirect assignment
report.
  • Loading branch information
virgo47 committed Mar 13, 2023
1 parent 5d9bc3c commit 227688d
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

import com.evolveum.midpoint.common.ActivationComputer;
import com.evolveum.midpoint.model.api.ModelExecuteOptions;
import com.evolveum.midpoint.model.api.context.EvaluatedAssignment;
import com.evolveum.midpoint.model.api.context.EvaluatedAssignmentTarget;
import com.evolveum.midpoint.model.api.context.ProjectionContextKey;
import com.evolveum.midpoint.model.api.context.SynchronizationPolicyDecision;
import com.evolveum.midpoint.model.api.util.ReferenceResolver;
Expand Down Expand Up @@ -1013,7 +1015,7 @@ <F extends ObjectType> void processMembershipAndDelegatedRefs(
for (EvaluatedAssignmentImpl<?> evalAssignment : evaluatedAssignmentTriple.getNonNegativeValues()) { // MID-6403
if (evalAssignment.isValid()) {
LOGGER.trace("Adding references from: {}", evalAssignment);
addRoleReferences(shouldBeRoleRefs, evalAssignment, operationResult);
addRoleReferences(shouldBeRoleRefs, evalAssignment, focusContext, operationResult);
addReferences(shouldBeDelegatedRefs, evalAssignment.getDelegationRefVals());
addReferences(shouldBeArchetypeRefs, evalAssignment.getArchetypeRefVals());
} else {
Expand All @@ -1031,9 +1033,10 @@ <F extends ObjectType> void processMembershipAndDelegatedRefs(
setReferences(focusContext, AssignmentHolderType.F_ARCHETYPE_REF, shouldBeArchetypeRefs);
}

private void addRoleReferences(Collection<PrismReferenceValue> shouldBeRoleRefs,
EvaluatedAssignmentImpl<?> evalAssignment, OperationResult operationResult) throws SchemaException {
addReferences(shouldBeRoleRefs, evalAssignment.getMembershipRefVals());
private void addRoleReferences(
Collection<PrismReferenceValue> shouldBeRoleRefs, EvaluatedAssignmentImpl<?> evaluatedAssignment,
LensFocusContext<?> focusContext, OperationResult operationResult) throws SchemaException {
addReferences(shouldBeRoleRefs, evaluatedAssignment.getMembershipRefVals());

// If sysconfig enables accesses value metadata, we will add them.
SystemConfigurationType sysconfig = systemObjectCache.getSystemConfigurationBean(operationResult);
Expand All @@ -1042,34 +1045,61 @@ private void addRoleReferences(Collection<PrismReferenceValue> shouldBeRoleRefs,
}

for (PrismReferenceValue roleRef : shouldBeRoleRefs) {
List<EvaluatedAssignmentTargetImpl> evaluatedAssignmentTargets =
findEvaluatedAssignmentTargets(roleRef, evalAssignment);
if (evaluatedAssignmentTargets.isEmpty()) {
LOGGER.warn("EvaluatedAssignmentTarget not found for role ref {}", roleRef);
continue;
}
addAssignmentPathValueMetadataValues(roleRef, evaluatedAssignment, focusContext);
}
}

for (EvaluatedAssignmentTargetImpl evaluatedAssignmentTarget : evaluatedAssignmentTargets) {
private void addAssignmentPathValueMetadataValues(PrismReferenceValue roleRef,
EvaluatedAssignmentImpl<?> evaluatedAssignment, LensFocusContext<?> focusContext)
throws SchemaException {
List<EvaluatedAssignmentTarget> evaluatedAssignmentTargets =
findEvaluatedAssignmentTargets(roleRef, evaluatedAssignment);
if (evaluatedAssignmentTargets.isEmpty()) {
// Cases like Approver relations, for which EvaluatedAssignment has empty roles DeltaSetTriple.
AssignmentType assignment = evaluatedAssignment.getAssignment();
ObjectReferenceType assignmentTargetRef = assignment.getTargetRef();
// We can get here with roleRef->X and assignment/targetRef->Y, in which case we do nothing.
if (assignmentTargetRef.getOid().equals(roleRef.getOid())
&& QNameUtil.match(assignmentTargetRef.getRelation(), roleRef.getRelation())) {
addAssignmentPathValueMetadataValue(roleRef, evaluatedAssignment,
new AssignmentPathType().segment(new AssignmentPathSegmentType()
.assignmentId(assignment.getId())
.segmentOrder(1)
.isAssignment(true)
.matchingOrder(true)
.sourceRef(focusContext.getOid(),
focusContext.getObjectDefinition().getTypeName(),
SchemaConstants.ORG_DEFAULT)
.targetRef(assignmentTargetRef)));
}
} else {
for (EvaluatedAssignmentTarget evaluatedAssignmentTarget : evaluatedAssignmentTargets) {
AssignmentPathType assignmentPath = evaluatedAssignmentTarget.getAssignmentPath().toAssignmentPathType(false);
// There can be some value metadata already created by previous assignment evaluation,
// but we will add new metadata container for each assignment path without touching any existing ones.
//noinspection unchecked
roleRef.getValueMetadataAsContainer().add(new ValueMetadataType()
.provenance(new ProvenanceMetadataType()
.assignmentPath(assignmentPathToMetadata(assignmentPath)))
.storage(new StorageMetadataType()
.createTimestamp(determineAssignmentSinceTimestamp(evalAssignment)))
.asPrismContainerValue());
addAssignmentPathValueMetadataValue(roleRef, evaluatedAssignment, assignmentPath);
}
}
}

private void addAssignmentPathValueMetadataValue(
PrismReferenceValue roleRef, EvaluatedAssignment evaluatedAssignment, AssignmentPathType assignmentPath)
throws SchemaException {
//noinspection unchecked
roleRef.getValueMetadataAsContainer().add(new ValueMetadataType()
.provenance(new ProvenanceMetadataType()
.assignmentPath(assignmentPathToMetadata(assignmentPath)))
.storage(new StorageMetadataType()
.createTimestamp(determineAssignmentSinceTimestamp(evaluatedAssignment)))
.asPrismContainerValue());
}

/**
* Technically, storage/createTimestamp should be "now", but in this case we use it as "assigned since" date as well.
* Normally, it is virtually the same date, but if metadata are created later, we want to "reconstruct" the date.
* This also solves the problem for existing deployments.
*/
private static XMLGregorianCalendar determineAssignmentSinceTimestamp(EvaluatedAssignmentImpl<?> evalAssignment) {
private static XMLGregorianCalendar determineAssignmentSinceTimestamp(EvaluatedAssignment evalAssignment) {
MetadataType assignmentMetadata = evalAssignment.getAssignment().getMetadata();
if (assignmentMetadata != null) {
XMLGregorianCalendar createTimestamp = assignmentMetadata.getCreateTimestamp();
Expand All @@ -1096,10 +1126,10 @@ private AssignmentPathMetadataType assignmentPathToMetadata(AssignmentPathType a
return metadata;
}

private @NotNull List<EvaluatedAssignmentTargetImpl> findEvaluatedAssignmentTargets(
PrismReferenceValue roleRef, EvaluatedAssignmentImpl<?> evalAssignment) {
List<EvaluatedAssignmentTargetImpl> result = new ArrayList<>();
for (EvaluatedAssignmentTargetImpl eat : evalAssignment.getRoles().getNonNegativeValues()) {
private @NotNull List<EvaluatedAssignmentTarget> findEvaluatedAssignmentTargets(
PrismReferenceValue roleRef, EvaluatedAssignmentImpl<?> evaluatedAssignment) {
List<EvaluatedAssignmentTarget> result = new ArrayList<>();
for (EvaluatedAssignmentTargetImpl eat : evaluatedAssignment.getRoles().getNonNegativeValues()) {
ObjectReferenceType evaluatedAssignmentTargetRef = eat.getAssignment().getTargetRef();
if (MiscUtil.equals(evaluatedAssignmentTargetRef.getOid(), roleRef.getOid())
&& prismContext.relationsEquivalent(evaluatedAssignmentTargetRef.getRelation(), roleRef.getRelation())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,11 @@ public void test400AddingOneAssignmentFromUser() throws Exception {
}

@Test
public void test500AddUserWithAssignmentToRoleNonDefaultRelation() throws Exception {
public void test500AddUserWithManagerAssignment() throws Exception {
Task task = getTestTask();
OperationResult result = task.getResult();

given("new user with assignment to a role with different relation");
given("new user with assignment to a role with manager relation");
UserType user = new UserType()
.name("user-" + getTestNumber())
.assignment(new AssignmentType().targetRef(
Expand All @@ -235,18 +235,73 @@ public void test500AddUserWithAssignmentToRoleNonDefaultRelation() throws Except
when("user is added");
String userOid = addObject(user, task, result);

then("roleMembershipRefs contain value metadata with accesses information");
then("roleMembershipRefs are created");
UserAsserter<Void> userAsserter = assertUser(userOid, "after")
.displayXml() // XML also shows the metadata
.assertRoleMembershipRefs(2); // details are not interesting for this test

and("first segments have targetRef with manager relation");
and("first segments of assignment path metadata have targetRef with manager relation");
segmentsHaveExpectedRelations(userAsserter, appRole1Oid,
SchemaConstants.ORG_MANAGER);
segmentsHaveExpectedRelations(userAsserter, appService1Oid,
SchemaConstants.ORG_MANAGER, SchemaConstants.ORG_DEFAULT);
}

@Test
public void test550AddUserWithApproverAssignment() throws Exception {
Task task = getTestTask();
OperationResult result = task.getResult();

given("new user with assignment to a role with approver relation");
UserType user = new UserType()
.name("user-" + getTestNumber())
.assignment(new AssignmentType().targetRef(
createObjectReference(appRole1Oid, RoleType.COMPLEX_TYPE, SchemaConstants.ORG_APPROVER)));

when("user is added");
String userOid = addObject(user, task, result);

then("first segments of assignment path metadata have targetRef with approver relation");
UserAsserter<Void> userAsserter = assertUser(userOid, "after")
.displayXml() // XML also shows the metadata
.assertRoleMembershipRefs(1); // details are not interesting for this test
segmentsHaveExpectedRelations(userAsserter, appRole1Oid, SchemaConstants.ORG_APPROVER);
}

@Test
public void test560AddUserAndThenAddApproverAssignment() throws Exception {
Task task = getTestTask();
OperationResult result = task.getResult();

given("user with business role 1 exists");
UserType user = newUserWithBusinessRole1();
String userOid = addObject(user, task, result);
assertUser(userOid, "before")
.displayXml() // XML also shows the metadata
.assertRoleMembershipRefs(3);

when("business role 1 with approver relation is added");
executeChanges(prismContext.deltaFor(UserType.class)
.item(F_ASSIGNMENT).add(new AssignmentType()
.targetRef(createObjectReference(businessRole1Oid,
RoleType.COMPLEX_TYPE, SchemaConstants.ORG_APPROVER)))
.<UserType>asObjectDelta(userOid),
null, task, result);

then("role membership ref with approver relation is added, including value metadata");
UserAsserter<Void> userAsserter = assertUser(userOid, "after")
.displayXml() // XML also shows the metadata
.assertRoleMembershipRefs(4);
ValueMetadataType metadata = (ValueMetadataType) userAsserter
.valueMetadata(F_ROLE_MEMBERSHIP_REF, ValueSelector.refEquals(businessRole1Oid, SchemaConstants.ORG_APPROVER))
.assertSize(1)
.getRealValue();
AssignmentPathMetadataType assignmentPathMetadata = metadata.getProvenance().getAssignmentPath();
assertThat(assignmentPathMetadata.getSegment()).hasSize(1);
assertThat(assignmentPathMetadata.getSegment().get(0).getTargetRef().getRelation())
.isEqualTo(SchemaConstants.ORG_APPROVER);
}

private void segmentsHaveExpectedRelations(
UserAsserter<Void> userAsserter, String membershipTargetOid, QName... expectedRelations)
throws SchemaException {
Expand Down

0 comments on commit 227688d

Please sign in to comment.