/* * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * */ package com.evolveum.midpoint.provisioning.impl.dummy; import com.evolveum.icf.dummy.resource.DummyAccount; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismPropertyValue; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.delta.PropertyDelta; import com.evolveum.midpoint.prism.match.MatchingRule; import com.evolveum.midpoint.prism.util.PrismAsserts; import com.evolveum.midpoint.provisioning.impl.ProvisioningTestUtil; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.internals.InternalMonitor; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ShadowUtil; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.DummyResourceContoller; import com.evolveum.midpoint.test.util.TestUtil; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationProvisioningScriptsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.testng.annotations.Listeners; import org.testng.annotations.Test; import javax.xml.namespace.QName; import java.io.File; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.evolveum.midpoint.test.DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME; import static com.evolveum.midpoint.test.IntegrationTestTools.display; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; /** * The test of Provisioning service on the API level. The test is using dummy * resource for speed and flexibility. * * @author Radovan Semancik * @author Pavol Mederly * */ @ContextConfiguration(locations = "classpath:ctx-provisioning-test-main.xml") @DirtiesContext @Listeners({ com.evolveum.midpoint.tools.testng.AlphabeticalMethodInterceptor.class }) public class TestDummyPrioritiesAndReadReplace extends AbstractDummyTest { private static final Trace LOGGER = TraceManager.getTrace(TestDummyPrioritiesAndReadReplace.class); protected String willIcfUid; public static final File TEST_DIR = new File("src/test/resources/impl/dummy-priorities-read-replace/"); public static final File RESOURCE_DUMMY_FILE = new File(TEST_DIR, "resource-dummy.xml"); @Override protected File getResourceDummyFilename() { return RESOURCE_DUMMY_FILE; } protected MatchingRule<String> getUidMatchingRule() { return null; } @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { super.initSystem(initTask, initResult); InternalMonitor.setTraceConnectorOperation(true); // in order to have schema available here resourceType = provisioningService.getObject(ResourceType.class, RESOURCE_DUMMY_OID, null, taskManager.createTaskInstance(), initResult).asObjectable(); } // copied from TestDummy @Test public void test100AddAccount() throws Exception { final String TEST_NAME = "test100AddAccount"; TestUtil.displayTestTile(TEST_NAME); // GIVEN Task task = taskManager.createTaskInstance(TestDummy.class.getName() + "." + TEST_NAME); OperationResult result = new OperationResult(TestDummy.class.getName() + "." + TEST_NAME); syncServiceMock.reset(); PrismObject<ShadowType> account = prismContext.parseObject(getAccountWillFile()); account.checkConsistence(); display("Adding shadow", account); // WHEN String addedObjectOid = provisioningService.addObject(account, null, null, task, result); // THEN result.computeStatus(); display("add object result", result); TestUtil.assertSuccess("addObject has failed (result)", result); assertEquals(ACCOUNT_WILL_OID, addedObjectOid); account.checkConsistence(); PrismObject<ShadowType> accountRepo = repositoryService.getObject(ShadowType.class, ACCOUNT_WILL_OID, null, result); willIcfUid = getIcfUid(accountRepo); ActivationType activationRepo = accountRepo.asObjectable().getActivation(); if (supportsActivation()) { assertNotNull("No activation in "+accountRepo+" (repo)", activationRepo); assertEquals("Wrong activation enableTimestamp in "+accountRepo+" (repo)", ACCOUNT_WILL_ENABLE_TIMESTAMP, activationRepo.getEnableTimestamp()); } else { assertNull("Activation sneaked in (repo)", activationRepo); } syncServiceMock.assertNotifySuccessOnly(); PrismObject<ShadowType> accountProvisioning = provisioningService.getObject(ShadowType.class, ACCOUNT_WILL_OID, null, task, result); display("Account provisioning", accountProvisioning); ShadowType accountTypeProvisioning = accountProvisioning.asObjectable(); display("account from provisioning", accountTypeProvisioning); PrismAsserts.assertEqualsPolyString("Name not equal", ACCOUNT_WILL_USERNAME, accountTypeProvisioning.getName()); assertEquals("Wrong kind (provisioning)", ShadowKindType.ACCOUNT, accountTypeProvisioning.getKind()); assertAttribute(accountProvisioning, SchemaConstants.ICFS_NAME, ACCOUNT_WILL_USERNAME); assertAttribute(accountProvisioning, getUidMatchingRule(), SchemaConstants.ICFS_UID, willIcfUid); ActivationType activationProvisioning = accountTypeProvisioning.getActivation(); if (supportsActivation()) { assertNotNull("No activation in "+accountProvisioning+" (provisioning)", activationProvisioning); assertEquals("Wrong activation administrativeStatus in "+accountProvisioning+" (provisioning)", ActivationStatusType.ENABLED, activationProvisioning.getAdministrativeStatus()); TestUtil.assertEqualsTimestamp("Wrong activation enableTimestamp in "+accountProvisioning+" (provisioning)", ACCOUNT_WILL_ENABLE_TIMESTAMP, activationProvisioning.getEnableTimestamp()); } else { assertNull("Activation sneaked in (provisioning)", activationProvisioning); } assertNull("The _PASSSWORD_ attribute sneaked into shadow", ShadowUtil.getAttributeValues( accountTypeProvisioning, new QName(SchemaConstants.NS_ICF_SCHEMA, "password"))); // Check if the account was created in the dummy resource DummyAccount dummyAccount = getDummyAccountAssert(ACCOUNT_WILL_USERNAME, willIcfUid); assertNotNull("No dummy account", dummyAccount); assertEquals("Username is wrong", ACCOUNT_WILL_USERNAME, dummyAccount.getName()); assertEquals("Fullname is wrong", "Will Turner", dummyAccount.getAttributeValue("fullname")); assertTrue("The account is not enabled", dummyAccount.isEnabled()); assertEquals("Wrong password", "3lizab3th", dummyAccount.getPassword()); // Check if the shadow is still in the repo (e.g. that the consistency or sync haven't removed it) PrismObject<ShadowType> shadowFromRepo = repositoryService.getObject(ShadowType.class, addedObjectOid, null, result); assertNotNull("Shadow was not created in the repository", shadowFromRepo); display("Repository shadow", shadowFromRepo.debugDump()); ProvisioningTestUtil.checkRepoAccountShadow(shadowFromRepo); checkConsistency(accountProvisioning); //assertSteadyResource(); } @Test public void test123ModifyObjectReplace() throws Exception { final String TEST_NAME = "test123ModifyObjectReplace"; TestUtil.displayTestTile(TEST_NAME); Task task = taskManager.createTaskInstance(TestDummyPrioritiesAndReadReplace.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); syncServiceMock.reset(); // todo add correct definition ObjectDelta<ShadowType> objectDelta = ObjectDelta.createModificationReplaceProperty(ShadowType.class, ACCOUNT_WILL_OID, dummyResourceCtl.getAttributeFullnamePath(), prismContext, "Pirate Master Will Turner"); PropertyDelta weaponDelta = objectDelta.createPropertyModification(dummyResourceCtl.getAttributeWeaponPath()); weaponDelta.setDefinition( getAttributeDefinition(resourceType, ShadowKindType.ACCOUNT, null, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_WEAPON_NAME)); weaponDelta.setValuesToReplace(new PrismPropertyValue<>("Gun")); objectDelta.addModification(weaponDelta); PropertyDelta lootDelta = objectDelta.createPropertyModification(dummyResourceCtl.getAttributeLootPath()); lootDelta.setDefinition( getAttributeDefinition(resourceType, ShadowKindType.ACCOUNT, null, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_LOOT_NAME)); lootDelta.setValuesToReplace(new PrismPropertyValue<>(43)); objectDelta.addModification(lootDelta); PropertyDelta titleDelta = objectDelta.createPropertyModification(dummyResourceCtl.getAttributePath(DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME)); titleDelta.setDefinition( getAttributeDefinition(resourceType, ShadowKindType.ACCOUNT, null, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME)); titleDelta.setValuesToReplace(new PrismPropertyValue<>("Pirate Master")); objectDelta.addModification(titleDelta); display("ObjectDelta", objectDelta); objectDelta.checkConsistence(); // WHEN provisioningService.modifyObject(ShadowType.class, objectDelta.getOid(), objectDelta.getModifications(), new OperationProvisioningScriptsType(), null, task, result); // THEN result.computeStatus(); display("modifyObject result", result); TestUtil.assertSuccess(result); objectDelta.checkConsistence(); assertDummyAccountAttributeValues(ACCOUNT_WILL_USERNAME, willIcfUid, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_FULLNAME_NAME, "Pirate Master Will Turner"); assertDummyAccountAttributeValues(ACCOUNT_WILL_USERNAME, willIcfUid, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME, "Pirate Master"); assertDummyAccountAttributeValues(ACCOUNT_WILL_USERNAME, willIcfUid, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_LOOT_NAME, 43); assertDummyAccountAttributeValues(ACCOUNT_WILL_USERNAME, willIcfUid, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_WEAPON_NAME, "Gun"); // BEWARE: very brittle! List<OperationResult> updatesExecuted = TestUtil.selectSubresults(result, ProvisioningTestUtil.CONNID_CONNECTOR_FACADE_CLASS_NAME + ".update"); assertEquals("Wrong number of updates executed", 3, updatesExecuted.size()); checkAttributesUpdated(updatesExecuted.get(0), "update", DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_WEAPON_NAME); checkAttributesUpdated(updatesExecuted.get(1), "update", DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_LOOT_NAME); checkAttributesUpdated(updatesExecuted.get(2), "update", DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_FULLNAME_NAME, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME); syncServiceMock.assertNotifySuccessOnly(); //assertSteadyResource(); } private void checkAttributesUpdated(OperationResult operationResult, String operation, String... attributeNames) { assertEquals("Wrong operation name", ProvisioningTestUtil.CONNID_CONNECTOR_FACADE_CLASS_NAME + "." + operation, operationResult.getOperation()); Collection<String> updatedAttributes = parseUpdatedAttributes(operationResult.getParams().get("attributes").toString()); assertEquals("Names of updated attributes do not match", new HashSet<>(Arrays.asList(attributeNames)), updatedAttributes); } // From something like this: [Attribute: {Name=fullname, Value=[Pirate Master Will Turner]},Attribute: {Name=title, Value=[Pirate Master]}] // we would like to get ["fullname", "title"] private Collection<String> parseUpdatedAttributes(String attributes) { Pattern pattern = Pattern.compile("Attribute: \\{Name=(\\w+),"); Matcher matcher = pattern.matcher(attributes); Set<String> retval = new HashSet<>(); while (matcher.find()) { retval.add(matcher.group(1)); } return retval; } @Test public void test150ModifyObjectAddDelete() throws Exception { final String TEST_NAME = "test150ModifyObjectAddDelete"; TestUtil.displayTestTile(TEST_NAME); Task task = taskManager.createTaskInstance(TestDummyPrioritiesAndReadReplace.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); syncServiceMock.reset(); // NOT a read replace attribute // todo add correct definition ObjectDelta<ShadowType> objectDelta = ObjectDelta.createModificationReplaceProperty(ShadowType.class, ACCOUNT_WILL_OID, dummyResourceCtl.getAttributeFullnamePath(), prismContext, "Pirate Great Master Will Turner"); // read replace attribute, priority 0 PropertyDelta weaponDelta = objectDelta.createPropertyModification(dummyResourceCtl.getAttributeWeaponPath()); weaponDelta.setDefinition( getAttributeDefinition(resourceType, ShadowKindType.ACCOUNT, null, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_WEAPON_NAME)); weaponDelta.addValuesToAdd(new PrismPropertyValue<>("Sword")); weaponDelta.addValuesToDelete(new PrismPropertyValue<>("GUN")); // case-insensitive treatment should work here objectDelta.addModification(weaponDelta); // read replace attribute, priority 1 PropertyDelta lootDelta = objectDelta.createPropertyModification(dummyResourceCtl.getAttributeLootPath()); lootDelta.setDefinition( getAttributeDefinition(resourceType, ShadowKindType.ACCOUNT, null, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_LOOT_NAME)); lootDelta.addValuesToAdd(new PrismPropertyValue<>(44)); lootDelta.addValuesToDelete(new PrismPropertyValue<>(43)); objectDelta.addModification(lootDelta); // NOT a read-replace attribute PropertyDelta titleDelta = objectDelta.createPropertyModification(dummyResourceCtl.getAttributePath(DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME)); titleDelta.setDefinition( getAttributeDefinition(resourceType, ShadowKindType.ACCOUNT, null, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME)); titleDelta.addValuesToAdd(new PrismPropertyValue<>("Pirate Great Master")); titleDelta.addValuesToDelete(new PrismPropertyValue<>("Pirate Master")); objectDelta.addModification(titleDelta); // read replace attribute PropertyDelta drinkDelta = objectDelta.createPropertyModification(dummyResourceCtl.getAttributePath(DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_DRINK_NAME)); drinkDelta.setDefinition( getAttributeDefinition(resourceType, ShadowKindType.ACCOUNT, null, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_DRINK_NAME)); drinkDelta.addValuesToAdd(new PrismPropertyValue<>("orange juice")); objectDelta.addModification(drinkDelta); display("ObjectDelta", objectDelta); objectDelta.checkConsistence(); // WHEN provisioningService.modifyObject(ShadowType.class, objectDelta.getOid(), objectDelta.getModifications(), new OperationProvisioningScriptsType(), null, task, result); // THEN result.computeStatus(); display("modifyObject result", result); TestUtil.assertSuccess(result); objectDelta.checkConsistence(); assertDummyAccountAttributeValues(ACCOUNT_WILL_USERNAME, willIcfUid, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_FULLNAME_NAME, "Pirate Great Master Will Turner"); assertDummyAccountAttributeValues(ACCOUNT_WILL_USERNAME, willIcfUid, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME, "Pirate Great Master"); assertDummyAccountAttributeValues(ACCOUNT_WILL_USERNAME, willIcfUid, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_LOOT_NAME, 44); assertDummyAccountAttributeValues(ACCOUNT_WILL_USERNAME, willIcfUid, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_WEAPON_NAME, "Sword"); assertDummyAccountAttributeValues(ACCOUNT_WILL_USERNAME, willIcfUid, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_DRINK_NAME, "orange juice"); // BEWARE: very brittle! List<OperationResult> updatesExecuted = TestUtil.selectSubresults(result, ProvisioningTestUtil.CONNID_CONNECTOR_FACADE_CLASS_NAME + ".update", ProvisioningTestUtil.CONNID_CONNECTOR_FACADE_CLASS_NAME + ".addAttributeValues", ProvisioningTestUtil.CONNID_CONNECTOR_FACADE_CLASS_NAME + ".removeAttributeValues"); assertEquals("Wrong number of updates executed", 5, updatesExecuted.size()); checkAttributesUpdated(updatesExecuted.get(0), "update", DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_WEAPON_NAME); // prio 0, read-replace checkAttributesUpdated(updatesExecuted.get(1), "update", DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_LOOT_NAME); // prio 1, read-replace checkAttributesUpdated(updatesExecuted.get(2), "addAttributeValues", DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME); // prio none, not read-replace checkAttributesUpdated(updatesExecuted.get(3), "update", DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_DRINK_NAME, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_FULLNAME_NAME); // prio none, read-replace + real replace checkAttributesUpdated(updatesExecuted.get(4), "removeAttributeValues", DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME); // prio none, not read-replace syncServiceMock.assertNotifySuccessOnly(); //assertSteadyResource(); } }