Skip to content

Commit

Permalink
Fix assignment activation deltas
Browse files Browse the repository at this point in the history
These deltas were usually created as "replace Activation container
with new one containing (effectiveStatus=ENABLED)". Notification module
currently cannot render these deltas correctly. So these deltas were
changed to simpler form of "replace activation/effectiveStatus with
ENABLED" that are processable by notifications.

See MID-5350 and MID-6111.

(cherry picked from commit 4f10f92)
  • Loading branch information
mederly committed Mar 14, 2020
1 parent ce81807 commit 27313f5
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 40 deletions.
Expand Up @@ -142,39 +142,27 @@ private <F extends AssignmentHolderType> void processAssignmentActivation(LensCo
// We care only about existing assignments here. New assignments will be taken care of in the executor
// (OperationalDataProcessor). And why care about deleted assignments?
Collection<EvaluatedAssignmentImpl<?>> zeroSet = evaluatedAssignmentTriple.getZeroSet();
if (zeroSet == null) {
return;
}
LensFocusContext<F> focusContext = context.getFocusContext();
for (EvaluatedAssignmentImpl<?> evaluatedAssignment: zeroSet) {
if (evaluatedAssignment.isVirtual()) {
continue;
}
AssignmentType assignmentType = evaluatedAssignment.getAssignmentType();
ActivationType currentActivationType = assignmentType.getActivation();
ActivationStatusType expectedEffectiveStatus = activationComputer.getEffectiveStatus(assignmentType.getLifecycleState(), currentActivationType, null);
if (currentActivationType == null) {
PrismContainerDefinition<ActivationType> activationDef = focusContext.getObjectDefinition().findContainerDefinition(SchemaConstants.PATH_ASSIGNMENT_ACTIVATION);
ContainerDelta<ActivationType> activationDelta = activationDef.createEmptyDelta(
ItemPath.create(FocusType.F_ASSIGNMENT, assignmentType.getId(), AssignmentType.F_ACTIVATION));
ActivationType newActivationType = new ActivationType();
activationDelta.setValuesToReplace(newActivationType.asPrismContainerValue());
newActivationType.setEffectiveStatus(expectedEffectiveStatus);
focusContext.swallowToSecondaryDelta(activationDelta);
} else {
ActivationStatusType currentEffectiveStatus = currentActivationType.getEffectiveStatus();
if (!expectedEffectiveStatus.equals(currentEffectiveStatus)) {
PrismPropertyDefinition<ActivationStatusType> effectiveStatusPropertyDef = focusContext.getObjectDefinition().findPropertyDefinition(SchemaConstants.PATH_ASSIGNMENT_ACTIVATION_EFFECTIVE_STATUS);
PropertyDelta<ActivationStatusType> effectiveStatusDelta = effectiveStatusPropertyDef.createEmptyDelta(
ItemPath.create(FocusType.F_ASSIGNMENT, assignmentType.getId(), AssignmentType.F_ACTIVATION, ActivationType.F_EFFECTIVE_STATUS));
effectiveStatusDelta.setRealValuesToReplace(expectedEffectiveStatus);
focusContext.swallowToSecondaryDelta(effectiveStatusDelta);
}
AssignmentType assignment = evaluatedAssignment.getAssignmentType();
ActivationType currentActivation = assignment.getActivation();
ActivationStatusType currentEffectiveStatus = currentActivation != null ? currentActivation.getEffectiveStatus() : null;
ActivationStatusType expectedEffectiveStatus = activationComputer.getEffectiveStatus(assignment.getLifecycleState(),
currentActivation, null);
if (currentEffectiveStatus != expectedEffectiveStatus) {
PrismPropertyDefinition<ActivationStatusType> effectiveStatusPropertyDef = focusContext.getObjectDefinition()
.findPropertyDefinition(SchemaConstants.PATH_ASSIGNMENT_ACTIVATION_EFFECTIVE_STATUS);
PropertyDelta<ActivationStatusType> effectiveStatusDelta = effectiveStatusPropertyDef.createEmptyDelta(
ItemPath.create(FocusType.F_ASSIGNMENT, assignment.getId(), AssignmentType.F_ACTIVATION, ActivationType.F_EFFECTIVE_STATUS));
effectiveStatusDelta.setRealValuesToReplace(expectedEffectiveStatus);
focusContext.swallowToSecondaryDelta(effectiveStatusDelta);
}
}
}


private <F extends FocusType> void processActivationAdministrativeAndValidity(LensFocusContext<F> focusContext, XMLGregorianCalendar now,
OperationResult result)
throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, PolicyViolationException {
Expand Down
Expand Up @@ -22,6 +22,7 @@
import com.evolveum.midpoint.schema.util.MiscSchemaUtil;
import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.test.TestResource;
import com.evolveum.midpoint.test.util.TestUtil;
import com.evolveum.midpoint.util.exception.CommonException;
import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException;
Expand Down Expand Up @@ -65,16 +66,20 @@ public class TestNotifications extends AbstractInitializedModelIntegrationTest {
public static final File TEST_DIR = new File("src/test/resources/notifications");
private static final File SYSTEM_CONFIGURATION_FILE = new File(TEST_DIR, "system-configuration.xml");

private static final TestResource ARCHETYPE_DUMMY = new TestResource(TEST_DIR, "archetype-dummy.xml", "c97780b7-6b07-4a25-be95-60125af6f650");
private static final TestResource ROLE_DUMMY = new TestResource(TEST_DIR, "role-dummy.xml", "8bc6d827-6ea6-4671-a506-a8388f117880");

@Autowired private LightweightIdentifierGenerator lightweightIdentifierGenerator;

private String accountJackOid;
private HttpServer httpServer;
private MyHttpHandler httpHandler;

@Override
public void initSystem(Task initTask, OperationResult initResult)
throws Exception {
public void initSystem(Task initTask, OperationResult initResult) throws Exception {
super.initSystem(initTask, initResult);
repoAdd(ARCHETYPE_DUMMY, initResult);
repoAdd(ROLE_DUMMY, initResult);
InternalMonitor.reset();
}

Expand Down Expand Up @@ -658,7 +663,7 @@ public void test220SendSmsViaProxy() {

assertNotNull("No http request found", httpHandler.lastRequest);
assertEquals("Wrong HTTP method", "GET", httpHandler.lastRequest.method);
assertTrue("Header proxy-connection not found in request headers", httpHandler.lastRequest.headers.keySet().contains("proxy-connection"));
assertTrue("Header proxy-connection not found in request headers", httpHandler.lastRequest.headers.containsKey("proxy-connection"));
assertEquals("Wrong proxy-connection header", "Keep-Alive", httpHandler.lastRequest.headers.get("proxy-connection").get(0));
}

Expand Down Expand Up @@ -770,7 +775,7 @@ public void test410ByteAttachment() throws Exception {
byte[] origJPEG = Base64.getDecoder().decode(origJPEGString);
Object content = RawType.getValue(message.getAttachments().get(0).getContent());
if(!(content instanceof byte[]) || !Arrays.equals(origJPEG, (byte[])content)) {
throw new AssertionError("Wrong content of attachments expected:" + origJPEG + " but was:" + content);
throw new AssertionError("Wrong content of attachments expected:" + Arrays.toString(origJPEG) + " but was:" + content);
}
assertEquals("Wrong fileName of attachments", "alf.jpg", message.getAttachments().get(0).getFileName());
assertEquals("Wrong fileName of attachments", null, message.getAttachments().get(0).getContentFromFile());
Expand Down Expand Up @@ -809,7 +814,7 @@ public void test420AttachmentFromFile() throws Exception {
assertEquals("Wrong contentType of attachment", "image/png", message.getAttachments().get(0).getContentType());
assertEquals("Wrong fileName of attachments", "alf.png", message.getAttachments().get(0).getFileName());
assertEquals("Wrong fileName of attachments", "/home/user/example.png", message.getAttachments().get(0).getContentFromFile());
assertEquals("Wrong fileName of attachments", null, message.getAttachments().get(0).getContent());
assertNull("Wrong fileName of attachments", message.getAttachments().get(0).getContent());
}

@Test
Expand Down Expand Up @@ -845,11 +850,48 @@ public void test430ExpressionAttachment() throws Exception {
assertEquals("Wrong contentType of attachment", "text/html", message.getAttachments().get(0).getContentType());
assertEquals("Wrong content of attachments", "<!DOCTYPE html><html><body>Hello World!</body></html>", message.getAttachments().get(0).getContent());
assertEquals("Wrong fileName of attachments", "hello_world.html", message.getAttachments().get(0).getFileName());
assertEquals("Wrong fileName of attachments", null, message.getAttachments().get(0).getContentFromFile());
assertNull("Wrong fileName of attachments", message.getAttachments().get(0).getContentFromFile());
}

// TODO binary attachment, attachment from file, attachment from expression

/**
* MID-5350
*/
@Test
public void test500RecomputeRole() throws Exception {

setGlobalTracingOverride(addNotificationsLogging(createModelLoggingTracingProfile()));

given();
Task task = getTestTask();
OperationResult result = task.getResult();
preTestCleanup(AssignmentPolicyEnforcementType.FULL);

when();
recomputeFocus(RoleType.class, ROLE_DUMMY.oid, task, result);

then();
result.computeStatus();
TestUtil.assertSuccess("executeChanges result", result);

// Check notifications
display("Notifications", dummyTransport);

notificationManager.setDisabled(true);
checkDummyTransportMessages("simpleRoleNotifier", 0); // MID-5350 (other asserts are just for sure)
checkDummyTransportMessages("accountPasswordNotifier", 0);
checkDummyTransportMessages("userPasswordNotifier", 0);
checkDummyTransportMessages("simpleAccountNotifier-SUCCESS", 0);
checkDummyTransportMessages("simpleAccountNotifier-FAILURE", 0);
checkDummyTransportMessages("simpleAccountNotifier-ADD-SUCCESS", 0);
checkDummyTransportMessages("simpleAccountNotifier-DELETE-SUCCESS", 0);
checkDummyTransportMessages("simpleUserNotifier", 0);
checkDummyTransportMessages("simpleUserNotifier-ADD", 0);

assertSteadyResources();
}

@SuppressWarnings("Duplicates")
private void preTestCleanup(AssignmentPolicyEnforcementType enforcementPolicy) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException {
assumeAssignmentPolicy(enforcementPolicy);
Expand All @@ -861,11 +903,11 @@ private void preTestCleanup(AssignmentPolicyEnforcementType enforcementPolicy) t

private static class MyHttpHandler implements HttpHandler {

private class Request {
URI uri;
String method;
Map<String, List<String>> headers;
List<String> body;
private static class Request {
private URI uri;
private String method;
private Map<String, List<String>> headers;
private List<String> body;
}

private Request lastRequest;
Expand Down
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2020 Evolveum and contributors
~
~ This work is dual-licensed under the Apache License 2.0
~ and European Union Public License. See LICENSE file for details.
-->

<archetype oid="c97780b7-6b07-4a25-be95-60125af6f650"
xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3">
<name>archetype-dummy</name>
</archetype>
25 changes: 25 additions & 0 deletions model/model-intest/src/test/resources/notifications/role-dummy.xml
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2020 Evolveum and contributors
~
~ This work is dual-licensed under the Apache License 2.0
~ and European Union Public License. See LICENSE file for details.
-->

<role oid="8bc6d827-6ea6-4671-a506-a8388f117880"
xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:org="http://midpoint.evolveum.com/xml/ns/public/common/org-3"
xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3">
<name>role-dummy</name>
<displayName>Dummy</displayName>
<assignment>
<targetRef oid="c97780b7-6b07-4a25-be95-60125af6f650" type="ArchetypeType"/> <!-- dummy -->
</assignment>
<archetypeRef oid="c97780b7-6b07-4a25-be95-60125af6f650" relation="org:default" type="c:ArchetypeType">
<!-- System Role -->
</archetypeRef>
<roleMembershipRef oid="c97780b7-6b07-4a25-be95-60125af6f650" relation="org:default" type="c:ArchetypeType">
<!-- System Role -->
</roleMembershipRef>
<subtype>dummy</subtype>
</role>
Expand Up @@ -143,6 +143,21 @@ Channel: $!event.channel</code>
<transport>dummy:simpleUserNotifier</transport>
</simpleUserNotifier>
</handler>
<handler>
<simpleFocalObjectNotifier>
<recipientExpression>
<value>recipient@evolveum.com</value>
</recipientExpression>
<expressionFilter>
<script>
<code>import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType

event.requesteeIs(RoleType.class)</code>
</script>
</expressionFilter>
<transport>dummy:simpleRoleNotifier</transport>
</simpleFocalObjectNotifier>
</handler>
<handler>
<chained>
<operation>add</operation>
Expand Down
Expand Up @@ -97,7 +97,7 @@ default String getRequesteeOid() {
boolean isOperationType(EventOperationType eventOperation);

/**
* @return true if the categoru of the event matches the specified one
* @return true if the category of the event matches the specified one
*/
boolean isCategoryType(EventCategoryType eventCategory);

Expand Down
Expand Up @@ -22,7 +22,7 @@
import com.evolveum.midpoint.schema.constants.ObjectTypes;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;

import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
Expand All @@ -42,9 +42,6 @@
import com.evolveum.midpoint.util.PrettyPrinter;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectModificationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
Expand Down Expand Up @@ -338,6 +335,48 @@ public void test050FormatDeltaWithOperAndAuxItems() throws Exception {

}

/**
* Delta formatter cannot correctly deal with a situation when we are replacing empty container value with one
* that contains only hidden items.
*
* An example:
* - BEFORE: assignment[1]/activation = (empty)
* - DELTA: REPLACE assignment[1]/activation with (effectiveStatus: ENABLED) -- i.e. with seemingly empty PCV
*
* We should hide such modification. But we do not do this now. (See MID-5350.)
*
* We fixed that issue by changing the delta that is created.
* But this behavior of delta formatter should be eventually fixed. See MID-6111.
*/
@Test(enabled = false)
public void test060FormatDeltaWithSingleOperationalItemContainer() throws Exception {

given();

PrismObject<UserType> jack = PrismTestUtil.parseObject(new File(USER_JACK_FILE));
display("jack", jack.debugDump());

// @formatter:off
ObjectDelta<Objectable> delta = prismContext.deltaFor(UserType.class)
.item(UserType.F_ACTIVATION, ActivationType.F_EFFECTIVE_STATUS)
.replace(ActivationStatusType.ENABLED)
// see MID-5350
.item(UserType.F_ASSIGNMENT, 1, UserType.F_ACTIVATION)
.replace(new ActivationType(prismContext).effectiveStatus(ActivationStatusType.ENABLED))
.asObjectDelta("some-user-oid");
// @formatter:on

display("delta", delta.debugDump());

when();

boolean hasVisible = textFormatter.containsVisibleModifiedItems(delta.getModifications(), false, false);

then();

assertFalse("There should be no visible modified items", hasVisible);
}

@SuppressWarnings("SameParameterValue")
private ObjectDelta<UserType> parseDelta(String filename) throws SchemaException, IOException {
ObjectModificationType modElement = PrismTestUtil.parseAtomicValue(new File(filename), ObjectModificationType.COMPLEX_TYPE);
Expand Down

0 comments on commit 27313f5

Please sign in to comment.