diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java index fe8933661f0..55ae5fe6a64 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java @@ -1338,10 +1338,24 @@ protected String debugName() { } protected String debugIdentifiers() { - return "oid=" + getOid(); + return toDebugType()+":" + getOid(); } + + /** + * Returns short string identification of object type. It should be in a form + * suitable for log messages. There is no requirement for the type name to be unique, + * but it rather has to be compact. E.g. short element names are preferred to long + * QNames or URIs. + * @return + */ + public String toDebugType() { + if (objectTypeClass == null) { + return "(unknown)"; + } + return objectTypeClass.getSimpleName(); + } - @Override + @Override public String debugDump() { return debugDump(0); } diff --git a/infra/schema/.gitignore b/infra/schema/.gitignore index 5476e3ea27f..182c35e0008 100644 --- a/infra/schema/.gitignore +++ b/infra/schema/.gitignore @@ -1,3 +1,4 @@ /target /test-output /target +/target diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-2a.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-2a.xsd index 82d4de64227..8d20cf967f2 100644 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-2a.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-2a.xsd @@ -4047,6 +4047,7 @@ + diff --git a/infra/test-util/src/main/java/com/evolveum/midpoint/test/ldap/OpenDJController.java b/infra/test-util/src/main/java/com/evolveum/midpoint/test/ldap/OpenDJController.java index 54018697c4b..5983dffa737 100755 --- a/infra/test-util/src/main/java/com/evolveum/midpoint/test/ldap/OpenDJController.java +++ b/infra/test-util/src/main/java/com/evolveum/midpoint/test/ldap/OpenDJController.java @@ -553,6 +553,13 @@ public static void assertDn(SearchResultEntry response, String expected) throws } } + public void assertNoEntry(String dn) throws DirectoryException { + SearchResultEntry entry = fetchEntry(dn); + if (entry != null) { + AssertJUnit.fail("Found entry for dn "+dn+" while not expecting it: "+entry); + } + } + public static void assertObjectClass(SearchResultEntry response, String expected) throws DirectoryException { Collection objectClassValues = getAttributeValues(response, "objectClass"); AssertJUnit.assertTrue("Wrong objectclass for entry "+getDn(response)+", expected "+expected+" but got "+objectClassValues, @@ -564,6 +571,22 @@ public void assertUniqueMember(SearchResultEntry groupEntry, String accountDn) { MidPointAsserts.assertContainsCaseIgnore("No member "+accountDn+" in group "+getDn(groupEntry), members, accountDn); } + + public void assertUniqueMember(String groupDn, String accountDn) throws DirectoryException { + SearchResultEntry groupEntry = fetchEntry(groupDn); + assertUniqueMember(groupEntry, accountDn); + } + + public void assertNoUniqueMember(String groupDn, String accountDn) throws DirectoryException { + SearchResultEntry groupEntry = fetchEntry(groupDn); + assertNoUniqueMember(groupEntry, accountDn); + } + + public void assertNoUniqueMember(SearchResultEntry groupEntry, String accountDn) { + Collection members = getAttributeValues(groupEntry, "uniqueMember"); + MidPointAsserts.assertNotContainsCaseIgnore("Member "+accountDn+" in group "+getDn(groupEntry), + members, accountDn); + } public static void assertAttribute(SearchResultEntry response, String name, String... values) { List attrs = response.getAttribute(name.toLowerCase()); diff --git a/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/MidPointAsserts.java b/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/MidPointAsserts.java index 2e25643de80..2bfe9913bec 100644 --- a/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/MidPointAsserts.java +++ b/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/MidPointAsserts.java @@ -171,4 +171,15 @@ public static void assertContainsCaseIgnore(String message, Collection a } AssertJUnit.fail(message+", expected "+expectedValue+", got "+actualValues); } + + public static void assertNotContainsCaseIgnore(String message, Collection actualValues, String expectedValue) { + if (actualValues == null) { + return; + } + for (String actualValue: actualValues) { + if (StringUtils.equalsIgnoreCase(actualValue, expectedValue)) { + AssertJUnit.fail(message+", expected that value "+expectedValue+" will not be present but it is"); + } + } + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/lens/ChangeExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/lens/ChangeExecutor.java index 70502d1f273..28f59614ed3 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/lens/ChangeExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/lens/ChangeExecutor.java @@ -228,7 +228,7 @@ public void executeChanges(LensContext syncContext, Ta + accCtx.getResourceShadowDiscriminator()); } if (focusContext != null) { - updateLinks(focusContext.getObjectNew(), focusContext, accCtx, task, + updateLinks(focusContext, accCtx, task, subResult); } @@ -254,7 +254,7 @@ public void executeChanges(LensContext syncContext, Ta } if (focusContext != null) { - updateLinks(focusContext.getObjectNew(), focusContext, accCtx, task, subResult); + updateLinks(focusContext, accCtx, task, subResult); } executeReconciliationScript(accCtx, syncContext, BeforeAfterType.AFTER, task, subResult); @@ -323,17 +323,16 @@ private void recordFatalError(OperationResult subResult, OperationResult result, /** * Make sure that the account is linked (or unlinked) as needed. */ - private void updateLinks(PrismObject prismObject, - LensFocusContext focusContext, LensProjectionContext projCtx, + private void updateLinks(LensFocusContext focusObjectContext, LensProjectionContext projCtx, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException { - if (prismObject == null) { + if (focusObjectContext == null) { return; } - F objectTypeNew = prismObject.asObjectable(); - if (!(objectTypeNew instanceof FocusType)) { - return; - } - FocusType focusTypeNew = (FocusType) objectTypeNew; + Class objectTypeClass = focusObjectContext.getObjectTypeClass(); + if (!FocusType.class.isAssignableFrom(objectTypeClass)) { + return; + } + LensFocusContext focusContext = (LensFocusContext) focusObjectContext; if (projCtx.getResourceShadowDiscriminator() != null && projCtx.getResourceShadowDiscriminator().getOrder() > 0) { // Don't mess with links for higher-order contexts. The link should be dealt with @@ -357,12 +356,13 @@ private void updateLinks(PrismObject prismObject, || projCtx.isDelete()) { // Link should NOT exist - PrismReference linkRef = focusTypeNew.asPrismObject().findReference(FocusType.F_LINK_REF); + PrismObject objectCurrent = focusContext.getObjectCurrent(); + PrismReference linkRef = objectCurrent.findReference(FocusType.F_LINK_REF); if (linkRef != null) { for (PrismReferenceValue linkRefVal: linkRef.getValues()) { if (linkRefVal.getOid().equals(projOid)) { // Linked, need to unlink - unlinkShadow(focusTypeNew.getOid(), linkRefVal, focusContext, task, result); + unlinkShadow(focusContext.getOid(), linkRefVal, focusObjectContext, task, result); } } @@ -372,7 +372,7 @@ private void updateLinks(PrismObject prismObject, LOGGER.trace("Resource object {} deleted, updating also situation in shadow.", projOid); // HACK HACK? try { - updateSituationInShadow(task, SynchronizationSituationType.DELETED, focusContext, projCtx, result); + updateSituationInShadow(task, SynchronizationSituationType.DELETED, focusObjectContext, projCtx, result); } catch (ObjectNotFoundException e) { // HACK HACK? LOGGER.trace("Resource object {} is gone, cannot update situation in shadow (this is probably harmless).", projOid); @@ -381,30 +381,33 @@ private void updateLinks(PrismObject prismObject, } else { // This should NOT be UNLINKED. We just do not know the situation here. Reflect that in the shadow. LOGGER.trace("Resource object {} unlinked from the user, updating also situation in shadow.", projOid); - updateSituationInShadow(task, null, focusContext, projCtx, result); + updateSituationInShadow(task, null, focusObjectContext, projCtx, result); } // Not linked, that's OK } else { // Link should exist - for (ObjectReferenceType linkRef : focusTypeNew.getLinkRef()) { - if (projOid.equals(linkRef.getOid())) { - // Already linked, nothing to do, only be sure, the situation is set with the good value - LOGGER.trace("Updating situation in already linked shadow."); - updateSituationInShadow(task, SynchronizationSituationType.LINKED, focusContext, projCtx, result); - return; - } - } + PrismObject objectCurrent = focusContext.getObjectCurrent(); + if (objectCurrent != null) { + for (ObjectReferenceType linkRef : objectCurrent.asObjectable().getLinkRef()) { + if (projOid.equals(linkRef.getOid())) { + // Already linked, nothing to do, only be sure, the situation is set with the good value + LOGGER.trace("Updating situation in already linked shadow."); + updateSituationInShadow(task, SynchronizationSituationType.LINKED, focusObjectContext, projCtx, result); + return; + } + } + } // Not linked, need to link - linkShadow(focusTypeNew.getOid(), projOid, focusContext, task, result); + linkShadow(focusContext.getOid(), projOid, focusObjectContext, task, result); //be sure, that the situation is set correctly LOGGER.trace("Updating situation after shadow was linked."); - updateSituationInShadow(task, SynchronizationSituationType.LINKED, focusContext, projCtx, result); + updateSituationInShadow(task, SynchronizationSituationType.LINKED, focusObjectContext, projCtx, result); } } - private void linkShadow(String userOid, String accountOid, LensElementContext focusContext, Task task, OperationResult parentResult) throws ObjectNotFoundException, + private void linkShadow(String userOid, String shadowOid, LensElementContext focusContext, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { Class typeClass = focusContext.getObjectTypeClass(); @@ -412,10 +415,10 @@ private void linkShadow(String userOid, String accountOid return; } - LOGGER.trace("Linking shadow " + accountOid + " to focus " + userOid); + LOGGER.debug("Linking shadow " + shadowOid + " to focus " + userOid); OperationResult result = parentResult.createSubresult(OPERATION_LINK_ACCOUNT); PrismReferenceValue linkRef = new PrismReferenceValue(); - linkRef.setOid(accountOid); + linkRef.setOid(shadowOid); linkRef.setTargetType(ShadowType.COMPLEX_TYPE); Collection linkRefDeltas = ReferenceDelta.createModificationAddCollection( FocusType.F_LINK_REF, getUserDefinition(), linkRef); @@ -438,7 +441,7 @@ private PrismObjectDefinition getUserDefinition() { return userDefinition; } - private void unlinkShadow(String userOid, PrismReferenceValue accountRef, LensElementContext focusContext, + private void unlinkShadow(String focusOid, PrismReferenceValue accountRef, LensElementContext focusContext, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { @@ -447,19 +450,19 @@ private void unlinkShadow(String userOid, PrismReferenceV return; } - LOGGER.trace("Deleting linkRef " + accountRef + " from focus " + userOid); + LOGGER.debug("Unlinnking shadow " + accountRef.getOid() + " from focus " + focusOid); OperationResult result = parentResult.createSubresult(OPERATION_UNLINK_ACCOUNT); Collection accountRefDeltas = ReferenceDelta.createModificationDeleteCollection( FocusType.F_LINK_REF, getUserDefinition(), accountRef.clone()); try { - cacheRepositoryService.modifyObject(typeClass, userOid, accountRefDeltas, result); + cacheRepositoryService.modifyObject(typeClass, focusOid, accountRefDeltas, result); } catch (ObjectAlreadyExistsException ex) { result.recordFatalError(ex); throw new SystemException(ex); } finally { result.computeStatus(); - ObjectDelta userDelta = ObjectDelta.createModifyDelta(userOid, accountRefDeltas, typeClass, prismContext); + ObjectDelta userDelta = ObjectDelta.createModifyDelta(focusOid, accountRefDeltas, typeClass, prismContext); LensObjectDeltaOperation userDeltaOp = new LensObjectDeltaOperation(userDelta); userDeltaOp.setExecutionResult(result); focusContext.addToExecutedDeltas(userDeltaOp); diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/EntitlementConverter.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/EntitlementConverter.java index dd1576fb269..5a3c2701fc6 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/EntitlementConverter.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/EntitlementConverter.java @@ -58,6 +58,8 @@ import com.evolveum.midpoint.util.exception.CommunicationException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.TunnelException; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_2a.ResourceObjectAssociationDirectionType; import com.evolveum.midpoint.xml.ns._public.common.common_2a.ResourceObjectAssociationType; import com.evolveum.midpoint.xml.ns._public.common.common_2a.ResourceType; @@ -76,6 +78,8 @@ @Component class EntitlementConverter { + private static final Trace LOGGER = TraceManager.getTrace(EntitlementConverter.class); + @Autowired(required=true) private PrismContext prismContext; @@ -307,6 +311,7 @@ public void collectEntitlementsAsObjectOperationDelete(ConnectorInstance con Collection entitlementAssociationDefs = objectClassDefinition.getEntitlementAssociations(); if (entitlementAssociationDefs == null || entitlementAssociationDefs.isEmpty()) { // Nothing to do + LOGGER.trace("No associations in deleted shadow"); return; } RefinedResourceSchema refinedSchema = RefinedResourceSchema.getRefinedSchema(resourceType); @@ -314,6 +319,13 @@ public void collectEntitlementsAsObjectOperationDelete(ConnectorInstance con for (RefinedAssociationDefinition assocDefType: objectClassDefinition.getEntitlementAssociations()) { if (assocDefType.getResourceObjectAssociationType().getDirection() != ResourceObjectAssociationDirectionType.OBJECT_TO_SUBJECT) { // We can ignore these. They will die together with the object. No need to explicitly delete them. + LOGGER.trace("Ignoring object-to-subject association in deleted shadow"); + continue; + } + if (assocDefType.getResourceObjectAssociationType().isExplicitReferentialIntegrity() != null + && !assocDefType.getResourceObjectAssociationType().isExplicitReferentialIntegrity()) { + // Referential integrity not required for this one + LOGGER.trace("Ignoring association in deleted shadow because it has explicity referential integrity turned off"); continue; } QName associationName = assocDefType.getName(); @@ -347,6 +359,7 @@ public void collectEntitlementsAsObjectOperationDelete(ConnectorInstance con } // ObjectFilter filter = EqualsFilter.createEqual(new ItemPath(ShadowType.F_ATTRIBUTES), assocAttrDef, valueAttr.getValue()); +// ObjectFilter filter = InFilter.createIn(new ItemPath(ShadowType.F_ATTRIBUTES, assocAttrDef.getName()), assocAttrDef, valueAttr.getValue()); ObjectFilter filter = EqualsFilter.createEqual(new ItemPath(ShadowType.F_ATTRIBUTES, assocAttrDef.getName()), assocAttrDef, valueAttr.getValue()); ObjectQuery query = ObjectQuery.createObjectQuery(filter); // new ObjectQuery(); @@ -381,11 +394,13 @@ public boolean handle(PrismObject entitlementShadow) { } attributeDelta.addValuesToDelete(valueAttr.getClonedValues()); + LOGGER.trace("Association in deleted shadow delta: {}", attributeDelta); return true; } }; try { + LOGGER.trace("Searching for associations in deleted shadow, query: {}", filter); connector.search(entitlementOcDef, query, handler, attributesToReturn, parentResult); } catch (TunnelException e) { throw (SchemaException)e.getCause(); diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/query/ValueOperation.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/query/ValueOperation.java index 3a88b5bffe9..a36b517e432 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/query/ValueOperation.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/query/ValueOperation.java @@ -30,6 +30,7 @@ import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.query.EqualsFilter; +import com.evolveum.midpoint.prism.query.InFilter; import com.evolveum.midpoint.prism.query.ObjectFilter; import com.evolveum.midpoint.prism.query.SubstringFilter; import com.evolveum.midpoint.prism.query.ValueFilter; @@ -79,6 +80,22 @@ public Filter interpret(ObjectFilter objectFilter, IcfNameMapper icfNameMapp return FilterBuilder.containsAllValues(attr); } } + + } else if (objectFilter instanceof InFilter) { + InFilter in = (InFilter) objectFilter; + + Collection convertedValues = new ArrayList(); + for (PrismValue value : in.getValues()) { + Object converted = UcfUtil.convertValueToIcf(value, null, propName); + convertedValues.add(converted); + } + + if (convertedValues.isEmpty()) { + throw new IllegalArgumentException("In filter with a null value makes no sense"); + } else { + Attribute attr = AttributeBuilder.build(icfName, convertedValues); + return FilterBuilder.equalTo(attr); + } } else if (objectFilter instanceof SubstringFilter) { SubstringFilter substring = (SubstringFilter) objectFilter; diff --git a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/test/impl/AbstractOpenDJTest.java b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/test/impl/AbstractOpenDJTest.java index c98f66ca547..82d4eb23dd2 100644 --- a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/test/impl/AbstractOpenDJTest.java +++ b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/test/impl/AbstractOpenDJTest.java @@ -15,6 +15,8 @@ */ package com.evolveum.midpoint.provisioning.test.impl; +import java.io.File; + import javax.xml.namespace.QName; import org.springframework.beans.factory.annotation.Autowired; @@ -41,6 +43,7 @@ public abstract class AbstractOpenDJTest extends AbstractIntegrationTest { protected static final String TEST_DIR_NAME = "src/test/resources/impl/opendj"; + protected static final File TEST_DIR = new File(TEST_DIR_NAME); protected static final String RESOURCE_OPENDJ_FILENAME = ProvisioningTestUtil.COMMON_TEST_DIR_FILENAME + "resource-opendj.xml"; protected static final String RESOURCE_OPENDJ_INITIALIZED_FILENAME = ProvisioningTestUtil.COMMON_TEST_DIR_FILENAME + "resource-opendj-initialized.xml"; @@ -88,6 +91,14 @@ public abstract class AbstractOpenDJTest extends AbstractIntegrationTest { protected static final String ACCOUNT_NO_SN_FILENAME = TEST_DIR_NAME + "/account-opendj-no-sn.xml"; protected static final String ACCOUNT_NO_SN_OID = "c0c010c0-d34d-beef-f33d-113222123444"; + protected static final File ACCOUNT_MORGAN_FILE = new File(TEST_DIR, "account-morgan.xml"); + protected static final String ACCOUNT_MORGAN_OID = "8dfcf05e-c571-11e3-abbd-001e8c717e5b"; + protected static final String ACCOUNT_MORGAN_DN = "uid=morgan,ou=People,dc=example,dc=com"; + + protected static final File GROUP_SWASHBUCKLERS_FILE = new File(TEST_DIR, "group-swashbucklers.xml"); + protected static final String GROUP_SWASHBUCKLERS_OID = "3d96846e-c570-11e3-a80f-001e8c717e5b"; + protected static final String GROUP_SWASHBUCKLERS_DN = "cn=swashbucklers,ou=Groups,dc=example,dc=com"; + protected static final String NON_EXISTENT_OID = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"; protected static final String RESOURCE_NS = "http://midpoint.evolveum.com/xml/ns/public/resource/instance/ef2bc95b-76e0-59e2-86d6-3d4f02d3ffff"; diff --git a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/test/impl/TestOpenDJ.java b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/test/impl/TestOpenDJ.java index f7fe613a5ff..6741dd7243b 100644 --- a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/test/impl/TestOpenDJ.java +++ b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/test/impl/TestOpenDJ.java @@ -36,6 +36,7 @@ import javax.xml.namespace.QName; import com.evolveum.midpoint.prism.PrismContext; + import org.apache.commons.lang.StringUtils; import org.opends.server.types.SearchResultEntry; import org.springframework.beans.factory.annotation.Autowired; @@ -114,6 +115,7 @@ import com.evolveum.midpoint.xml.ns._public.common.common_2a.ConnectorType; import com.evolveum.midpoint.xml.ns._public.common.common_2a.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_2a.ProvisioningScriptHostType; +import com.evolveum.midpoint.xml.ns._public.common.common_2a.ShadowAssociationType; import com.evolveum.midpoint.xml.ns._public.common.common_2a.ShadowType; import com.evolveum.midpoint.xml.ns._public.common.common_2a.ResourceType; import com.evolveum.midpoint.xml.ns._public.common.common_2a.ShadowKindType; @@ -956,9 +958,128 @@ public void test180AddDisabledAccount() throws Exception { assertNotNull("No activation disableTimestamp in repo", repoDisableTimestamp); assertEquals("Wrong activation disableTimestamp in repo", XmlTypeConverter.createXMLGregorianCalendar(1999, 8, 7, 6, 5, 4), repoDisableTimestamp); - } + + @Test + public void test190AddGroupSwashbucklers() throws Exception { + final String TEST_NAME = "test190AddGroupSwashbucklers"; + TestUtil.displayTestTile(TEST_NAME); + + OperationResult result = new OperationResult(TestOpenDJ.class.getName() + + "." + TEST_NAME); + + ShadowType object = parseObjectType(GROUP_SWASHBUCKLERS_FILE, ShadowType.class); + IntegrationTestTools.display("Adding object", object); + + // WHEN + String addedObjectOid = provisioningService.addObject(object.asPrismObject(), null, null, taskManager.createTaskInstance(), result); + + // THEN + assertEquals(GROUP_SWASHBUCKLERS_OID, addedObjectOid); + + ShadowType shadowType = repositoryService.getObject(ShadowType.class, GROUP_SWASHBUCKLERS_OID, + null, result).asObjectable(); + PrismAsserts.assertEqualsPolyString("Wrong ICF name (repo)", GROUP_SWASHBUCKLERS_DN, shadowType.getName()); + + ShadowType provisioningShadowType = provisioningService.getObject(ShadowType.class, GROUP_SWASHBUCKLERS_OID, + null, taskManager.createTaskInstance(), result).asObjectable(); + PrismAsserts.assertEqualsPolyString("Wrong ICF name (provisioning)", GROUP_SWASHBUCKLERS_DN, provisioningShadowType.getName()); + + String uid = ShadowUtil.getSingleStringAttributeValue(shadowType, ConnectorFactoryIcfImpl.ICFS_UID); + assertNotNull(uid); + + SearchResultEntry ldapEntry = openDJController.searchAndAssertByEntryUuid(uid); + display("LDAP group", ldapEntry); + assertNotNull("No LDAP group entry"); + String groupDn = ldapEntry.getDN().toString(); + assertEquals("Wrong group DN", GROUP_SWASHBUCKLERS_DN, groupDn); + } + + @Test + public void test192AddAccountMorganWithAssociation() throws Exception { + final String TEST_NAME = "test192AddAccountMorganWithAssociation"; + TestUtil.displayTestTile(TEST_NAME); + + OperationResult result = new OperationResult(TestOpenDJ.class.getName() + + "." + TEST_NAME); + + ShadowType object = parseObjectType(ACCOUNT_MORGAN_FILE, ShadowType.class); + IntegrationTestTools.display("Adding object", object); + + // WHEN + String addedObjectOid = provisioningService.addObject(object.asPrismObject(), null, null, taskManager.createTaskInstance(), result); + + // THEN + assertEquals(ACCOUNT_MORGAN_OID, addedObjectOid); + ShadowType shadowType = repositoryService.getObject(ShadowType.class, ACCOUNT_MORGAN_OID, + null, result).asObjectable(); + PrismAsserts.assertEqualsPolyString("Wrong ICF name (repo)", ACCOUNT_MORGAN_DN, shadowType.getName()); + + ShadowType provisioningShadowType = provisioningService.getObject(ShadowType.class, ACCOUNT_MORGAN_OID, + null, taskManager.createTaskInstance(), result).asObjectable(); + PrismAsserts.assertEqualsPolyString("Wrong ICF name (provisioning)", ACCOUNT_MORGAN_DN, provisioningShadowType.getName()); + + String uid = ShadowUtil.getSingleStringAttributeValue(shadowType, ConnectorFactoryIcfImpl.ICFS_UID); + assertNotNull(uid); + + List associations = provisioningShadowType.getAssociation(); + assertEquals("Unexpected number of associations", 1, associations.size()); + ShadowAssociationType association = associations.get(0); + assertEquals("Wrong group OID in association", GROUP_SWASHBUCKLERS_OID, association.getShadowRef().getOid()); + + SearchResultEntry accountEntry = openDJController.searchAndAssertByEntryUuid(uid); + display("LDAP account", accountEntry); + assertNotNull("No LDAP account entry"); + String accountDn = accountEntry.getDN().toString(); + assertEquals("Wrong account DN", ACCOUNT_MORGAN_DN, accountDn); + + SearchResultEntry groupEntry = openDJController.fetchEntry(GROUP_SWASHBUCKLERS_DN); + display("LDAP group", groupEntry); + assertNotNull("No LDAP group entry"); + openDJController.assertUniqueMember(groupEntry, accountDn); + } + + /** + * Morgan has a group association. If the account is gone the group membership should also be gone. + */ + @Test + public void test199DeleteAccountMorgan() throws Exception { + final String TEST_NAME = "test199DeleteAccountMorgan"; + TestUtil.displayTestTile(TEST_NAME); + Task task = taskManager.createTaskInstance(TestOpenDJ.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + provisioningService.deleteObject(ShadowType.class, ACCOUNT_MORGAN_OID, null, null, task, result); + + ShadowType objType = null; + + try { + objType = provisioningService.getObject(ShadowType.class, ACCOUNT_MORGAN_OID, + null, task, result).asObjectable(); + Assert.fail("Expected exception ObjectNotFoundException, but haven't got one."); + } catch (ObjectNotFoundException ex) { + System.out.println("Catched ObjectNotFoundException."); + assertNull(objType); + } + + try { + objType = repositoryService.getObject(ShadowType.class, ACCOUNT_MORGAN_OID, + null, result).asObjectable(); + // objType = container.getObject(); + Assert.fail("Expected exception, but haven't got one."); + } catch (Exception ex) { + assertNull(objType); + assertEquals(ex.getClass(), ObjectNotFoundException.class); + assertTrue(ex.getMessage().contains(ACCOUNT_MORGAN_OID)); + } + + SearchResultEntry groupEntry = openDJController.fetchEntry(GROUP_SWASHBUCKLERS_DN); + display("LDAP group", groupEntry); + assertNotNull("No LDAP group entry"); + openDJController.assertNoUniqueMember(groupEntry, ACCOUNT_MORGAN_DN); + } @Test public void test200SearchObjectsIterative() throws Exception { diff --git a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/test/mock/SynchornizationServiceMock.java b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/test/mock/SynchornizationServiceMock.java index 995d477a1be..240a53ad7f3 100644 --- a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/test/mock/SynchornizationServiceMock.java +++ b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/test/mock/SynchornizationServiceMock.java @@ -142,9 +142,6 @@ public void notifyChange(ResourceObjectShadowChangeDescription change, Task task } else { assertNull("Activation sneaked into current shadow", account.getActivation()); } - } else { - // We don't support other types now - AssertJUnit.fail("Unexpected kind of shadow " + change.getCurrentShadow().asObjectable().getKind()); } } } diff --git a/provisioning/provisioning-impl/src/test/resources/impl/opendj/account-morgan.xml b/provisioning/provisioning-impl/src/test/resources/impl/opendj/account-morgan.xml new file mode 100644 index 00000000000..885f8184985 --- /dev/null +++ b/provisioning/provisioning-impl/src/test/resources/impl/opendj/account-morgan.xml @@ -0,0 +1,39 @@ + + + + + + uid=morgan,ou=People,dc=example,dc=com + + ri:AccountObjectClass + account + + uid=morgan,ou=People,dc=example,dc=com + morgan + Henry Morgan + Henry + Morgan + + + ri:group + + + \ No newline at end of file diff --git a/provisioning/provisioning-impl/src/test/resources/impl/opendj/group-swashbucklers.xml b/provisioning/provisioning-impl/src/test/resources/impl/opendj/group-swashbucklers.xml new file mode 100644 index 00000000000..b1411458ace --- /dev/null +++ b/provisioning/provisioning-impl/src/test/resources/impl/opendj/group-swashbucklers.xml @@ -0,0 +1,33 @@ + + + + + + cn=swashbucklers,ou=Groups,dc=example,dc=com + + ri:GroupObjectClass + entitlement + group + + cn=swashbucklers,ou=Groups,dc=example,dc=com + swashbucklers + + \ No newline at end of file diff --git a/provisioning/provisioning-impl/src/test/resources/object/resource-opendj.xml b/provisioning/provisioning-impl/src/test/resources/object/resource-opendj.xml index 59e49bc2a86..e2ce79b7e50 100644 --- a/provisioning/provisioning-impl/src/test/resources/object/resource-opendj.xml +++ b/provisioning/provisioning-impl/src/test/resources/object/resource-opendj.xml @@ -65,6 +65,10 @@ -1 -1 + + + true + @@ -92,6 +96,7 @@ objectToSubject ri:uniqueMember icfs:name + true diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestVillage.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestVillage.java index 5b4c42e58cd..6432a2d7de7 100644 --- a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestVillage.java +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestVillage.java @@ -16,6 +16,7 @@ */ +import static org.testng.AssertJUnit.assertTrue; import static com.evolveum.midpoint.test.IntegrationTestTools.display; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; @@ -310,14 +311,8 @@ public void test100AddSrcAccountHerman() throws Exception { // THEN PrismObject user = findUserByUsername(USER_HERMAN_NAME); - assertNotNull("No herman user", user); - display("User", user); - assertUser(user, user.getOid(), USER_HERMAN_NAME, ACCOUNT_HERMAN_FIST_NAME+" "+ACCOUNT_HERMAN_LAST_NAME, - ACCOUNT_HERMAN_FIST_NAME, ACCOUNT_HERMAN_LAST_NAME); - assertLocGov(user, ACCOUNT_HERMAN_LOC, ACCOUNT_HERMAN_ORG); - assertLinks(user, 1); - assertAccount(user, RESOURCE_DUMMY_SOURCE_OID); - assertAssignments(user, 0); + assertUserNoRole(user, ACCOUNT_HERMAN_FIST_NAME, ACCOUNT_HERMAN_LAST_NAME); + assertLocGov(user, ACCOUNT_HERMAN_LOC, ACCOUNT_HERMAN_ORG); } @Test @@ -338,6 +333,116 @@ public void test102HermanAssignBasicRole() throws Exception { assertLdapLocGov(userAfter, ACCOUNT_HERMAN_LOC, ACCOUNT_HERMAN_ORG); } + @Test + public void test104HermanUnAssignBasicRole() throws Exception { + final String TEST_NAME = "test104HermanUnAssignBasicRole"; + TestUtil.displayTestTile(this, TEST_NAME); + Task task = taskManager.createTaskInstance(TestTrafo.class.getName() + "." + TEST_NAME); + + PrismObject user = findUserByUsername(USER_HERMAN_NAME); + + // WHEN + unassignRole(user.getOid(), ROLE_BASIC_OID); + + // THEN + PrismObject userAfter = getUser(user.getOid()); + assertUserNoRole(userAfter, ACCOUNT_HERMAN_FIST_NAME, ACCOUNT_HERMAN_LAST_NAME); + assertLocGov(userAfter, ACCOUNT_HERMAN_LOC, ACCOUNT_HERMAN_ORG); + assertNoLdapLocGov(userAfter, ACCOUNT_HERMAN_LOC, ACCOUNT_HERMAN_ORG); + } + + @Test + public void test105ModifySrcAccountHermanRemoveLoc() throws Exception { + final String TEST_NAME = "test105ModifySrcAccountHermanRemoveLoc"; + TestUtil.displayTestTile(this, TEST_NAME); + Task task = taskManager.createTaskInstance(TestTrafo.class.getName() + "." + TEST_NAME); + + DummyAccount account = dummyResourceSrc.getAccountByUsername(ACCOUNT_HERMAN_USERNAME); + + // WHEN + account.replaceAttributeValues(DUMMY_ACCOUNT_ATTRIBUTE_SRC_LOC); + waitForTaskNextRun(TASK_LIVE_SYNC_DUMMY_SOURCE_OID, true); + + // THEN + PrismObject user = findUserByUsername(USER_HERMAN_NAME); + assertUserNoRole(user, ACCOUNT_HERMAN_FIST_NAME, ACCOUNT_HERMAN_LAST_NAME); + assertLocGov(user, null, ACCOUNT_HERMAN_ORG); + } + + @Test + public void test106HermanAssignBasicRole() throws Exception { + final String TEST_NAME = "test106HermanAssignBasicRole"; + TestUtil.displayTestTile(this, TEST_NAME); + Task task = taskManager.createTaskInstance(TestTrafo.class.getName() + "." + TEST_NAME); + + PrismObject user = findUserByUsername(USER_HERMAN_NAME); + + // WHEN + assignRole(user.getOid(), ROLE_BASIC_OID); + + // THEN + PrismObject userAfter = getUser(user.getOid()); + assertUserLdap(userAfter, ACCOUNT_HERMAN_FIST_NAME, ACCOUNT_HERMAN_LAST_NAME); + assertLocGov(userAfter, null, ACCOUNT_HERMAN_ORG); + assertNoLdapLocGov(userAfter, ACCOUNT_HERMAN_LOC, ACCOUNT_HERMAN_ORG); + } + + @Test + public void test107ModifySrcAccountHermanAddLoc() throws Exception { + final String TEST_NAME = "test107ModifySrcAccountHermanAddLoc"; + TestUtil.displayTestTile(this, TEST_NAME); + Task task = taskManager.createTaskInstance(TestTrafo.class.getName() + "." + TEST_NAME); + + DummyAccount account = dummyResourceSrc.getAccountByUsername(ACCOUNT_HERMAN_USERNAME); + + // WHEN + account.replaceAttributeValues(DUMMY_ACCOUNT_ATTRIBUTE_SRC_LOC, ACCOUNT_HERMAN_LOC); + waitForTaskNextRun(TASK_LIVE_SYNC_DUMMY_SOURCE_OID, true); + + // THEN + PrismObject user = findUserByUsername(USER_HERMAN_NAME); + assertUserLdap(user, ACCOUNT_HERMAN_FIST_NAME, ACCOUNT_HERMAN_LAST_NAME); + assertLocGov(user, ACCOUNT_HERMAN_LOC, ACCOUNT_HERMAN_ORG); + assertLdapLocGov(user, ACCOUNT_HERMAN_LOC, ACCOUNT_HERMAN_ORG); + } + + @Test + public void test108ModifySrcAccountHermanRemoveLoc() throws Exception { + final String TEST_NAME = "test108ModifySrcAccountHermanRemoveLoc"; + TestUtil.displayTestTile(this, TEST_NAME); + Task task = taskManager.createTaskInstance(TestTrafo.class.getName() + "." + TEST_NAME); + + DummyAccount account = dummyResourceSrc.getAccountByUsername(ACCOUNT_HERMAN_USERNAME); + + // WHEN + account.replaceAttributeValues(DUMMY_ACCOUNT_ATTRIBUTE_SRC_LOC); + waitForTaskNextRun(TASK_LIVE_SYNC_DUMMY_SOURCE_OID, true); + + // THEN + PrismObject user = findUserByUsername(USER_HERMAN_NAME); + assertUserLdap(user, ACCOUNT_HERMAN_FIST_NAME, ACCOUNT_HERMAN_LAST_NAME); + assertLocGov(user, null, ACCOUNT_HERMAN_ORG); + assertNoLdapLocGov(user, ACCOUNT_HERMAN_LOC, ACCOUNT_HERMAN_ORG); + } + + @Test + public void test109HermanUnAssignBasicRole() throws Exception { + final String TEST_NAME = "test109HermanUnAssignBasicRole"; + TestUtil.displayTestTile(this, TEST_NAME); + Task task = taskManager.createTaskInstance(TestTrafo.class.getName() + "." + TEST_NAME); + + PrismObject user = findUserByUsername(USER_HERMAN_NAME); + + // WHEN + unassignRole(user.getOid(), ROLE_BASIC_OID); + + // THEN + PrismObject userAfter = getUser(user.getOid()); + assertUserNoRole(userAfter, ACCOUNT_HERMAN_FIST_NAME, ACCOUNT_HERMAN_LAST_NAME); + assertLocGov(userAfter, null, ACCOUNT_HERMAN_ORG); + assertNoLdapLocGov(userAfter, ACCOUNT_HERMAN_LOC, ACCOUNT_HERMAN_ORG); + } + @Test public void test110AddSrcAccountLemonhead() throws Exception { final String TEST_NAME = "test110AddSrcAccountLemonhead"; @@ -367,26 +472,43 @@ private void assertLocGov(PrismObject user, String expLoc, String expO PrismAsserts.assertEqualsPolyString("Wrong locality in "+user, expLoc, userType.getLocality()); PrismAsserts.assertEqualsCollectionUnordered("Wrong organization in "+user, userType.getOrganization(), PrismTestUtil.createPolyStringType(expOrg)); - PrismAsserts.assertEqualsCollectionUnordered("Wrong organizationalUnit in "+user, userType.getOrganizationalUnit(), + if (expLoc == null || expOrg == null) { + assertTrue("Wrong organizationalUnit in "+user+", expected empty but was "+userType.getOrganizationalUnit(), userType.getOrganizationalUnit().isEmpty()); + } else { + PrismAsserts.assertEqualsCollectionUnordered("Wrong organizationalUnit in "+user, userType.getOrganizationalUnit(), PrismTestUtil.createPolyStringType(expOrg+":"+expLoc)); + } + } + + private void assertUserNoRole(PrismObject user, String firstName, String lastName) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, DirectoryException { + String username = firstName+"."+lastName; + assertNotNull("No "+username+" user", user); + display("User", user); + assertUser(user, user.getOid(), username, firstName+" "+lastName, + firstName, lastName); + assertLinks(user, 1); + assertAccount(user, RESOURCE_DUMMY_SOURCE_OID); + assertAssignments(user, 0); + + openDJController.assertNoEntry("uid="+username+",ou=people,dc=example,dc=com"); } - private void assertUserLdap(PrismObject userAfter, String firstName, String lastName) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException { + private void assertUserLdap(PrismObject user, String firstName, String lastName) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException { String username = firstName+"."+lastName; - assertNotNull("No "+username+" user", userAfter); - display("User", userAfter); - assertUser(userAfter, userAfter.getOid(), username, firstName+" "+lastName, + assertNotNull("No "+username+" user", user); + display("User", user); + assertUser(user, user.getOid(), username, firstName+" "+lastName, firstName, lastName); - assertLinks(userAfter, 2); - assertAccount(userAfter, RESOURCE_DUMMY_SOURCE_OID); + assertLinks(user, 2); + assertAccount(user, RESOURCE_DUMMY_SOURCE_OID); - assertAssignments(userAfter, 1); - assertAssignedRole(userAfter, ROLE_BASIC_OID); + assertAssignments(user, 1); + assertAssignedRole(user, ROLE_BASIC_OID); - assertAccount(userAfter, RESOURCE_OPENDJ_OID); - PrismReferenceValue linkRef = getLinkRef(userAfter, RESOURCE_OPENDJ_OID); + assertAccount(user, RESOURCE_OPENDJ_OID); + PrismReferenceValue linkRef = getLinkRef(user, RESOURCE_OPENDJ_OID); PrismObject shadow = getShadowModel(linkRef.getOid()); - display("OpenDJ shadow linked to "+userAfter, shadow); + display("OpenDJ shadow linked to "+user, shadow); IntegrationTestTools.assertIcfsNameAttribute(shadow, "uid="+username+",ou=people,dc=example,dc=com"); } @@ -404,4 +526,15 @@ private void assertLdapLocGov(PrismObject user, String expLoc, String openDJController.assertUniqueMember(groupEntry, accountDn); } + private void assertNoLdapLocGov(PrismObject user, String expLoc, String expOrg) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, DirectoryException { + UserType userType = user.asObjectable(); + + String groupCn = expOrg+":"+expLoc; + String groupDn = "cn="+groupCn+",ou=groups,"+openDJController.getSuffix(); + SearchResultEntry groupEntry = openDJController.fetchAndAssertEntry(groupDn, "groupOfUniqueNames"); + display("Group entry", groupEntry); + String accountDn = "uid="+userType.getName()+",ou=people,"+openDJController.getSuffix(); + openDJController.assertNoUniqueMember(groupEntry, accountDn); + } + } diff --git a/testing/story/src/test/resources/logback-test.xml b/testing/story/src/test/resources/logback-test.xml index 7317a2d7fe3..47b3e8691d2 100644 --- a/testing/story/src/test/resources/logback-test.xml +++ b/testing/story/src/test/resources/logback-test.xml @@ -39,7 +39,7 @@ - + @@ -54,7 +54,7 @@ - + diff --git a/testing/story/src/test/resources/village/resource-opendj.xml b/testing/story/src/test/resources/village/resource-opendj.xml index 897aa13e858..b24963f9596 100644 --- a/testing/story/src/test/resources/village/resource-opendj.xml +++ b/testing/story/src/test/resources/village/resource-opendj.xml @@ -35,7 +35,7 @@ Dummy description, just for the test - c:connectorType + connectorType org.identityconnectors.ldap.LdapConnector @@ -55,6 +55,10 @@ uid ds-pwp-account-disabled + + + true + @@ -74,9 +78,6 @@ $user/name - - $focus/extension/ext:orgpath -