diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/CapabilityUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/CapabilityUtil.java index 67a4753580d..ee4cd4774b0 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/CapabilityUtil.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/CapabilityUtil.java @@ -333,4 +333,15 @@ public static boolean hasNativeCapability(Capabiliti } return getCapability(nativeCaps.getAny(), capabilityClass) != null; } + + public static boolean hasConfiguredCapability(CapabilitiesType capabilities, Class capabilityClass) { + if (capabilities == null) { + return false; + } + CapabilityCollectionType configuredCaps = capabilities.getConfigured(); + if (configuredCaps == null) { + return false; + } + return getCapability(configuredCaps.getAny(), capabilityClass) != null; + } } diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningContext.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningContext.java index afb396aa780..6e2b4df4927 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningContext.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningContext.java @@ -335,6 +335,14 @@ public boolean hasNativeCapability(Class capabili return CapabilityUtil.hasNativeCapability(connectorCapabilities, capabilityClass); } + public boolean hasConfiguredCapability(Class capabilityClass) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + CapabilitiesType connectorCapabilities = getConnectorCapabilities(capabilityClass); + if (connectorCapabilities == null) { + return false; + } + return CapabilityUtil.hasConfiguredCapability(connectorCapabilities, capabilityClass); + } + private CapabilitiesType getConnectorCapabilities(Class operationCapabilityClass) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, ExpressionEvaluationException { return resourceManager.getConnectorCapabilities(getResource(), getObjectClassDefinition(), operationCapabilityClass); } diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceManager.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceManager.java index 889b99936d7..c225153930a 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceManager.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceManager.java @@ -1343,7 +1343,9 @@ public CapabilitiesType getConnectorCapabilities(Reso } - return applyObjectClassCapabilities(connectorCapabilities, objectClassDefinition); + CapabilitiesType finalCapabilities = applyObjectClassCapabilities(connectorCapabilities, objectClassDefinition); + LOGGER.trace("Returning final capabilities:\n{} ", finalCapabilities); + return finalCapabilities; } @@ -1355,21 +1357,23 @@ private CapabilitiesType applyObjectClassCapabilities(CapabilitiesType connector CapabilitiesType objectClassCapabilities = objectClassDefinition.getCapabilities(); if (objectClassCapabilities == null) { - if (connectorCapabilities == null) { - return null; - } + LOGGER.trace("No capabilities for {} specified, skipping merge.", objectClassDefinition); + return connectorCapabilities; } - CapabilitiesType finalCapabilities = new CapabilitiesType(); - finalCapabilities.setNative(connectorCapabilities.getNative()); + CapabilityCollectionType configured = objectClassCapabilities.getConfigured(); + if (configured == null) { + LOGGER.trace("Empty capabilities in {} specified, skipping merge", objectClassDefinition); + return connectorCapabilities; + } - if (objectClassCapabilities == null) { - finalCapabilities.setConfigured(connectorCapabilities.getConfigured()); - return finalCapabilities; + CapabilitiesType finalCapabilities = new CapabilitiesType(); + if (connectorCapabilities.getNative() != null) { + finalCapabilities.setNative(connectorCapabilities.getNative()); } - CapabilityCollectionType configured = objectClassCapabilities.getConfigured(); if (!hasConfiguredCapabilities(connectorCapabilities)) { + LOGGER.trace("No configured capabilities found for connector, replacing with capabilities defined for {}", objectClassDefinition); finalCapabilities.setConfigured(configured); return finalCapabilities; } diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectConverter.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectConverter.java index 20e77f35676..43c729baecf 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectConverter.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectConverter.java @@ -1175,8 +1175,6 @@ private PrismObject executeEntitlementChangesModify(ProvisioningCont entitlementConverter.collectEntitlementsAsObjectOperation(ctx, roMap, associationDelta, subjectShadowBefore, subjectShadowAfter, parentResult); } -// shadowAfter.findOrCreateContainer(ShadowType.F_ASSOCIATION).addAll((Collection) association.getClonedValues()); -// entitlementConverter.processEntitlementsAdd(resource, shadowAfter, objectClassDefinition); } } @@ -1422,9 +1420,8 @@ private Collection determineActivationChange(ProvisioningContext ctx, ResourceType resource = ctx.getResource(); Collection operations = new ArrayList<>(); -// CapabilitiesType connectorCapabilities = ctx.getConnectorCapabilities(UpdateCapabilityType.class); ActivationCapabilityType activationCapability = ctx.getEffectiveCapability(ActivationCapabilityType.class); - + LOGGER.trace("Found activation capability: {}", PrettyPrinter.prettyPrint(activationCapability)); // administrativeStatus PropertyDelta enabledPropertyDelta = PropertyDeltaCollectionsUtil.findPropertyDelta(objectChange, SchemaConstants.PATH_ACTIVATION_ADMINISTRATIVE_STATUS); @@ -1436,19 +1433,17 @@ private Collection determineActivationChange(ProvisioningContext ctx, } ActivationStatusType status = enabledPropertyDelta.getPropertyNewMatchingPath().getRealValue(); LOGGER.trace("Found activation administrativeStatus change to: {}", status); - - if (ctx.hasNativeCapability(ActivationCapabilityType.class)) { - // Native activation, need to check if there is not also change to simulated activation which may be in conflict - checkSimulatedActivationAdministrativeStatus(ctx, objectChange, status, activationCapability, shadow, result); - operations.add(new PropertyModificationOperation(enabledPropertyDelta)); - } else { - // Try to simulate activation capability + + if (ctx.hasConfiguredCapability(ActivationCapabilityType.class)) { PropertyModificationOperation activationAttribute = convertToSimulatedActivationAdministrativeStatusAttribute( ctx, enabledPropertyDelta, shadow, status, activationCapability, result); if (activationAttribute != null) { operations.add(activationAttribute); } - } + } else { + LOGGER.trace("Native activation capability, creating property modification"); + operations.add(new PropertyModificationOperation(enabledPropertyDelta)); + } } // validFrom @@ -1489,17 +1484,14 @@ private Collection determineActivationChange(ProvisioningContext ctx, } LockoutStatusType status = lockoutPropertyDelta.getPropertyNewMatchingPath().getRealValue(); LOGGER.trace("Found activation lockoutStatus change to: {}", status); - - if (ctx.hasNativeCapability(ActivationCapabilityType.class)) { - // Native lockout, need to check if there is not also change to simulated activation which may be in conflict - checkSimulatedActivationLockoutStatus(ctx, objectChange, status, activationCapability, shadow, result); - operations.add(new PropertyModificationOperation(lockoutPropertyDelta)); - } else { + if (ctx.hasConfiguredCapability(ActivationCapabilityType.class)) { // Try to simulate lockout capability PropertyModificationOperation activationAttribute = convertToSimulatedActivationLockoutStatusAttribute( ctx, lockoutPropertyDelta, shadow, status, activationCapability, result); operations.add(activationAttribute); - } + } else { + operations.add(new PropertyModificationOperation(lockoutPropertyDelta)); + } } return operations; @@ -1605,13 +1597,15 @@ private void transformActivationAttributesAdd(ProvisioningContext ctx, ShadowTyp ActivationCapabilityType activationCapability = ctx.getEffectiveCapability(ActivationCapabilityType.class); if (activation.getAdministrativeStatus() != null) { - if (!ctx.hasNativeCapability(ActivationCapabilityType.class)) { + if (ctx.hasConfiguredCapability(ActivationCapabilityType.class)) { + LOGGER.trace("Start converting simulated activation attribute."); ActivationStatusCapabilityType capActStatus = getActivationAdministrativeStatusFromSimulatedActivation( ctx, activationCapability, shadow, result); if (capActStatus == null) { throw new SchemaException("Attempt to change activation/administrativeStatus on "+ctx.getResource()+" that has neither native" + " nor simulated activation capability"); } + LOGGER.trace("Activation status capability: \n{}", capActStatus); ResourceAttribute newSimulatedAttr = getSimulatedActivationAdministrativeStatusAttribute(ctx, shadow, capActStatus, result); if (newSimulatedAttr != null) { @@ -1624,6 +1618,7 @@ private void transformActivationAttributesAdd(ProvisioningContext ctx, ShadowTyp newSimulatedAttrRealValue = getDisableValue(capActStatus, simulatedAttrValueClass); } + LOGGER.trace("Converted activation status value to {}, attribute {}", newSimulatedAttrRealValue, newSimulatedAttr); Item existingSimulatedAttr = attributesContainer.findItem(newSimulatedAttr.getElementName()); if (!isBlank(newSimulatedAttrRealValue)) { PrismPropertyValue newSimulatedAttrValue = prismContext.itemFactory().createPropertyValue(newSimulatedAttrRealValue); @@ -1638,12 +1633,14 @@ private void transformActivationAttributesAdd(ProvisioningContext ctx, ShadowTyp } activation.setAdministrativeStatus(null); } + }else { + LOGGER.trace("No activation simulated capability, nothing to do."); } } // TODO enable non-string lockout values (MID-3374) if (activation.getLockoutStatus() != null) { - if (!ctx.hasNativeCapability(ActivationCapabilityType.class)) { + if (ctx.hasConfiguredCapability(ActivationCapabilityType.class)) { ActivationLockoutStatusCapabilityType capActStatus = getActivationLockoutStatusFromSimulatedActivation( ctx, activationCapability, shadow, result); if (capActStatus == null) { @@ -2003,13 +2000,11 @@ private void completeActivation(ProvisioningContext ctx, PrismObject if (resourceObjectType.getActivation() != null || CapabilityUtil.isCapabilityEnabled(activationCapability)) { ActivationType activationType = null; - - if (ctx.hasNativeCapability(ActivationCapabilityType.class)) { - activationType = resourceObjectType.getActivation(); - - } else if (CapabilityUtil.isCapabilityEnabled(activationCapability)) { + + if (ctx.hasConfiguredCapability(ActivationCapabilityType.class) && CapabilityUtil.isCapabilityEnabled(activationCapability)){ activationType = convertFromSimulatedActivationAttributes(resourceType, resourceObject, activationCapability, parentResult); - + } else if (ctx.hasNativeCapability(ActivationCapabilityType.class)) { + activationType = resourceObjectType.getActivation(); } else { // No activation capability, nothing to do } @@ -2306,6 +2301,8 @@ private PropertyModificationOperation convertToSimulatedActivationAdministrative return null; } + LOGGER.trace("Simulted activation attribute: {}", simulatedAttribute); + Class simulatedAttrValueClass = getAttributeValueClass(ctx, shadow, simulatedAttribute, capActStatus); PropertyDelta simulatedAttrDelta; @@ -2315,9 +2312,11 @@ private PropertyModificationOperation convertToSimulatedActivationAdministrative ItemPath.create(ShadowType.F_ATTRIBUTES, simulatedAttribute.getElementName()), simulatedAttribute.getDefinition(), simulatedAttribute.getRealValue()); } else if (status == ActivationStatusType.ENABLED) { Object enableValue = getEnableValue(capActStatus, simulatedAttrValueClass); + LOGGER.trace("Value for simulated enable {}", enableValue); simulatedAttrDelta = createActivationPropDelta(simulatedAttribute.getElementName(), simulatedAttribute.getDefinition(), enableValue); } else { Object disableValue = getDisableValue(capActStatus, simulatedAttrValueClass); + LOGGER.trace("Value for simulated disable {}", disableValue); simulatedAttrDelta = createActivationPropDelta(simulatedAttribute.getElementName(), simulatedAttribute.getDefinition(), disableValue); } diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestConfiguredCapabilitiesActivation.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestConfiguredCapabilitiesActivation.java index 7be25305c72..3c4f74198d4 100644 --- a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestConfiguredCapabilitiesActivation.java +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestConfiguredCapabilitiesActivation.java @@ -15,12 +15,23 @@ */ package com.evolveum.midpoint.testing.story; +import com.evolveum.icf.dummy.resource.DummyAccount; +import com.evolveum.icf.dummy.resource.DummyResource; +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.prism.PrismObject; +import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.DummyResourceContoller; +import com.evolveum.midpoint.test.asserter.UserAsserter; import com.evolveum.midpoint.test.util.MidPointTestConstants; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; 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 java.io.File; @@ -39,16 +50,41 @@ public class TestConfiguredCapabilitiesActivation extends AbstractStoryTest { protected static final File RESOURCE_DUMMY_ACTIVATION_FILE = new File(TEST_DIR, "resource-dummy-activation.xml"); protected static final String RESOURCE_DUMMY_ACTIVATION_OID = "4bac305c-ed1f-4919-9670-e11863156811"; protected static final String RESOURCE_DUMMY_ACTIVATION_NAME = "activation"; + + private static final File RESOURCE_DUMMY_ACTIVATION_NATIVE_FILE = new File(TEST_DIR, "resource-dummy-activation-native.xml"); + private static final String RESOURCE_DUMMY_ACTIVATION_NATIVE_OID = "4bac305c-ed1f-4919-aaaa-e11863156811"; + private static final String RESOURCE_DUMMY_ACTIVATION_NATIVE_NAME = "activation-native"; protected static final File SHADOW_FILE = new File(TEST_DIR, "shadow-sample.xml"); protected static final String SHADOW_OID = "6925f5a7-2acb-409b-b2e1-94a6534a9745"; + private static final File ROLE_PIRATE_FILE = new File(TEST_DIR, "role-pirate.xml"); + private static final String ROLE_PIRATE_OID = "34713dae-8d56-4717-b184-86d02c9a2361"; + + private DummyResourceContoller dummyActivationNativeController; + private DummyResource dummyActivationNativeResource; + private ResourceType resourceActivationNativeType; + private PrismObject resourceActivationNative; + + @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { super.initSystem(initTask, initResult); initDummyResourcePirate(RESOURCE_DUMMY_ACTIVATION_NAME, RESOURCE_DUMMY_ACTIVATION_FILE, RESOURCE_DUMMY_ACTIVATION_OID, initTask, initResult); addObject(SHADOW_FILE); + +// dummyActivationNativeController = DummyResourceContoller.create(RESOURCE_DUMMY_ACTIVATION_NATIVE_NAME, resourceActivationNative); +// dummyActivationNativeController.extendSchemaPirate(); +// dummyActivationNativeResource = dummyActivationNativeController.getDummyResource(); +// resourceActivationNative = importAndGetObjectFromFile(ResourceType.class, RESOURCE_DUMMY_ACTIVATION_NATIVE_FILE, RESOURCE_DUMMY_ACTIVATION_NATIVE_OID, initTask, initResult); +// resourceActivationNativeType = resourceActivationNative.asObjectable(); +// dummyActivationNativeController.setResource(resourceActivationNative); + + dummyActivationNativeController = initDummyResource(RESOURCE_DUMMY_ACTIVATION_NATIVE_NAME, RESOURCE_DUMMY_ACTIVATION_NATIVE_FILE, RESOURCE_DUMMY_ACTIVATION_NATIVE_OID, initTask, initResult); + dummyActivationNativeResource = dummyActivationNativeController.getDummyResource(); + + importObjectFromFile(ROLE_PIRATE_FILE, initResult); } @Test @@ -75,5 +111,62 @@ public void test000ImportAccount() throws Exception { assertTrue("Configured addRemoveAttributeValues is not disabled", Boolean.FALSE.equals(capUpdate.isAddRemoveAttributeValues())); */ } + + @Test + public void test010assignJack() throws Exception { + String TEST_NAME = "test010assignJack"; + displayTestTitle(TEST_NAME); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + //GIVEN + PrismObject userJackBefore = getUser(USER_JACK_OID); + UserAsserter.forUser(userJackBefore).activation().assertAdministrativeStatus(ActivationStatusType.ENABLED); + UserAsserter.forUser(userJackBefore).links().assertLinks(0); + + //WHEN + assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); + assignRole(USER_JACK_OID, ROLE_PIRATE_OID, task, result); + display("Result:\n", result); + assertSuccess(result); + + + //THEN + displayThen(TEST_NAME); + PrismObject userJackAfter = getUser(USER_JACK_OID); + UserAsserter.forUser(userJackAfter).links().assertLinks(1); + + DummyAccount jackAccount = dummyActivationNativeResource.getAccountByUsername(USER_JACK_USERNAME); + display("Jack Dummy Account: ", jackAccount.debugDump()); + String enableDisableValue = jackAccount.getAttributeValue("privileges"); + AssertJUnit.assertEquals("Unexpected activation status value: " + enableDisableValue, "false", enableDisableValue); + } + + @Test + public void test011modifyActivationJack() throws Exception { + String TEST_NAME = "test011modifyActivationJack"; + displayTestTitle(TEST_NAME); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + PrismObject userJack = getUser(USER_JACK_OID); + UserAsserter.forUser(userJack).activation().assertAdministrativeStatus(ActivationStatusType.ENABLED); + + //WHEN + modifyUserReplace(USER_JACK_OID, SchemaConstants.PATH_ACTIVATION_ADMINISTRATIVE_STATUS, task, result, ActivationStatusType.DISABLED); + + //THEN + displayThen(TEST_NAME); + PrismObject userJackAfter = getUser(USER_JACK_OID); + UserAsserter.forUser(userJackAfter).activation().assertAdministrativeStatus(ActivationStatusType.DISABLED); + + DummyAccount jackAccount = dummyActivationNativeResource.getAccountByUsername(USER_JACK_USERNAME); + display("Jack Dummy Account: ", jackAccount.debugDump()); + + String enableDisableValue = jackAccount.getAttributeValue("privileges"); + AssertJUnit.assertEquals("Unexpected activation status value: " + enableDisableValue, "true", enableDisableValue); + } } diff --git a/testing/story/src/test/resources/configured-capabilities-activation/resource-dummy-activation-native.xml b/testing/story/src/test/resources/configured-capabilities-activation/resource-dummy-activation-native.xml new file mode 100644 index 00000000000..dcc0f19bf54 --- /dev/null +++ b/testing/story/src/test/resources/configured-capabilities-activation/resource-dummy-activation-native.xml @@ -0,0 +1,154 @@ + + + + + + Dummy Activation Native + + + + + connectorType + com.evolveum.icf.dummy.connector.DummyConnector + + + connectorVersion + 2.0 + + + + + + + + activation-native + true + uuid + + + + + true + true + + + + + + + + account + default + Default Account + true + ri:AccountObjectClass + + icfs:uid + GUID + + + true + + + + + icfs:name + Name + + + true + true + true + + + + + $user/name + + + + + + + + + + + + + + + + + + ri:privileges + false + true + + + + + + + true + + + + polyStringNorm + c:name + + + + + + + + deleted + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlink + + + + unlinked + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#link + + + + unmatched + + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#inactivateShadow + + + + linked + true + + + + diff --git a/testing/story/src/test/resources/configured-capabilities-activation/role-pirate.xml b/testing/story/src/test/resources/configured-capabilities-activation/role-pirate.xml new file mode 100644 index 00000000000..b53218dd56c --- /dev/null +++ b/testing/story/src/test/resources/configured-capabilities-activation/role-pirate.xml @@ -0,0 +1,32 @@ + + + + Pirate + + + + + account + default + + + + diff --git a/testing/story/src/test/resources/logback-test.xml b/testing/story/src/test/resources/logback-test.xml index 18161d74026..07260468c66 100644 --- a/testing/story/src/test/resources/logback-test.xml +++ b/testing/story/src/test/resources/logback-test.xml @@ -34,7 +34,7 @@ - + @@ -68,13 +68,15 @@ - + - + + +