diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/multi/TestMultiAccount.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/multi/TestMultiAccount.java index 1a10a3f007e..269732987d5 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/multi/TestMultiAccount.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/multi/TestMultiAccount.java @@ -9,6 +9,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.net.ConnectException; +import java.util.Collections; import javax.xml.namespace.QName; import com.evolveum.icf.dummy.resource.ConflictException; @@ -52,6 +53,20 @@ public class TestMultiAccount extends AbstractInitializedModelIntegrationTest { protected static final String RESOURCE_DUMMY_MULTI_GREEN_NAME = "multi-green"; protected static final String RESOURCE_DUMMY_MULTI_GREEN_NAMESPACE = MidPointConstants.NS_RI; + // Clever HR resource. It has one account for every work contract. One of these contracts is the primary one. + protected static final File RESOURCE_DUMMY_CLEVER_HR_FILE = new File(TEST_DIR, "resource-dummy-clever-hr.xml"); + protected static final String RESOURCE_DUMMY_CLEVER_HR_OID = "4b20aab4-99d2-11ea-b0ae-bfae68238f94"; + protected static final String RESOURCE_DUMMY_CLEVER_HR_NAME = "clever-hr"; + protected static final String RESOURCE_DUMMY_CLEVER_HR_NAMESPACE = MidPointConstants.NS_RI; + + private static final String CLEVER_HR_ATTRIBUTE_FIRST_NAME = "firstName"; + private static final String CLEVER_HR_ATTRIBUTE_LAST_NAME = "lastName"; + private static final String CLEVER_HR_ATTRIBUTE_PERSONAL_NUMBER = "personalNumber"; + private static final String CLEVER_HR_ATTRIBUTE_PRIMARY = "primary"; + private static final String CLEVER_HR_ATTRIBUTE_LOCATION = "location"; + private static final String CLEVER_HR_ATTRIBUTE_OU = "ou"; + + // Multi outbound dummy resource, target with multiaccounts. protected static final File RESOURCE_DUMMY_MULTI_OUTBOUND_FILE = new File(TEST_DIR, "resource-dummy-multi-outbound.xml"); protected static final String RESOURCE_DUMMY_MULTI_OUTBOUND_OID = "d4da475e-8539-11ea-8343-dfdb4091c1dc"; @@ -78,6 +93,17 @@ public class TestMultiAccount extends AbstractInitializedModelIntegrationTest { protected static final String ACCOUNT_MAHDI_FULL_NAME = "Mahdi Muad'Dib"; protected static final String ACCOUNT_MAHDI_TITLE = "mahdi"; + protected static final String USER_ODRADE_USERNAME = "odrade"; + protected static final String ACCOUNT_ODRADE_FIRST_NAME = "Darwi"; + protected static final String ACCOUNT_ODRADE_LAST_NAME = "Odrade"; + protected static final String ACCOUNT_ODRADE_PERSONAL_NUMBER = "54321"; + protected static final String ACCOUNT_ODRADE_CONTRACT_NUMBER_APPRENTICE = "A007"; + protected static final String ACCOUNT_ODRADE_CONTRACT_NUMBER_GUARDIAN = "G007"; + protected static final String ACCOUNT_ODRADE_CONTRACT_NUMBER_MOTHER_SUPERIOR = "MS007"; + protected static final String OU_MOTHER_SCHOOL = "Mother School"; + protected static final String OU_MOTHER_SUPERIOR_OFFICE = "Mother Superior Office"; + protected static final String OU_SECURITY = "Security"; + private static final String INTENT_ADMIN = "admin"; private static final String INTENT_ENVOY = "envoy"; @@ -85,6 +111,9 @@ public class TestMultiAccount extends AbstractInitializedModelIntegrationTest { private static final String PLANET_KAITAIN = "Kaitain"; private static final String PLANET_IX = "Ix"; private static final String PLANET_GINAZ = "Ginaz"; + private static final String PLANET_WALLACH_IX = "Wallach IX"; + private static final String PLANET_CHAPTERHOUSE = "Chapterhouse"; + private static final String PLANET_ARRAKIS = "Arrakis"; private String accountPaulOid; private String accountMuaddibOid; @@ -98,6 +127,24 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti initDummyResourcePirate(RESOURCE_DUMMY_MULTI_GREEN_NAME, RESOURCE_DUMMY_MULTI_GREEN_FILE, RESOURCE_DUMMY_MULTI_GREEN_OID, initTask, initResult); + initDummyResource(RESOURCE_DUMMY_CLEVER_HR_NAME, + RESOURCE_DUMMY_CLEVER_HR_FILE, RESOURCE_DUMMY_CLEVER_HR_OID, + controller -> { + controller.addAttrDef(controller.getDummyResource().getAccountObjectClass(), + CLEVER_HR_ATTRIBUTE_FIRST_NAME, String.class, false, false); + controller.addAttrDef(controller.getDummyResource().getAccountObjectClass(), + CLEVER_HR_ATTRIBUTE_LAST_NAME, String.class, true, false); + controller.addAttrDef(controller.getDummyResource().getAccountObjectClass(), + CLEVER_HR_ATTRIBUTE_PERSONAL_NUMBER, String.class, true, false); + controller.addAttrDef(controller.getDummyResource().getAccountObjectClass(), + CLEVER_HR_ATTRIBUTE_PRIMARY, Boolean.class, false, false); + controller.addAttrDef(controller.getDummyResource().getAccountObjectClass(), + CLEVER_HR_ATTRIBUTE_LOCATION, String.class, false, false); + controller.addAttrDef(controller.getDummyResource().getAccountObjectClass(), + CLEVER_HR_ATTRIBUTE_OU, String.class, false, false); + }, + initTask, initResult); + initDummyResourcePirate(RESOURCE_DUMMY_MULTI_OUTBOUND_NAME, RESOURCE_DUMMY_MULTI_OUTBOUND_FILE, RESOURCE_DUMMY_MULTI_OUTBOUND_OID, initTask, initResult); } @@ -587,6 +634,209 @@ public void test350IdahoAssignOutboundMultiaccount() throws Exception { assertUsers(getNumberOfUsers() + 2); } + /** + * Mostly just sanity. Make sure that "empty" import works and that the clever HR + * resource configuration is sane. + * MID-6080 + */ + @Test + public void test400ImportAccountsFromCleverHr() throws Exception { + // GIVEN + Task task = getTestTask(); + OperationResult result = task.getResult(); + + // Preconditions + assertUsers(getNumberOfUsers() + 2); + + // WHEN + when(); + importCleverHrAccounts(task, result); + + // THEN + then(); + + // No accounts on HR resource yet. No users should be created. + assertUsers(getNumberOfUsers() + 2); + } + + /** + * Import the first account. Nothing special here yet. + */ + @Test + public void test410ImportOdradeApprentice() throws Exception { + // GIVEN + Task task = getTestTask(); + OperationResult result = task.getResult(); + + DummyAccount account = new DummyAccount(ACCOUNT_ODRADE_CONTRACT_NUMBER_APPRENTICE); + account.setEnabled(true); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_FIRST_NAME, ACCOUNT_ODRADE_FIRST_NAME); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_LAST_NAME, ACCOUNT_ODRADE_LAST_NAME); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_PERSONAL_NUMBER, ACCOUNT_ODRADE_PERSONAL_NUMBER); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_LOCATION, PLANET_WALLACH_IX); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_PRIMARY, Collections.singleton(Boolean.TRUE)); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_OU, OU_MOTHER_SCHOOL); + getDummyResource(RESOURCE_DUMMY_CLEVER_HR_NAME).addAccount(account); + + // Preconditions + assertUsers(getNumberOfUsers() + 2); + + // WHEN + when(); + importCleverHrAccounts(task, result); + + // THEN + then(); + + assertUserAfterByUsername(USER_ODRADE_USERNAME) + .displayWithProjections() + .assertGivenName(ACCOUNT_ODRADE_FIRST_NAME) + .assertFamilyName(ACCOUNT_ODRADE_LAST_NAME) + .assertEmployeeNumber(ACCOUNT_ODRADE_CONTRACT_NUMBER_APPRENTICE) + .assertLocality(PLANET_WALLACH_IX) + .assertOrganizationalUnits(OU_MOTHER_SCHOOL) + .singleLink() + .resolveTarget() + .assertKind(ShadowKindType.ACCOUNT) + .assertIntent(SchemaConstants.INTENT_DEFAULT) + .assertTag(ACCOUNT_ODRADE_CONTRACT_NUMBER_APPRENTICE); + + assertUsers(getNumberOfUsers() + 3); + + } + + /** + * Import the second account. It should correlate to Odrade user as well. + * However, this contract is NOT primary. Therefore employee number and location should NOT be changed. + * MID-6080 + */ + @Test + public void test420ImportOdradeGuardian() throws Exception { + // GIVEN + Task task = getTestTask(); + OperationResult result = task.getResult(); + + DummyAccount account = new DummyAccount(ACCOUNT_ODRADE_CONTRACT_NUMBER_GUARDIAN); + account.setEnabled(true); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_FIRST_NAME, ACCOUNT_ODRADE_FIRST_NAME); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_LAST_NAME, ACCOUNT_ODRADE_LAST_NAME); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_PERSONAL_NUMBER, ACCOUNT_ODRADE_PERSONAL_NUMBER); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_LOCATION, PLANET_ARRAKIS); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_PRIMARY, Collections.singleton(Boolean.FALSE)); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_OU, OU_SECURITY); + getDummyResource(RESOURCE_DUMMY_CLEVER_HR_NAME).addAccount(account); + + // Preconditions + assertUsers(getNumberOfUsers() + 3); + + // WHEN + when(); + importCleverHrAccounts(task, result); + + // THEN + then(); + + assertUserAfterByUsername(USER_ODRADE_USERNAME) + .displayWithProjections() + .assertGivenName(ACCOUNT_ODRADE_FIRST_NAME) + .assertFamilyName(ACCOUNT_ODRADE_LAST_NAME) + .assertLocality(PLANET_WALLACH_IX) + .assertEmployeeNumber(ACCOUNT_ODRADE_CONTRACT_NUMBER_APPRENTICE) + .assertOrganizationalUnits(OU_MOTHER_SCHOOL, OU_SECURITY) + .links() + .assertLinks(2) + .by() + .tag(ACCOUNT_ODRADE_CONTRACT_NUMBER_APPRENTICE) + .find() + .resolveTarget() + .assertKind(ShadowKindType.ACCOUNT) + .assertIntent(SchemaConstants.INTENT_DEFAULT) + .end() + .end() + .by() + .tag(ACCOUNT_ODRADE_CONTRACT_NUMBER_GUARDIAN) + .find() + .resolveTarget() + .assertKind(ShadowKindType.ACCOUNT) + .assertIntent(SchemaConstants.INTENT_DEFAULT) + .end() + .end(); + + assertUsers(getNumberOfUsers() + 3); + + } + + /** + * Promote Odrade to Mother Superior. The primary contract is changed in this case. + * User object should reflect data from the new primary contract. + */ + @Test + public void test430ImportOdradeMotherSuperior() throws Exception { + // GIVEN + Task task = getTestTask(); + OperationResult result = task.getResult(); + + getDummyResource(RESOURCE_DUMMY_CLEVER_HR_NAME) + .getAccountByUsername(ACCOUNT_ODRADE_CONTRACT_NUMBER_APPRENTICE) + .replaceAttributeValue(CLEVER_HR_ATTRIBUTE_PRIMARY, Boolean.FALSE); + + DummyAccount account = new DummyAccount(ACCOUNT_ODRADE_CONTRACT_NUMBER_MOTHER_SUPERIOR); + account.setEnabled(true); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_FIRST_NAME, ACCOUNT_ODRADE_FIRST_NAME); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_LAST_NAME, ACCOUNT_ODRADE_LAST_NAME); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_PERSONAL_NUMBER, ACCOUNT_ODRADE_PERSONAL_NUMBER); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_LOCATION, PLANET_CHAPTERHOUSE); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_PRIMARY, Collections.singleton(Boolean.TRUE)); + account.addAttributeValues(CLEVER_HR_ATTRIBUTE_OU, OU_MOTHER_SUPERIOR_OFFICE); + getDummyResource(RESOURCE_DUMMY_CLEVER_HR_NAME).addAccount(account); + + // Preconditions + assertUsers(getNumberOfUsers() + 3); + + // WHEN + when(); + importCleverHrAccounts(task, result); + + // THEN + then(); + + assertUserAfterByUsername(USER_ODRADE_USERNAME) + .displayWithProjections() + .assertGivenName(ACCOUNT_ODRADE_FIRST_NAME) + .assertFamilyName(ACCOUNT_ODRADE_LAST_NAME) + .assertLocality(PLANET_CHAPTERHOUSE) + .assertEmployeeNumber(ACCOUNT_ODRADE_CONTRACT_NUMBER_MOTHER_SUPERIOR) + .assertOrganizationalUnits(OU_MOTHER_SUPERIOR_OFFICE, OU_MOTHER_SCHOOL, OU_SECURITY) + .links() + .assertLinks(3) + .by() + .tag(ACCOUNT_ODRADE_CONTRACT_NUMBER_APPRENTICE) + .find() + .resolveTarget() + .assertKind(ShadowKindType.ACCOUNT) + .assertIntent(SchemaConstants.INTENT_DEFAULT) + .end() + .end() + .by() + .tag(ACCOUNT_ODRADE_CONTRACT_NUMBER_GUARDIAN) + .find() + .resolveTarget() + .assertKind(ShadowKindType.ACCOUNT) + .assertIntent(SchemaConstants.INTENT_DEFAULT) + .end() + .end() + .by() + .tag(ACCOUNT_ODRADE_CONTRACT_NUMBER_MOTHER_SUPERIOR) + .find() + .resolveTarget() + .assertKind(ShadowKindType.ACCOUNT) + .assertIntent(SchemaConstants.INTENT_DEFAULT) + .end() + .end(); + + assertUsers(getNumberOfUsers() + 3); + } + private void assertEnvoyAccounts(String userOid, String username, String... planets) throws SchemaException, ObjectNotFoundException, ConfigurationException, CommunicationException, SecurityViolationException, ExpressionEvaluationException, InterruptedException, FileNotFoundException, ConnectException, SchemaViolationException, ConflictException { UserAsserter asserter = assertUserAfter(userOid) .displayWithProjections() @@ -635,6 +885,13 @@ private void importMultiGreenAccounts(Task task, OperationResult result) throws waitForTaskFinish(task, true, 40000); } + private void importCleverHrAccounts(Task task, OperationResult result) throws Exception { + modelService.importFromResource(RESOURCE_DUMMY_CLEVER_HR_OID, new QName(getDummyResourceController(RESOURCE_DUMMY_CLEVER_HR_NAME).getNamespace(), SchemaConstants.ACCOUNT_OBJECT_CLASS_LOCAL_NAME), task, result); + OperationResult subresult = result.getLastSubresult(); + TestUtil.assertInProgress("importAccountsFromResource result", subresult); + waitForTaskFinish(task, true, 40000); + } + private String getEnvoy(String username, String planet) { return "envoy-" + username + "-" + planet.toLowerCase(); } diff --git a/model/model-intest/src/test/resources/logback-test.xml b/model/model-intest/src/test/resources/logback-test.xml index bc6f7750312..5b36fd5763e 100644 --- a/model/model-intest/src/test/resources/logback-test.xml +++ b/model/model-intest/src/test/resources/logback-test.xml @@ -65,7 +65,7 @@ - + @@ -105,7 +105,8 @@ - + + diff --git a/model/model-intest/src/test/resources/multi-account/resource-dummy-clever-hr.xml b/model/model-intest/src/test/resources/multi-account/resource-dummy-clever-hr.xml new file mode 100644 index 00000000000..f954c4a0ec7 --- /dev/null +++ b/model/model-intest/src/test/resources/multi-account/resource-dummy-clever-hr.xml @@ -0,0 +1,304 @@ + + + + + + + + + Dummy Resource Clever HR + + + + + connectorType + com.evolveum.icf.dummy.connector.DummyConnector + + + connectorVersion + 2.0 + + + + + + + + clever-hr + true + + + + + + + true + ri:AccountObjectClass + + unbounded + + + + + + + + icfs:name + Contract number + + Employee number is number of the primary employment contract + + + + employeeNumber + + + + + + + + + + + + + ri:firstName + + + $focus/givenName + + + + + + + + + + + + + ri:lastName + + + $focus/familyName + + + + + + + + + + + + + true + + + + $focus/name + + + + + + + + + + + + + ri:personalNumber + + weak + + $focus/extension/personalNumber + + + + + ri:location + + MMMlocation + true + strong + + locality + + + + + + + + true + + + + + + ri:ou + + + Normal mapping to a multi-value user attribute. + Therefore organizationalUnit should have values from all the accounts. + + + organizationalUnit + + + + + + 5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + weak + + + + + + + + + + + + + none + + + + + default account + true + + + extension/personalNumber + + $projection/attributes/personalNumber + + + + + linked + true + + + deleted + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#deleteFocus + + + + unlinked + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#link + + + + unmatched + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#addFocus + + + + + + diff --git a/model/model-intest/src/test/resources/multi-account/resource-dummy-multi-green.xml b/model/model-intest/src/test/resources/multi-account/resource-dummy-multi-green.xml index 1276c241da6..e1bc4ce27bf 100644 --- a/model/model-intest/src/test/resources/multi-account/resource-dummy-multi-green.xml +++ b/model/model-intest/src/test/resources/multi-account/resource-dummy-multi-green.xml @@ -74,7 +74,7 @@ weak - $c:user/c:name + name diff --git a/model/model-intest/src/test/resources/schema/enterprise.xsd b/model/model-intest/src/test/resources/schema/enterprise.xsd new file mode 100644 index 00000000000..14f982ad5c5 --- /dev/null +++ b/model/model-intest/src/test/resources/schema/enterprise.xsd @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + true + + + + + + +