/* * 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.model.intest.negative; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import static com.evolveum.midpoint.test.IntegrationTestTools.assertNoRepoCache; import static com.evolveum.midpoint.test.IntegrationTestTools.display; import static org.testng.AssertJUnit.assertNotNull; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import javax.xml.namespace.QName; import com.evolveum.midpoint.common.refinery.RefinedResourceSchemaImpl; import org.apache.commons.lang.StringUtils; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; import org.testng.AssertJUnit; import org.testng.annotations.Test; import com.evolveum.icf.dummy.resource.BreakMode; import com.evolveum.midpoint.common.refinery.RefinedAttributeDefinition; import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; import com.evolveum.midpoint.common.refinery.RefinedResourceSchema; import com.evolveum.midpoint.model.impl.lens.LensContext; import com.evolveum.midpoint.model.intest.AbstractInitializedModelIntegrationTest; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.ChangeType; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.schema.ObjectDeltaOperation; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.result.OperationResultStatus; import com.evolveum.midpoint.schema.util.MiscSchemaUtil; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.util.TestUtil; import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; 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.AssignmentPolicyEnforcementType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType; 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 com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; /** * Tests the model service contract by using a broken CSV resource. Tests for negative test cases, mostly * correct handling of connector exceptions. * * @author semancik * */ @ContextConfiguration(locations = {"classpath:ctx-model-intest-test-main.xml"}) @DirtiesContext(classMode = ClassMode.AFTER_CLASS) public class TestAssignmentErrors extends AbstractInitializedModelIntegrationTest { private static final String TEST_DIR = "src/test/resources/negative"; private static final String TEST_TARGET_DIR = "target/test/negative"; private static final String USER_LEMONHEAD_NAME = "lemonhead"; private static final String USER_LEMONHEAD_FULLNAME = "Lemonhead"; private static final String USER_SHARPTOOTH_NAME = "sharptooth"; private static final String USER_SHARPTOOTH_FULLNAME = "Sharptooth"; private static final String USER_SHARPTOOTH_PASSWORD_1_CLEAR = "SHARPyourT33TH"; private static final String USER_SHARPTOOTH_PASSWORD_2_CLEAR = "L00SEyourT33TH"; private static final String USER_SHARPTOOTH_PASSWORD_3_CLEAR = "HAV3noT33TH"; private static final String USER_REDSKULL_NAME = "redskull"; private static final String USER_REDSKULL_FULLNAME = "Red Skull"; private static final String USER_AFET_NAME = "afet"; private static final String USER_AFET_FULLNAME = "Alfredo Fettucini"; private static final String USER_BFET_NAME = "bfet"; private static final String USER_BFET_FULLNAME = "Bill Fettucini"; private static final String USER_CFET_NAME = "cfet"; private static final String USER_CFET_FULLNAME = "Carlos Fettucini"; protected static final Trace LOGGER = TraceManager.getTrace(TestAssignmentErrors.class); private PrismObject<ResourceType> resource; private String userLemonheadOid; private String userSharptoothOid; @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { super.initSystem(initTask, initResult); } @Test public void test010RefinedSchemaWhite() throws Exception { final String TEST_NAME = "test010RefinedSchemaWhite"; TestUtil.displayTestTile(TEST_NAME); // GIVEN // WHEN PrismObject<ResourceType> resourceWhite = getObject(ResourceType.class, RESOURCE_DUMMY_WHITE_OID); RefinedResourceSchema refinedSchema = RefinedResourceSchemaImpl.getRefinedSchema(resourceWhite, prismContext); display("Refined schema", refinedSchema); RefinedObjectClassDefinition accountDef = refinedSchema.getDefaultRefinedDefinition(ShadowKindType.ACCOUNT); assertNotNull("Account definition is missing", accountDef); assertNotNull("Null identifiers in account", accountDef.getPrimaryIdentifiers()); assertFalse("Empty identifiers in account", accountDef.getPrimaryIdentifiers().isEmpty()); assertNotNull("Null secondary identifiers in account", accountDef.getSecondaryIdentifiers()); assertFalse("Empty secondary identifiers in account", accountDef.getSecondaryIdentifiers().isEmpty()); assertNotNull("No naming attribute in account", accountDef.getNamingAttribute()); assertFalse("No nativeObjectClass in account", StringUtils.isEmpty(accountDef.getNativeObjectClass())); assertEquals("Unexpected kind in account definition", ShadowKindType.ACCOUNT, accountDef.getKind()); assertTrue("Account definition in not default", accountDef.isDefaultInAKind()); assertEquals("Wrong intent in account definition", SchemaConstants.INTENT_DEFAULT, accountDef.getIntent()); assertFalse("Account definition is deprecated", accountDef.isDeprecated()); assertFalse("Account definition in auxiliary", accountDef.isAuxiliary()); RefinedAttributeDefinition uidDef = accountDef.findAttributeDefinition(SchemaConstants.ICFS_UID); assertEquals(1, uidDef.getMaxOccurs()); assertEquals(0, uidDef.getMinOccurs()); assertFalse("No UID display name", StringUtils.isBlank(uidDef.getDisplayName())); assertFalse("UID has create", uidDef.canAdd()); assertFalse("UID has update", uidDef.canModify()); assertTrue("No UID read", uidDef.canRead()); assertTrue("UID definition not in identifiers", accountDef.getPrimaryIdentifiers().contains(uidDef)); RefinedAttributeDefinition nameDef = accountDef.findAttributeDefinition(SchemaConstants.ICFS_NAME); assertEquals(1, nameDef.getMaxOccurs()); assertEquals(1, nameDef.getMinOccurs()); assertFalse("No NAME displayName", StringUtils.isBlank(nameDef.getDisplayName())); assertTrue("No NAME create", nameDef.canAdd()); assertTrue("No NAME update", nameDef.canModify()); assertTrue("No NAME read", nameDef.canRead()); assertTrue("NAME definition not in identifiers", accountDef.getSecondaryIdentifiers().contains(nameDef)); RefinedAttributeDefinition fullnameDef = accountDef.findAttributeDefinition("fullname"); assertNotNull("No definition for fullname", fullnameDef); assertEquals(1, fullnameDef.getMaxOccurs()); assertEquals(1, fullnameDef.getMinOccurs()); assertTrue("No fullname create", fullnameDef.canAdd()); assertTrue("No fullname update", fullnameDef.canModify()); assertTrue("No fullname read", fullnameDef.canRead()); assertNull("The _PASSSWORD_ attribute sneaked into schema", accountDef.findAttributeDefinition(new QName(SchemaConstants.NS_ICF_SCHEMA, "password"))); } /** * The "white" resource has no outbound mapping and there is also no mapping in the assignment. Therefore * this results in account without any attributes. It should fail. */ @Test public void test100UserJackAssignBlankAccount() throws Exception { final String TEST_NAME = "test100UserJackAssignBlankAccount"; TestUtil.displayTestTile(this, TEST_NAME); // GIVEN Task task = taskManager.createTaskInstance(TestAssignmentErrors.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); dummyAuditService.clear(); Collection<ObjectDelta<? extends ObjectType>> deltas = new ArrayList<ObjectDelta<? extends ObjectType>>(); ObjectDelta<UserType> accountAssignmentUserDelta = createAccountAssignmentUserDelta(USER_JACK_OID, RESOURCE_DUMMY_WHITE_OID, null, true); deltas.add(accountAssignmentUserDelta); dummyAuditService.clear(); // WHEN //not expected that it fails, insted the fatal error in the result is excpected modelService.executeChanges(deltas, null, task, result); result.computeStatus(); display(result); // This has to be a partial error as some changes were executed (user) and others were not (account) TestUtil.assertPartialError(result); // Check audit display("Audit", dummyAuditService); dummyAuditService.assertSimpleRecordSanity(); dummyAuditService.assertRecords(2); dummyAuditService.assertAnyRequestDeltas(); dummyAuditService.assertTarget(USER_JACK_OID); dummyAuditService.assertExecutionOutcome(OperationResultStatus.PARTIAL_ERROR); dummyAuditService.assertExecutionMessage(); } /** * The "while" resource has no outbound mapping and there is also no mapping in the assignment. Therefore * this results in account without any attributes. It should fail. */ @Test public void test101AddUserCharlesAssignBlankAccount() throws Exception { final String TEST_NAME = "test101AddUserCharlesAssignBlankAccount"; TestUtil.displayTestTile(this, TEST_NAME); // GIVEN Task task = taskManager.createTaskInstance(TestAssignmentErrors.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); dummyAuditService.clear(); PrismObject<UserType> userCharles = createUser("charles", "Charles L. Charles"); fillinUserAssignmentAccountConstruction(userCharles, RESOURCE_DUMMY_WHITE_OID); ObjectDelta<UserType> userDelta = ObjectDelta.createAddDelta(userCharles); Collection<ObjectDelta<? extends ObjectType>> deltas = MiscSchemaUtil.createCollection(userDelta); // WHEN //we do not expect this to throw an exception. instead the fatal error in the result is excpected modelService.executeChanges(deltas, null, task, result); result.computeStatus(); TestUtil.assertFailure(result); // Even though the operation failed the addition of a user should be successful. Let's check if user was really added. String userOid = userDelta.getOid(); assertNotNull("No user OID in delta after operation", userOid); PrismObject<UserType> userAfter = getUser(userOid); assertUser(userAfter, userOid, "charles", "Charles L. Charles", null, null, null); // Check audit display("Audit", dummyAuditService); dummyAuditService.assertSimpleRecordSanity(); dummyAuditService.assertRecords(2); dummyAuditService.assertAnyRequestDeltas(); dummyAuditService.assertExecutionOutcome(OperationResultStatus.PARTIAL_ERROR); dummyAuditService.assertExecutionMessage(); } @Test public void test200UserLemonheadAssignAccountBrokenNetwork() throws Exception { final String TEST_NAME = "test200UserLemonheadAssignAccountBrokenNetwork"; TestUtil.displayTestTile(this, TEST_NAME); // GIVEN Task task = taskManager.createTaskInstance(TestAssignmentErrors.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); PrismObject<UserType> user = createUser(USER_LEMONHEAD_NAME, USER_LEMONHEAD_FULLNAME); addObject(user); userLemonheadOid = user.getOid(); Collection<ObjectDelta<? extends ObjectType>> deltas = new ArrayList<ObjectDelta<? extends ObjectType>>(); ObjectDelta<UserType> accountAssignmentUserDelta = createAccountAssignmentUserDelta(user.getOid(), RESOURCE_DUMMY_OID, null, true); deltas.add(accountAssignmentUserDelta); getDummyResource().setBreakMode(BreakMode.NETWORK); dummyAuditService.clear(); // WHEN //not expected that it fails, instead the error in the result is expected modelService.executeChanges(deltas, null, task, result); result.computeStatus(); display(result); // This has to be a partial error as some changes were executed (user) and others were not (account) TestUtil.assertResultStatus(result, OperationResultStatus.HANDLED_ERROR); // Check audit display("Audit", dummyAuditService); dummyAuditService.assertSimpleRecordSanity(); dummyAuditService.assertRecords(2); dummyAuditService.assertAnyRequestDeltas(); dummyAuditService.assertTarget(user.getOid()); dummyAuditService.assertExecutionOutcome(OperationResultStatus.HANDLED_ERROR); dummyAuditService.assertExecutionMessage(); } // PARTIAL_ERROR: Unable to get object from the resource. Probably it has not been created yet because of previous unavailability of the resource. // TODO: timeout or explicit retry // @Test // public void test205UserLemonheadRecovery() throws Exception { // final String TEST_NAME = "test205UserLemonheadRecovery"; // TestUtil.displayTestTile(this, TEST_NAME); // // // GIVEN // Task task = taskManager.createTaskInstance(TestAssignmentErrors.class.getName() + "." + TEST_NAME); // OperationResult result = task.getResult(); // assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); // // dummyResource.setBreakMode(BreakMode.NONE); // dummyAuditService.clear(); // // // WHEN // //not expected that it fails, instead the error in the result is expected // modelService.recompute(UserType.class, userLemonheadOid, task, result); // // result.computeStatus(); // // display(result); // // This has to be a partial error as some changes were executed (user) and others were not (account) // TestUtil.assertSuccess(result); // // // Check audit // display("Audit", dummyAuditService); // dummyAuditService.assertSimpleRecordSanity(); // dummyAuditService.assertRecords(2); // dummyAuditService.assertAnyRequestDeltas(); // dummyAuditService.assertTarget(userLemonheadOid); // dummyAuditService.assertExecutionOutcome(OperationResultStatus.HANDLED_ERROR); // dummyAuditService.assertExecutionMessage(); // // } @Test public void test210UserSharptoothAssignAccountBrokenGeneric() throws Exception { final String TEST_NAME = "test210UserSharptoothAssignAccountBrokenGeneric"; TestUtil.displayTestTile(this, TEST_NAME); // GIVEN Task task = taskManager.createTaskInstance(TestAssignmentErrors.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); PrismObject<UserType> user = createUser(USER_SHARPTOOTH_NAME, USER_SHARPTOOTH_FULLNAME); CredentialsType credentialsType = new CredentialsType(); PasswordType passwordType = new PasswordType(); ProtectedStringType passwordPs = new ProtectedStringType(); passwordPs.setClearValue(USER_SHARPTOOTH_PASSWORD_1_CLEAR); passwordType.setValue(passwordPs); credentialsType.setPassword(passwordType); user.asObjectable().setCredentials(credentialsType); addObject(user); userSharptoothOid = user.getOid(); Collection<ObjectDelta<? extends ObjectType>> deltas = new ArrayList<ObjectDelta<? extends ObjectType>>(); ObjectDelta<UserType> accountAssignmentUserDelta = createAccountAssignmentUserDelta(user.getOid(), RESOURCE_DUMMY_OID, null, true); deltas.add(accountAssignmentUserDelta); getDummyResource().setBreakMode(BreakMode.GENERIC); dummyAuditService.clear(); // WHEN TestUtil.displayWhen(TEST_NAME); //not expected that it fails, instead the error in the result is expected modelService.executeChanges(deltas, null, task, result); // THEN TestUtil.displayThen(TEST_NAME); result.computeStatus(); display(result); // This has to be a partial error as some changes were executed (user) and others were not (account) TestUtil.assertPartialError(result); // Check audit display("Audit", dummyAuditService); dummyAuditService.assertSimpleRecordSanity(); dummyAuditService.assertRecords(2); dummyAuditService.assertAnyRequestDeltas(); dummyAuditService.assertExecutionDeltas(2); dummyAuditService.assertHasDelta(ChangeType.MODIFY, UserType.class); dummyAuditService.assertHasDelta(ChangeType.ADD, ShadowType.class, OperationResultStatus.FATAL_ERROR); dummyAuditService.assertTarget(user.getOid()); dummyAuditService.assertExecutionOutcome(OperationResultStatus.PARTIAL_ERROR); dummyAuditService.assertExecutionMessage(); LensContext<UserType> lastLensContext = lensDebugListener.getLastLensContext(); Collection<ObjectDeltaOperation<? extends ObjectType>> executedDeltas = lastLensContext.getExecutedDeltas(); display("Executed deltas", executedDeltas); assertEquals("Unexpected number of execution deltas in context", 2, executedDeltas.size()); Iterator<ObjectDeltaOperation<? extends ObjectType>> i = executedDeltas.iterator(); ObjectDeltaOperation<? extends ObjectType> deltaop1 = i.next(); assertEquals("Unexpected result of first executed deltas", OperationResultStatus.SUCCESS, deltaop1.getExecutionResult().getStatus()); ObjectDeltaOperation<? extends ObjectType> deltaop2 = i.next(); assertEquals("Unexpected result of second executed deltas", OperationResultStatus.FATAL_ERROR, deltaop2.getExecutionResult().getStatus()); } /** * User has assigned account. We recover the resource (clear break mode) and recompute. * The account should be created. */ @Test public void test212UserSharptoothAssignAccountRecovery() throws Exception { final String TEST_NAME = "test212UserSharptoothAssignAccountRecovery"; TestUtil.displayTestTile(this, TEST_NAME); // GIVEN Task task = taskManager.createTaskInstance(TestAssignmentErrors.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); getDummyResource().resetBreakMode(); dummyAuditService.clear(); // WHEN TestUtil.displayWhen(TEST_NAME); recomputeUser(userSharptoothOid, task, result); // THEN TestUtil.displayThen(TEST_NAME); result.computeStatus(); TestUtil.assertSuccess(result); assertDummyAccount(null, USER_SHARPTOOTH_NAME, USER_SHARPTOOTH_FULLNAME, true); assertDummyPassword(null, USER_SHARPTOOTH_NAME, USER_SHARPTOOTH_PASSWORD_1_CLEAR); } /** * Change user password. But there is error on the resource. * User password should be changed, account password unchanged and there * should be handled error in the result. Delta is remembered in the shadow. * MID-3569 */ @Test public void test214UserSharptoothChangePasswordNetworkError() throws Exception { testUserSharptoothChangePasswordError("test214UserSharptoothChangePasswordNetworkError", BreakMode.NETWORK, USER_SHARPTOOTH_PASSWORD_1_CLEAR, USER_SHARPTOOTH_PASSWORD_2_CLEAR, OperationResultStatus.HANDLED_ERROR); } /** * Change user password. But there is error on the resource. * User password should be changed, account password unchanged and there * should be partial error in the result. We have no idea what's going on here. * MID-3569 */ @Test public void test215UserSharptoothChangePasswordGenericError() throws Exception { testUserSharptoothChangePasswordError("test215UserSharptoothChangePasswordGenericError", BreakMode.GENERIC, USER_SHARPTOOTH_PASSWORD_1_CLEAR, USER_SHARPTOOTH_PASSWORD_3_CLEAR, OperationResultStatus.PARTIAL_ERROR); } public void testUserSharptoothChangePasswordError(final String TEST_NAME, BreakMode breakMode, String oldPassword, String newPassword, OperationResultStatus expectedResultStatus) throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN Task task = taskManager.createTaskInstance(TestAssignmentErrors.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); getDummyResource().setBreakMode(breakMode); dummyAuditService.clear(); // WHEN TestUtil.displayWhen(TEST_NAME); modifyUserChangePassword(userSharptoothOid, newPassword, task, result); // THEN TestUtil.displayThen(TEST_NAME); result.computeStatus(); TestUtil.assertStatus(result, expectedResultStatus); getDummyResource().resetBreakMode(); PrismObject<UserType> userAfter = getUser(userSharptoothOid); display("User afte", userAfter); assertEncryptedUserPassword(userAfter, newPassword); assertDummyAccount(null, USER_SHARPTOOTH_NAME, USER_SHARPTOOTH_FULLNAME, true); assertDummyPassword(null, USER_SHARPTOOTH_NAME, oldPassword); } /** * Assign account to user, delete the account shadow (not the account), recompute the user. * We expect that the shadow will be re-created and re-linked. * * This is tried on the default dummy resource where synchronization is enabled. */ @Test public void test220UserAssignAccountDeletedShadowRecomputeSync() throws Exception { final String TEST_NAME = "test220UserAssignAccountDeletedShadowRecomputeSync"; TestUtil.displayTestTile(this, TEST_NAME); //GIVEN PrismObject<UserType> user = setupUserAssignAccountDeletedShadowRecompute(TEST_NAME, RESOURCE_DUMMY_OID, null, USER_AFET_NAME, USER_AFET_FULLNAME); String shadowOidBefore = getSingleLinkOid(user); Task task = taskManager.createTaskInstance(TestAssignmentErrors.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); // WHEN recomputeUser(user.getOid(), task, result); // THEN result.computeStatus(); display("Recompute result", result); TestUtil.assertSuccess(result,2); user = getUser(user.getOid()); display("User after", user); String shadowOidAfter = getSingleLinkOid(user); display("Shadow OID after", shadowOidAfter); PrismObject<ShadowType> shadowAfter = repositoryService.getObject(ShadowType.class, shadowOidAfter, null, result); display("Shadow after", shadowAfter); assertFalse("New and old shadow OIDs are the same", shadowOidBefore.equals(shadowOidAfter)); // ... and again ... task = taskManager.createTaskInstance(TestAssignmentErrors.class.getName() + "." + TEST_NAME); result = task.getResult(); // WHEN recomputeUser(user.getOid(), task, result); // THEN result.computeStatus(); display("Recompute result", result); TestUtil.assertSuccess(result,2); user = getUser(user.getOid()); display("User after", user); String shadowOidAfterAfter = getSingleLinkOid(user); display("Shadow OID after the second time", shadowOidAfterAfter); assertEquals("The shadow OIDs has changed after second recompute", shadowOidAfter, shadowOidAfterAfter); } /** * Assign account to user, delete the account shadow (not the account), recompute the user. * We expect ObjectAlreadyExistsException. * * This is tried on the red dummy resource where there is no synchronization. */ @Test public void test222UserAssignAccountDeletedShadowRecomputeNoSync() throws Exception { final String TEST_NAME = "test222UserAssignAccountDeletedShadowRecomputeNoSync"; TestUtil.displayTestTile(this, TEST_NAME); //GIVEN PrismObject<UserType> user = setupUserAssignAccountDeletedShadowRecompute(TEST_NAME, RESOURCE_DUMMY_RED_OID, RESOURCE_DUMMY_RED_NAME, USER_BFET_NAME, USER_BFET_FULLNAME); Task task = taskManager.createTaskInstance(TestAssignmentErrors.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); try { // WHEN recomputeUser(user.getOid(), task, result); AssertJUnit.fail("Unexpected success"); } catch (ObjectAlreadyExistsException e) { // this is expected result.computeStatus(); TestUtil.assertFailure(result); } user = getUser(user.getOid()); display("User after", user); assertNoLinkedAccount(user); // and again ... task = taskManager.createTaskInstance(TestAssignmentErrors.class.getName() + "." + TEST_NAME); result = task.getResult(); try { // WHEN recomputeUser(user.getOid(), task, result); AssertJUnit.fail("Unexpected success"); } catch (ObjectAlreadyExistsException e) { // this is expected result.computeStatus(); TestUtil.assertFailure(result); } user = getUser(user.getOid()); display("User after", user); assertNoLinkedAccount(user); } /** * Assign account to user, delete the account shadow (not the account), recompute the user. * We expect that the shadow will be re-created and re-linked. * * This is tried on the yellow dummy resource where there is reduced synchronization config. */ @Test public void test224UserAssignAccountDeletedShadowRecomputeReducedSync() throws Exception { final String TEST_NAME = "test224UserAssignAccountDeletedShadowRecomputeReducedSync"; TestUtil.displayTestTile(this, TEST_NAME); //GIVEN PrismObject<UserType> user = setupUserAssignAccountDeletedShadowRecompute(TEST_NAME, RESOURCE_DUMMY_YELLOW_OID, RESOURCE_DUMMY_YELLOW_NAME, USER_CFET_NAME, USER_CFET_FULLNAME); String shadowOidBefore = getSingleLinkOid(user); Task task = taskManager.createTaskInstance(TestAssignmentErrors.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); // WHEN recomputeUser(user.getOid(), task, result); // THEN result.computeStatus(); display("Recompute result", result); TestUtil.assertSuccess(result,2); user = getUser(user.getOid()); display("User after", user); String shadowOidAfter = getSingleLinkOid(user); display("Shadow OID after", shadowOidAfter); PrismObject<ShadowType> shadowAfter = repositoryService.getObject(ShadowType.class, shadowOidAfter, null, result); display("Shadow after", shadowAfter); assertFalse("New and old shadow OIDs are the same", shadowOidBefore.equals(shadowOidAfter)); // ... and again ... task = taskManager.createTaskInstance(TestAssignmentErrors.class.getName() + "." + TEST_NAME); result = task.getResult(); // WHEN recomputeUser(user.getOid(), task, result); // THEN result.computeStatus(); display("Recompute result", result); TestUtil.assertSuccess(result,2); user = getUser(user.getOid()); display("User after", user); String shadowOidAfterAfter = getSingleLinkOid(user); display("Shadow OID after the second time", shadowOidAfterAfter); assertEquals("The shadow OIDs has changed after second recompute", shadowOidAfter, shadowOidAfterAfter); } private PrismObject<UserType> setupUserAssignAccountDeletedShadowRecompute(final String TEST_NAME, String dummyResourceOid, String dummyResourceName, String userName, String userFullName) throws Exception { // GIVEN Task task = taskManager.createTaskInstance(TestAssignmentErrors.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); getDummyResource().resetBreakMode(); PrismObject<UserType> user = createUser(userName, userFullName); AssignmentType assignmentType = createConstructionAssignment(dummyResourceOid, ShadowKindType.ACCOUNT, null); user.asObjectable().getAssignment().add(assignmentType); ActivationType activationType = new ActivationType(); activationType.setAdministrativeStatus(ActivationStatusType.ENABLED); user.asObjectable().setActivation(activationType); addObject(user); // precondition assertDummyAccount(dummyResourceName, userName, userFullName, true); // Re-read user to get the links user = getUser(user.getOid()); display("User before", user); String shadowOidBefore = getSingleLinkOid(user); // precondition PrismObject<ShadowType> shadowBefore = repositoryService.getObject(ShadowType.class, shadowOidBefore, null, result); display("Shadow before", shadowBefore); // delete just the shadow, not the account repositoryService.deleteObject(ShadowType.class, shadowOidBefore, result); result.computeStatus(); TestUtil.assertSuccess(result); assertNoRepoCache(); dummyAuditService.clear(); return user; } }