Skip to content

Commit

Permalink
Filling-in missing association names (this bug shows itself during re…
Browse files Browse the repository at this point in the history
…conciliation when associations are defined as explicit pointers to shadows).
  • Loading branch information
mederly committed Oct 15, 2014
1 parent 6606d89 commit 4876aa1
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 5 deletions.
Expand Up @@ -734,4 +734,22 @@ public String dumpEntries() throws DirectoryException {
return sb.toString();
}

public Collection<String> getGroupUniqueMembers(String groupDn) throws DirectoryException {
SearchResultEntry groupEntry = fetchEntry(groupDn);
if (groupEntry == null) {
throw new IllegalArgumentException(groupDn + " was not found");
}
return getAttributeValues(groupEntry, "uniqueMember");
}

/*
dn: <group>
changetype: modify
delete: uniqueMember
uniqueMember: <member>
*/
public ChangeRecordEntry removeGroupUniqueMember(String groupDn, String memberDn) throws IOException, LDIFException {
String ldif = "dn: " + groupDn + "\nchangetype: modify\ndelete: uniqueMember\nuniqueMember: " + memberDn;
return executeLdifChange(ldif);
}
}
Expand Up @@ -40,6 +40,7 @@
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismProperty;
import com.evolveum.midpoint.prism.PrismPropertyDefinition;
import com.evolveum.midpoint.prism.PrismReference;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.PrismPropertyValue;
Expand Down Expand Up @@ -183,6 +184,13 @@ public Collection<Mapping<PrismContainerValue<ShadowAssociationType>>> getMappin
Map<QName, DeltaSetTriple<ItemValueWithOrigin<PrismContainerValue<ShadowAssociationType>>>> squeezedAssociations =
sqeeze(projCtx, associationExtractor);
projCtx.setSqueezedAssociations(squeezedAssociations);

// Association values in squeezed associations do not contain association name attribute.
// It is hacked-in later for use in this class, but not for other uses (e.g. in ReconciliationProcessor).
// So, we do it here - once and for all.
if (!squeezedAssociations.isEmpty()) {
fillInAssociationNames(squeezedAssociations);
}

ResourceShadowDiscriminator discr = projCtx.getResourceShadowDiscriminator();
ObjectDelta<ShadowType> objectDelta = new ObjectDelta<ShadowType>(ShadowType.class, ChangeType.MODIFY, prismContext);
Expand Down Expand Up @@ -245,6 +253,23 @@ public Collection<Mapping<PrismContainerValue<ShadowAssociationType>>> getMappin
return objectDelta;
}

private void fillInAssociationNames(Map<QName, DeltaSetTriple<ItemValueWithOrigin<PrismContainerValue<ShadowAssociationType>>>> squeezedAssociations) throws SchemaException {
PrismPropertyDefinition<QName> nameDefinition = prismContext.getSchemaRegistry()
.findContainerDefinitionByCompileTimeClass(ShadowAssociationType.class)
.findPropertyDefinition(ShadowAssociationType.F_NAME);
for (Entry<QName, DeltaSetTriple<ItemValueWithOrigin<PrismContainerValue<ShadowAssociationType>>>> entry : squeezedAssociations.entrySet()) {
DeltaSetTriple<ItemValueWithOrigin<PrismContainerValue<ShadowAssociationType>>> deltaSetTriple = entry.getValue();
for (ItemValueWithOrigin<PrismContainerValue<ShadowAssociationType>> ivwo : deltaSetTriple.getAllValues()) {
PrismContainerValue<ShadowAssociationType> value = ivwo.getItemValue();
if (value != null && value.findProperty(ShadowAssociationType.F_NAME) == null) { // just for safety
PrismProperty<QName> nameProperty = value.createProperty(nameDefinition);
nameProperty.setRealValue(entry.getKey());
}
}
}
LOGGER.trace("Names for squeezed associations filled-in.");
}

private <T,V extends PrismValue> PropertyDelta<T> consolidateAttribute(RefinedObjectClassDefinition rOcDef,
ResourceShadowDiscriminator discr, ObjectDelta<ShadowType> existingDelta, LensProjectionContext projCtx,
boolean addUnchangedValues, boolean completeShadow, QName itemName,
Expand Down
Expand Up @@ -19,8 +19,10 @@
import static com.evolveum.midpoint.test.IntegrationTestTools.display;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertTrue;

import java.io.File;
import java.util.Collection;
import java.util.List;

import javax.xml.namespace.QName;
Expand Down Expand Up @@ -127,6 +129,7 @@ public class TestOrgSync extends AbstractStoryTest {
private static final String ACCOUNT_LEMONHEAD_USERNAME = "lemonhead";
private static final String ACCOUNT_LEMONHEAD_FIST_NAME = "Lemonhead";
private static final String ACCOUNT_LEMONHEAD_LAST_NAME = "Canibal";
private static final String ACCOUNT_LEMONHEAD_DN = "uid=lemonhead,ou=Monkey Island,dc=example,dc=com";

private static final String ACCOUNT_SHARPTOOTH_USERNAME = "sharptooth";
private static final String ACCOUNT_SHARPTOOTH_FIST_NAME = "Sharptooth";
Expand Down Expand Up @@ -189,12 +192,13 @@ public class TestOrgSync extends AbstractStoryTest {
private static final String ORGPATH_HRAD = HRAD+"/"+ORGPATH_KARPATULA;

private static final String RESP_CANIBALISM = "canibalism";

private static final String RESP_CANIBALISM_DN = "cn=R_canibalism,ou=Groups,dc=example,dc=com";

private static final File SCABB_OU_LDIF_FILE = new File(TEST_DIR, "scabb.ldif");
private static final File BOOTY_OU_LDIF_FILE = new File(TEST_DIR, "booty.ldif");
private static final File BOOTY_LOOKOUT_OU_LDIF_FILE = new File(TEST_DIR, "booty-lookout.ldif");
@Autowired(required=true)

@Autowired(required=true)
private ReconciliationTaskHandler reconciliationTaskHandler;

private DebugReconciliationTaskResultListener reconciliationTaskResultListener;
Expand Down Expand Up @@ -1004,8 +1008,55 @@ public void test510ReconcileOpenDJLdapGroup() throws Exception {
display("Recon task result", reconTaskResult);
TestUtil.assertSuccess(reconTaskResult);
}

protected void assertUserGuybrush(PrismObject<UserType> user) {

@Test
public void test550ReconcileOpenDJAfterMembershipChange() throws Exception {
final String TEST_NAME = "test550ReconcileOpenDJAfterMembershipChange";
TestUtil.displayTestTile(this, TEST_NAME);

// We manually remove Lemonhead from R_canibalism group
// And check whether reconciliation re-adds him again

// GIVEN
Task task = createTask(TestOrgSync.class.getName() + "." + TEST_NAME);
OperationResult result = task.getResult();
assumeAssignmentPolicy(AssignmentPolicyEnforcementType.NONE);

Collection<String> membersBeforeTest = openDJController.getGroupUniqueMembers(RESP_CANIBALISM_DN);
System.out.println("group members before test = " + membersBeforeTest);
assertTrue(RESP_CANIBALISM_DN + " does not contain " + ACCOUNT_LEMONHEAD_DN, membersBeforeTest.contains(ACCOUNT_LEMONHEAD_DN));

openDJController.removeGroupUniqueMember(RESP_CANIBALISM_DN, ACCOUNT_LEMONHEAD_DN);

System.out.println("group members after removal = " + openDJController.getGroupUniqueMembers(RESP_CANIBALISM_DN));

openDJController.assertNoUniqueMember(RESP_CANIBALISM_DN, ACCOUNT_LEMONHEAD_DN);

// WHEN
TestUtil.displayWhen(TEST_NAME);
restartTask(TASK_RECON_OPENDJ_DEFAULT_SINGLE_OID);

// THEN
TestUtil.displayThen(TEST_NAME);

waitForTaskFinish(TASK_RECON_OPENDJ_DEFAULT_SINGLE_OID, false);

// THEN
TestUtil.displayThen(TEST_NAME);

// Task result
PrismObject<TaskType> reconTaskAfter = getTask(TASK_RECON_OPENDJ_DEFAULT_SINGLE_OID);
OperationResultType reconTaskResult = reconTaskAfter.asObjectable().getResult();
display("Recon task result", reconTaskResult);
TestUtil.assertSuccess(reconTaskResult);

Collection<String> membersAfterTest = openDJController.getGroupUniqueMembers(RESP_CANIBALISM_DN);
System.out.println("group members after test = " + membersAfterTest);
assertTrue(RESP_CANIBALISM_DN + " does not contain " + ACCOUNT_LEMONHEAD_DN, membersAfterTest.contains(ACCOUNT_LEMONHEAD_DN.toLowerCase())); // ...it seems to get lowercased during the reconciliation
}


protected void assertUserGuybrush(PrismObject<UserType> user) {
assertUser(user, ACCOUNT_GUYBRUSH_USERNAME, ACCOUNT_GUYBRUSH_FIST_NAME, ACCOUNT_GUYBRUSH_LAST_NAME);
}

Expand Down

0 comments on commit 4876aa1

Please sign in to comment.