diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/IcfConvertor.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/IcfConvertor.java index 6f30c9c7c72..a0f6a212fef 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/IcfConvertor.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/IcfConvertor.java @@ -126,9 +126,10 @@ PrismObject convertToResourceObject(ConnectorObject co List auxiliaryObjectClassDefinitions = new ArrayList<>(); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Resource attribute container definition {}.", attributesContainerDefinition.debugDump()); - } + // too loud +// if (LOGGER.isTraceEnabled()) { +// LOGGER.trace("Resource attribute container definition {}.", attributesContainerDefinition.debugDump()); +// } // Uid is always there Uid uid = co.getUid(); diff --git a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractEDirTest.java b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractEDirTest.java index 2d0bad918b9..0a4ee51f0bb 100644 --- a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractEDirTest.java +++ b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractEDirTest.java @@ -22,7 +22,13 @@ import static org.testng.AssertJUnit.assertFalse; import java.io.File; +import java.io.IOException; +import java.util.Collection; +import javax.xml.namespace.QName; + +import org.apache.directory.api.ldap.model.cursor.CursorException; +import org.apache.directory.api.ldap.model.entry.Entry; import org.apache.directory.api.ldap.model.exception.LdapException; import org.apache.directory.ldap.client.api.LdapNetworkConnection; import org.testng.AssertJUnit; @@ -31,25 +37,37 @@ import org.testng.annotations.Test; import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.prism.delta.PropertyDelta; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.query.EqualFilter; import com.evolveum.midpoint.prism.query.ObjectFilter; import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.prism.util.PrismTestUtil; import com.evolveum.midpoint.schema.SearchResultList; import com.evolveum.midpoint.schema.SearchResultMetadata; +import com.evolveum.midpoint.schema.constants.MidPointConstants; import com.evolveum.midpoint.schema.processor.ResourceAttribute; import com.evolveum.midpoint.schema.processor.ResourceAttributeDefinition; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.MiscSchemaUtil; import com.evolveum.midpoint.schema.util.ObjectQueryUtil; import com.evolveum.midpoint.schema.util.ShadowUtil; import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.IntegrationTestTools; import com.evolveum.midpoint.test.util.MidPointTestConstants; import com.evolveum.midpoint.test.util.TestUtil; +import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.LockoutStatusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; /** * @author semancik @@ -58,11 +76,30 @@ @Listeners({com.evolveum.midpoint.tools.testng.AlphabeticalMethodInterceptor.class}) public abstract class AbstractEDirTest extends AbstractLdapTest { + protected static final File TEST_DIR = new File(MidPointTestConstants.TEST_RESOURCES_DIR, "edir"); + + protected static final File ROLE_PIRATES_FILE = new File(TEST_DIR, "role-pirate.xml"); + protected static final String ROLE_PIRATES_OID = "5dd034e8-41d2-11e5-a123-001e8c717e5b"; + + public static final String ATTRIBUTE_LOCKOUT_LOCKED_NAME = "lockedByIntruder"; + public static final String ATTRIBUTE_LOCKOUT_RESET_TIME_NAME = "loginIntruderResetTime"; + public static final String ATTRIBUTE_GROUP_MEMBERSHIP_NAME = "groupMembership"; + public static final String ATTRIBUTE_EQUIVALENT_TO_ME_NAME = "equivalentToMe"; + public static final String ATTRIBUTE_SECURITY_EQUALS_NAME = "securityEquals"; + protected static final String ACCOUNT_JACK_UID = "jack"; - protected static final int NUMBER_OF_ACCOUNTS = 5; + protected static final String ACCOUNT_JACK_PASSWORD = "qwe123"; + + private static final String GROUP_PIRATES_NAME = "pirates"; + + protected static final int NUMBER_OF_ACCOUNTS = 4; protected static final int LOCKOUT_EXPIRATION_SECONDS = 65; + private static final String ASSOCIATION_GROUP_NAME = "group"; + protected String jackAccountOid; + protected String groupPiratesOid; protected long jackLockoutTimestamp; + private String accountBarbossaOid; @Override @@ -77,7 +114,7 @@ public String getStopSystemCommand() { @Override protected File getBaseDir() { - return new File(MidPointTestConstants.TEST_RESOURCES_DIR, "edir"); + return TEST_DIR; } @Override @@ -125,6 +162,34 @@ protected String getLdapGroupMemberAttribute() { return "member"; } + private QName getAssociationGroupQName() { + return new QName(MidPointConstants.NS_RI, ASSOCIATION_GROUP_NAME); + } + + @Override + public void initSystem(Task initTask, OperationResult initResult) throws Exception { + super.initSystem(initTask, initResult); + + binaryAttributeDetector.addBinaryAttribute("GUID"); + + // Users + repoAddObjectFromFile(USER_BARBOSSA_FILE, UserType.class, initResult); + repoAddObjectFromFile(USER_GUYBRUSH_FILE, UserType.class, initResult); + + // Roles + repoAddObjectFromFile(ROLE_PIRATES_FILE, RoleType.class, initResult); + + } + + @Test + public void test000Sanity() throws Exception { + assertLdapPassword(ACCOUNT_JACK_UID, ACCOUNT_JACK_PASSWORD); + assertEDirGroupMember(ACCOUNT_JACK_UID, GROUP_PIRATES_NAME); + cleanupDelete(toDn(USER_BARBOSSA_USERNAME)); + cleanupDelete(toDn(USER_CPTBARBOSSA_USERNAME)); + cleanupDelete(toDn(USER_GUYBRUSH_USERNAME)); + } + @Test public void test100SeachJackByLdapUid() throws Exception { final String TEST_NAME = "test100SeachJackByLdapUid"; @@ -134,8 +199,6 @@ public void test100SeachJackByLdapUid() throws Exception { Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); OperationResult result = task.getResult(); - ResourceAttributeDefinition ldapUidAttrDef = accountObjectClassDefinition.findAttributeDefinition("uid"); - ObjectQuery query = createUidQuery(ACCOUNT_JACK_UID); rememberConnectorOperationCount(); @@ -155,6 +218,7 @@ public void test100SeachJackByLdapUid() throws Exception { display("Shadow", shadow); assertAccountShadow(shadow, toDn(ACCOUNT_JACK_UID)); assertLockout(shadow, LockoutStatusType.NORMAL); + jackAccountOid = shadow.getOid(); assertConnectorOperationIncrement(2); assertConnectorSimulatedPagingSearchIncrement(0); @@ -166,8 +230,78 @@ public void test100SeachJackByLdapUid() throws Exception { } @Test - public void test110JackLockout() throws Exception { - final String TEST_NAME = "test110JackLockout"; + public void test105SeachPiratesByCn() throws Exception { + final String TEST_NAME = "test105SeachPiratesByCn"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + ObjectQuery query = ObjectQueryUtil.createResourceAndObjectClassQuery(getResourceOid(), getGroupObjectClass(), prismContext); + ObjectQueryUtil.filterAnd(query.getFilter(), createAttributeFilter("cn", GROUP_PIRATES_NAME)); + + rememberConnectorOperationCount(); + rememberConnectorSimulatedPagingSearchCount(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + SearchResultList> shadows = modelService.searchObjects(ShadowType.class, query, null, task, result); + + // THEN + result.computeStatus(); + TestUtil.assertSuccess(result); + + assertEquals("Unexpected search result: "+shadows, 1, shadows.size()); + + PrismObject shadow = shadows.get(0); + display("Shadow", shadow); + groupPiratesOid = shadow.getOid(); + + assertConnectorOperationIncrement(1); + assertConnectorSimulatedPagingSearchIncrement(0); + + SearchResultMetadata metadata = shadows.getMetadata(); + if (metadata != null) { + assertFalse(metadata.isPartialResults()); + } + } + + @Test + public void test110GetJack() throws Exception { + final String TEST_NAME = "test110GetJack"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + ObjectQuery query = createUidQuery(ACCOUNT_JACK_UID); + + rememberConnectorOperationCount(); + rememberConnectorSimulatedPagingSearchCount(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + PrismObject shadow = modelService.getObject(ShadowType.class, jackAccountOid, null, task, result); + + // THEN + result.computeStatus(); + TestUtil.assertSuccess(result); + display("Shadow", shadow); + assertAccountShadow(shadow, toDn(ACCOUNT_JACK_UID)); + assertLockout(shadow, LockoutStatusType.NORMAL); + jackAccountOid = shadow.getOid(); + + IntegrationTestTools.assertAssociation(shadow, getAssociationGroupQName(), groupPiratesOid); + + assertConnectorOperationIncrement(1); + assertConnectorSimulatedPagingSearchIncrement(0); + } + + @Test + public void test120JackLockout() throws Exception { + final String TEST_NAME = "test120JackLockout"; TestUtil.displayTestTile(this, TEST_NAME); // GIVEN @@ -202,7 +336,7 @@ public void test110JackLockout() throws Exception { assertAccountShadow(shadow, toDn(ACCOUNT_JACK_UID)); assertLockout(shadow, LockoutStatusType.LOCKED); - assertConnectorOperationIncrement(2); + assertConnectorOperationIncrement(1); assertConnectorSimulatedPagingSearchIncrement(0); SearchResultMetadata metadata = shadows.getMetadata(); @@ -227,7 +361,7 @@ public void test150SeachAllAccounts() throws Exception { SearchResultList> searchResultList = doSearch(TEST_NAME, query, NUMBER_OF_ACCOUNTS, task, result); - assertConnectorOperationIncrement(6); + assertConnectorOperationIncrement(1); assertConnectorSimulatedPagingSearchIncrement(0); SearchResultMetadata metadata = searchResultList.getMetadata(); @@ -251,7 +385,7 @@ public void test190SeachLockedAccounts() throws Exception { SearchResultList> searchResultList = doSearch(TEST_NAME, query, 1, task, result); - assertConnectorOperationIncrement(2); + assertConnectorOperationIncrement(1); assertConnectorSimulatedPagingSearchIncrement(0); PrismObject shadow = searchResultList.get(0); @@ -265,9 +399,215 @@ public void test190SeachLockedAccounts() throws Exception { } } - // TODO: Search all locked accounts. Jack should be there. + @Test + public void test200AssignAccountBarbossa() throws Exception { + final String TEST_NAME = "test200AssignAccountBarbossa"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + long tsStart = System.currentTimeMillis(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + assignAccount(USER_BARBOSSA_OID, getResourceOid(), null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + long tsEnd = System.currentTimeMillis(); + + Entry entry = assertLdapAccount(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + assertAttribute(entry, "title", null); + + PrismObject user = getUser(USER_BARBOSSA_OID); + String shadowOid = getSingleLinkOid(user); + PrismObject shadow = getShadowModel(shadowOid); + display("Shadow (model)", shadow); + accountBarbossaOid = shadow.getOid(); + Collection> identifiers = ShadowUtil.getIdentifiers(shadow); + String accountBarbossaIcfUid = (String) identifiers.iterator().next().getRealValue(); + assertNotNull("No identifier in "+shadow, accountBarbossaIcfUid); + + assertEquals("Wrong ICFS UID", MiscUtil.binaryToHex(entry.get(getPrimaryIdentifierAttributeName()).getBytes()), accountBarbossaIcfUid); + + assertLdapPassword(USER_BARBOSSA_USERNAME, "deadjacktellnotales"); + + ResourceAttribute createTimestampAttribute = ShadowUtil.getAttribute(shadow, new QName(MidPointConstants.NS_RI, "createTimestamp")); + assertNotNull("No createTimestamp in "+shadow, createTimestampAttribute); + Long createTimestamp = createTimestampAttribute.getRealValue(); + // LDAP server may be on a different host. Allow for some clock offset. + TestUtil.assertBetween("Wrong createTimestamp in "+shadow, roundTsDown(tsStart)-1000, roundTsUp(tsEnd)+1000, createTimestamp); + } + + @Test + public void test210ModifyAccountBarbossaTitle() throws Exception { + final String TEST_NAME = "test210ModifyAccountBarbossaTitle"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + ObjectDelta delta = ObjectDelta.createEmptyModifyDelta(ShadowType.class, accountBarbossaOid, prismContext); + QName attrQName = new QName(MidPointConstants.NS_RI, "title"); + ResourceAttributeDefinition attrDef = accountObjectClassDefinition.findAttributeDefinition(attrQName); + PropertyDelta attrDelta = PropertyDelta.createModificationReplaceProperty( + new ItemPath(ShadowType.F_ATTRIBUTES, attrQName), attrDef, "Captain"); + delta.addModification(attrDelta); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + modelService.executeChanges(MiscSchemaUtil.createCollection(delta), null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + assertAttribute(entry, "title", "Captain"); + + PrismObject user = getUser(USER_BARBOSSA_OID); + String shadowOid = getSingleLinkOid(user); + assertEquals("Shadows have moved", accountBarbossaOid, shadowOid); + } - // TODO: test rename + @Test + public void test220ModifyUserBarbossaPassword() throws Exception { + final String TEST_NAME = "test220ModifyUserBarbossaPassword"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + ProtectedStringType userPasswordPs = new ProtectedStringType(); + userPasswordPs.setClearValue("hereThereBeMonsters"); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + modifyUserReplace(USER_BARBOSSA_OID, + new ItemPath(UserType.F_CREDENTIALS, CredentialsType.F_PASSWORD, PasswordType.F_VALUE), + task, result, userPasswordPs); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + assertAttribute(entry, "title", "Captain"); + assertLdapPassword(USER_BARBOSSA_USERNAME, "hereThereBeMonsters"); + + PrismObject user = getUser(USER_BARBOSSA_OID); + String shadowOid = getSingleLinkOid(user); + assertEquals("Shadows have moved", accountBarbossaOid, shadowOid); + } + + /** + * This should create account with a group. + */ + @Test + public void test250AssignGuybrushPirates() throws Exception { + final String TEST_NAME = "test250AssignGuybrushPirates"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + assignRole(USER_GUYBRUSH_OID, ROLE_PIRATES_OID, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_GUYBRUSH_USERNAME, USER_GUYBRUSH_FULL_NAME); + display("Entry", entry); + + assertEDirGroupMember(entry, GROUP_PIRATES_NAME); + + PrismObject user = getUser(USER_GUYBRUSH_OID); + String shadowOid = getSingleLinkOid(user); + + PrismObject shadow = getObject(ShadowType.class, shadowOid); + IntegrationTestTools.assertAssociation(shadow, getAssociationGroupQName(), groupPiratesOid); + } + + @Test + public void test300AssignBarbossaPirates() throws Exception { + final String TEST_NAME = "test300AssignBarbossaPirates"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + assignRole(USER_BARBOSSA_OID, ROLE_PIRATES_OID, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + display("Entry", entry); + assertAttribute(entry, "title", "Captain"); + + assertEDirGroupMember(entry, GROUP_PIRATES_NAME); + + PrismObject user = getUser(USER_BARBOSSA_OID); + String shadowOid = getSingleLinkOid(user); + assertEquals("Shadows have moved", accountBarbossaOid, shadowOid); + + PrismObject shadow = getObject(ShadowType.class, shadowOid); + IntegrationTestTools.assertAssociation(shadow, getAssociationGroupQName(), groupPiratesOid); + + } + + @Test + public void test390ModifyUserBarbossaRename() throws Exception { + final String TEST_NAME = "test390ModifyUserBarbossaRename"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + modifyUserReplace(USER_BARBOSSA_OID, UserType.F_NAME, task, result, PrismTestUtil.createPolyString(USER_CPTBARBOSSA_USERNAME)); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_CPTBARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + assertAttribute(entry, "title", "Captain"); + + PrismObject user = getUser(USER_BARBOSSA_OID); + String shadowOid = getSingleLinkOid(user); + assertEquals("Shadows have moved", accountBarbossaOid, shadowOid); + PrismObject shadow = getObject(ShadowType.class, shadowOid); + display("Shadow after rename (model)", shadow); + + PrismObject repoShadow = repositoryService.getObject(ShadowType.class, shadowOid, null, result); + display("Shadow after rename (repo)", repoShadow); + + assertNoLdapAccount(USER_BARBOSSA_USERNAME); + } + + // TODO: create account with a group membership // Wait until the lockout of Jack expires, check status @Test @@ -309,7 +649,7 @@ public void test800JackLockoutExpires() throws Exception { assertAccountShadow(shadow, toDn(ACCOUNT_JACK_UID)); assertLockout(shadow, LockoutStatusType.NORMAL); - assertConnectorOperationIncrement(2); + assertConnectorOperationIncrement(1); assertConnectorSimulatedPagingSearchIncrement(0); SearchResultMetadata metadata = shadows.getMetadata(); @@ -318,7 +658,147 @@ public void test800JackLockoutExpires() throws Exception { } } - // TODO: Search all locked accounts. Jack should not be there. + @Test + public void test810SeachLockedAccounts() throws Exception { + final String TEST_NAME = "test810SeachLockedAccounts"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + ObjectQuery query = ObjectQueryUtil.createResourceAndObjectClassQuery(getResourceOid(), getAccountObjectClass(), prismContext); + ObjectQueryUtil.filterAnd(query.getFilter(), + EqualFilter.createEqual(new ItemPath(ShadowType.F_ACTIVATION, ActivationType.F_LOCKOUT_STATUS), getShadowDefinition(), LockoutStatusType.LOCKED)); + + SearchResultList> searchResultList = doSearch(TEST_NAME, query, 0, task, result); + + assertConnectorOperationIncrement(1); + assertConnectorSimulatedPagingSearchIncrement(0); + } + + @Test + public void test820JackLockoutAndUnlock() throws Exception { + final String TEST_NAME = "test820JackLockoutAndUnlock"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + makeBadLoginAttempt(ACCOUNT_JACK_UID); + makeBadLoginAttempt(ACCOUNT_JACK_UID); + makeBadLoginAttempt(ACCOUNT_JACK_UID); + makeBadLoginAttempt(ACCOUNT_JACK_UID); + + jackLockoutTimestamp = System.currentTimeMillis(); + + ObjectQuery query = createUidQuery(ACCOUNT_JACK_UID); + + // precondition + SearchResultList> shadows = modelService.searchObjects(ShadowType.class, query, null, task, result); + result.computeStatus(); + TestUtil.assertSuccess(result); + assertEquals("Unexpected search result: "+shadows, 1, shadows.size()); + PrismObject shadowLocked = shadows.get(0); + display("Locked shadow", shadowLocked); + assertAccountShadow(shadowLocked, toDn(ACCOUNT_JACK_UID)); + assertLockout(shadowLocked, LockoutStatusType.LOCKED); + + rememberConnectorOperationCount(); + rememberConnectorSimulatedPagingSearchCount(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + modifyObjectReplaceProperty(ShadowType.class, shadowLocked.getOid(), + new ItemPath(ShadowType.F_ACTIVATION, ActivationType.F_LOCKOUT_STATUS), task, result, + LockoutStatusType.NORMAL); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + assertConnectorOperationIncrement(1); + assertConnectorSimulatedPagingSearchIncrement(0); + + PrismObject shadowAfter = getObject(ShadowType.class, shadowLocked.getOid()); + display("Shadow after", shadowAfter); + assertAccountShadow(shadowAfter, toDn(ACCOUNT_JACK_UID)); + assertLockout(shadowAfter, LockoutStatusType.NORMAL); + + assertLdapPassword(ACCOUNT_JACK_UID, ACCOUNT_JACK_PASSWORD); + + SearchResultMetadata metadata = shadows.getMetadata(); + if (metadata != null) { + assertFalse(metadata.isPartialResults()); + } + } + + // Let's do this at the very end. + // We need to wait after rename, otherwise the delete fail with: + // NDS error: previous move in progress (-637) + // So ... let's give some time to eDirectory to sort the things out + + @Test + public void test890UnAssignBarbossaPirates() throws Exception { + final String TEST_NAME = "test890UnAssignBarbossaPirates"; + TestUtil.displayTestTile(this, TEST_NAME); + + // TODO: do this on another account. There is a bad interference with rename. + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + unassignRole(USER_BARBOSSA_OID, ROLE_PIRATES_OID, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_CPTBARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + display("Entry", entry); + assertAttribute(entry, "title", "Captain"); + + assertEDirNoGroupMember(entry, GROUP_PIRATES_NAME); + + PrismObject user = getUser(USER_BARBOSSA_OID); + String shadowOid = getSingleLinkOid(user); + assertEquals("Shadows have moved", accountBarbossaOid, shadowOid); + + PrismObject shadow = getObject(ShadowType.class, shadowOid); + IntegrationTestTools.assertNoAssociation(shadow, getAssociationGroupQName(), groupPiratesOid); + + } + + @Test + public void test899UnAssignAccountBarbossa() throws Exception { + final String TEST_NAME = "test899UnAssignAccountBarbossa"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + unassignAccount(USER_BARBOSSA_OID, getResourceOid(), null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + assertNoLdapAccount(USER_BARBOSSA_USERNAME); + assertNoLdapAccount(USER_CPTBARBOSSA_USERNAME); + + PrismObject user = getUser(USER_BARBOSSA_OID); + assertNoLinkedAccount(user); + } // TODO: lock out jack again, explicitly reset the lock, see that he can login @@ -338,4 +818,24 @@ private void makeBadLoginAttempt(String uid) throws LdapException { } } + private void assertEDirGroupMember(String accountUid, String groupName) throws LdapException, IOException, CursorException { + Entry accountEntry = getLdapAccountByUid(accountUid); + assertEDirGroupMember(accountEntry, groupName); + } + + private void assertEDirGroupMember(Entry accountEntry, String groupName) throws LdapException, IOException, CursorException { + Entry groupEntry = getLdapGroupByName(groupName); + assertAttributeContains(groupEntry, getLdapGroupMemberAttribute(), accountEntry.getDn().toString()); + assertAttributeContains(groupEntry, ATTRIBUTE_EQUIVALENT_TO_ME_NAME, accountEntry.getDn().toString()); + assertAttributeContains(accountEntry, ATTRIBUTE_GROUP_MEMBERSHIP_NAME, groupEntry.getDn().toString()); + assertAttributeContains(accountEntry, ATTRIBUTE_SECURITY_EQUALS_NAME, groupEntry.getDn().toString()); + } + + private void assertEDirNoGroupMember(Entry accountEntry, String groupName) throws LdapException, IOException, CursorException { + Entry groupEntry = getLdapGroupByName(groupName); + assertAttributeNotContains(groupEntry, getLdapGroupMemberAttribute(), accountEntry.getDn().toString()); + assertAttributeNotContains(groupEntry, ATTRIBUTE_EQUIVALENT_TO_ME_NAME, accountEntry.getDn().toString()); + assertAttributeNotContains(accountEntry, ATTRIBUTE_GROUP_MEMBERSHIP_NAME, groupEntry.getDn().toString()); + assertAttributeNotContains(accountEntry, ATTRIBUTE_SECURITY_EQUALS_NAME, groupEntry.getDn().toString()); + } } diff --git a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractLdapConnTest.java b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractLdapConnTest.java index 14f7f0599f3..719c290f85b 100644 --- a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractLdapConnTest.java +++ b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractLdapConnTest.java @@ -130,16 +130,6 @@ public abstract class AbstractLdapConnTest extends AbstractLdapTest { private static final Trace LOGGER = TraceManager.getTrace(AbstractLdapConnTest.class); - - protected static final File USER_BARBOSSA_FILE = new File(COMMON_DIR, "user-barbossa.xml"); - protected static final String USER_BARBOSSA_OID = "c0c010c0-d34d-b33f-f00d-111111111112"; - protected static final String USER_BARBOSSA_USERNAME = "barbossa"; - protected static final String USER_BARBOSSA_FULL_NAME = "Hector Barbossa"; - - protected static final File USER_GUYBRUSH_FILE = new File (COMMON_DIR, "user-guybrush.xml"); - protected static final String USER_GUYBRUSH_OID = "c0c010c0-d34d-b33f-f00d-111111111116"; - protected static final String USER_GUYBRUSH_USERNAME = "guybrush"; - protected static final String USER_GUYBRUSH_FULL_NAME = "Guybrush Threepwood"; private static final String USER_LECHUCK_NAME = "lechuck"; private static final String ACCOUNT_LECHUCK_NAME = "lechuck"; @@ -628,14 +618,14 @@ public void test290ModifyUserBarbossaRename() throws Exception { // WHEN TestUtil.displayWhen(TEST_NAME); - modifyUserReplace(USER_BARBOSSA_OID, UserType.F_NAME, task, result, PrismTestUtil.createPolyString("cptbarbossa")); + modifyUserReplace(USER_BARBOSSA_OID, UserType.F_NAME, task, result, PrismTestUtil.createPolyString(USER_CPTBARBOSSA_USERNAME)); // THEN TestUtil.displayThen(TEST_NAME); result.computeStatus(); TestUtil.assertSuccess(result); - Entry entry = assertLdapAccount("cptbarbossa", USER_BARBOSSA_FULL_NAME); + Entry entry = assertLdapAccount(USER_CPTBARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); assertAttribute(entry, "title", "Captain"); PrismObject user = getUser(USER_BARBOSSA_OID); @@ -662,7 +652,7 @@ public void test299UnAssignAccountBarbossa() throws Exception { TestUtil.assertSuccess(result); assertNoLdapAccount(USER_BARBOSSA_USERNAME); - assertNoLdapAccount("cptbarbossa"); + assertNoLdapAccount(USER_CPTBARBOSSA_USERNAME); PrismObject user = getUser(USER_BARBOSSA_OID); assertNoLinkedAccount(user); diff --git a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractLdapTest.java b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractLdapTest.java index 359c1f638b7..0ede9193788 100644 --- a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractLdapTest.java +++ b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractLdapTest.java @@ -30,6 +30,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -51,6 +52,8 @@ import org.testng.annotations.AfterClass; import org.testng.annotations.Test; import org.apache.commons.lang.mutable.MutableInt; +import org.apache.directory.api.ldap.codec.api.BinaryAttributeDetector; +import org.apache.directory.api.ldap.codec.api.DefaultConfigurableBinaryAttributeDetector; import org.apache.directory.api.ldap.model.cursor.CursorException; import org.apache.directory.api.ldap.model.cursor.EntryCursor; import org.apache.directory.api.ldap.model.entry.Attribute; @@ -59,6 +62,7 @@ import org.apache.directory.api.ldap.model.entry.Entry; import org.apache.directory.api.ldap.model.entry.Modification; import org.apache.directory.api.ldap.model.entry.ModificationOperation; +import org.apache.directory.api.ldap.model.entry.Value; import org.apache.directory.api.ldap.model.exception.LdapException; import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException; import org.apache.directory.api.ldap.model.message.BindRequest; @@ -144,6 +148,19 @@ public abstract class AbstractLdapTest extends AbstractModelIntegrationTest { protected static final File ROLE_SUPERUSER_FILE = new File(COMMON_DIR, "role-superuser.xml"); protected static final String ROLE_SUPERUSER_OID = "00000000-0000-0000-0000-000000000004"; + + protected static final File USER_BARBOSSA_FILE = new File(COMMON_DIR, "user-barbossa.xml"); + protected static final String USER_BARBOSSA_OID = "c0c010c0-d34d-b33f-f00d-111111111112"; + protected static final String USER_BARBOSSA_USERNAME = "barbossa"; + protected static final String USER_BARBOSSA_FULL_NAME = "Hector Barbossa"; + + // Barbossa after rename + protected static final String USER_CPTBARBOSSA_USERNAME = "cptbarbossa"; + + protected static final File USER_GUYBRUSH_FILE = new File (COMMON_DIR, "user-guybrush.xml"); + protected static final String USER_GUYBRUSH_OID = "c0c010c0-d34d-b33f-f00d-111111111116"; + protected static final String USER_GUYBRUSH_USERNAME = "guybrush"; + protected static final String USER_GUYBRUSH_FULL_NAME = "Guybrush Threepwood"; private static final String LDAP_ACCOUNT_OBJECTCLASS = "inetOrgPerson"; @@ -161,6 +178,8 @@ public abstract class AbstractLdapTest extends AbstractModelIntegrationTest { private static String stopCommand; protected ObjectClassComplexTypeDefinition accountObjectClassDefinition; + + protected DefaultConfigurableBinaryAttributeDetector binaryAttributeDetector = new DefaultConfigurableBinaryAttributeDetector(); @Override protected void startResources() throws Exception { @@ -211,6 +230,10 @@ protected QName getAccountObjectClass() { return new QName(MidPointConstants.NS_RI, LDAP_ACCOUNT_OBJECTCLASS); } + protected QName getGroupObjectClass() { + return new QName(MidPointConstants.NS_RI, getLdapGroupObjectClass()); + } + protected abstract String getLdapServerHost(); protected abstract int getLdapServerPort(); @@ -415,6 +438,17 @@ protected Entry assertLdapAccount(String uid, String cn) throws LdapException, I return entry; } + protected Entry getLdapGroupByName(String name) throws LdapException, IOException, CursorException { + LdapNetworkConnection connection = ldapConnect(); + List entries = ldapSearch(connection, "(&(cn="+name+")(objectClass="+getLdapGroupObjectClass()+"))"); + ldapDisconnect(connection); + + assertEquals("Unexpected number of entries for group cn="+name+": "+entries, 1, entries.size()); + Entry entry = entries.get(0); + + return entry; + } + protected void assertAttribute(Entry entry, String attrName, String expectedValue) throws LdapInvalidAttributeValueException { String dn = entry.getDn().toString(); Attribute ldapAttribute = entry.get(attrName); @@ -429,6 +463,60 @@ protected void assertAttribute(Entry entry, String attrName, String expectedValu } } + protected void assertAttributeContains(Entry entry, String attrName, String expectedValue) throws LdapInvalidAttributeValueException { + String dn = entry.getDn().toString(); + Attribute ldapAttribute = entry.get(attrName); + if (ldapAttribute == null) { + if (expectedValue == null) { + return; + } else { + AssertJUnit.fail("No attribute "+attrName+" in "+dn+", expected: "+expectedValue); + } + } else { + List vals = new ArrayList<>(); + Iterator> iterator = ldapAttribute.iterator(); + while (iterator.hasNext()) { + Value value = iterator.next(); + if (expectedValue.equals(value.getString())) { + return; + } + vals.add(value.getString()); + } + AssertJUnit.fail("Wrong attribute "+attrName+" in "+dn+" expected to contain value " + expectedValue + " but it has values " + vals); + } + } + + protected void assertAttributeNotContains(Entry entry, String attrName, String expectedValue) throws LdapInvalidAttributeValueException { + String dn = entry.getDn().toString(); + Attribute ldapAttribute = entry.get(attrName); + if (ldapAttribute == null) { + return; + } else { + Iterator> iterator = ldapAttribute.iterator(); + while (iterator.hasNext()) { + Value value = iterator.next(); + if (expectedValue.equals(value.getString())) { + AssertJUnit.fail("Attribute "+attrName+" in "+dn+" contains value " + expectedValue + ", but it should not have it"); + } + } + } + } + + protected Entry getLdapEntry(String dn) throws LdapException, IOException, CursorException { + LdapNetworkConnection connection = ldapConnect(); + Entry entry = getLdapEntry(connection, dn); + ldapDisconnect(connection); + return entry; + } + + protected Entry getLdapEntry(LdapNetworkConnection connection, String dn) throws LdapException, IOException, CursorException { + List entries = ldapSearch(connection, dn, "(objectclass=*)", SearchScope.OBJECT, "*"); + if (entries.isEmpty()) { + return null; + } + return entries.get(0); + } + protected void assertNoLdapAccount(String uid) throws LdapException, IOException, CursorException { LdapNetworkConnection connection = ldapConnect(); List entries = ldapSearch(connection, "(uid="+uid+")"); @@ -502,6 +590,19 @@ protected void deleteLdapEntry(String dn) throws LdapException, IOException { ldapDisconnect(connection); } + /** + * Silent delete. Used to clean up after previous test runs. + */ + protected void cleanupDelete(String dn) throws LdapException, IOException, CursorException { + LdapNetworkConnection connection = ldapConnect(); + Entry entry = getLdapEntry(connection, dn); + if (entry != null) { + connection.delete(dn); + display("Cleaning up LDAP entry: "+dn); + } + ldapDisconnect(connection); + } + protected String toDn(String username) { return "uid="+username+","+getPeopleLdapSuffix(); } @@ -536,6 +637,8 @@ public X509Certificate[] getAcceptedIssuers() { config.setTrustManagers(trustManager); } + config.setBinaryAttributeDetector(binaryAttributeDetector); + LdapNetworkConnection connection = new LdapNetworkConnection(config); boolean connected = connection.connect(); if (!connected) { diff --git a/testing/conntest/src/test/resources/edir/resource-athena.xml b/testing/conntest/src/test/resources/edir/resource-athena.xml index 554c8766448..8b47a8c9d8c 100644 --- a/testing/conntest/src/test/resources/edir/resource-athena.xml +++ b/testing/conntest/src/test/resources/edir/resource-athena.xml @@ -160,6 +160,8 @@ objectToSubject ri:member ri:dn + ri:groupMembership + ri:dn diff --git a/testing/conntest/src/test/resources/edir/role-pirate.xml b/testing/conntest/src/test/resources/edir/role-pirate.xml new file mode 100644 index 00000000000..aef6f83ed19 --- /dev/null +++ b/testing/conntest/src/test/resources/edir/role-pirate.xml @@ -0,0 +1,45 @@ + + + + Pirate + + + + + ri:group + false + + + + + + attributes/ri:cn + Pirates + + + true + + + + + + + \ No newline at end of file diff --git a/testing/story/src/test/resources/science/role-statistics.xml b/testing/story/src/test/resources/science/role-statistics.xml index 7d468d75721..5a5a7d9eca9 100644 --- a/testing/story/src/test/resources/science/role-statistics.xml +++ b/testing/story/src/test/resources/science/role-statistics.xml @@ -1,3 +1,19 @@ + +