diff --git a/build-system/pom.xml b/build-system/pom.xml index 6e8edfbb3d7..c16f6dfcf6b 100644 --- a/build-system/pom.xml +++ b/build-system/pom.xml @@ -80,7 +80,7 @@ 5.19.0.2 1.3 2.0.6 - 1.4.2.16 + 1.4.3.0-SNAPSHOT 6.1.1 10.11.1.1 1.8.0 @@ -603,7 +603,7 @@ com.evolveum.polygon connector-ldap - 1.4.2.18 + 1.4.3.0-SNAPSHOT xml-apis diff --git a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConfiguration.java b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConfiguration.java index bac0841a44b..ceaf98de250 100644 --- a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConfiguration.java +++ b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConfiguration.java @@ -59,6 +59,7 @@ public class DummyConfiguration extends AbstractConfiguration { private Integer minPasswordLength = null; private boolean addConnectorStateAttributes = false; private boolean supportReturnDefaultAttributes = false; // used e.g. for livesync vs. auxiliary object classes test + private boolean requireNameHint = false; /** * Defines name of the dummy resource instance. There may be several dummy resource running in @@ -345,6 +346,15 @@ public void setSupportReturnDefaultAttributes(boolean supportReturnDefaultAttrib this.supportReturnDefaultAttributes = supportReturnDefaultAttributes; } + @ConfigurationProperty + public boolean isRequireNameHint() { + return requireNameHint; + } + + public void setRequireNameHint(boolean requireNameHint) { + this.requireNameHint = requireNameHint; + } + /** * {@inheritDoc} */ diff --git a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java index 3c187817fc1..4f1e211758f 100644 --- a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java +++ b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java @@ -49,6 +49,7 @@ import org.identityconnectors.framework.common.objects.filter.EqualsFilter; import org.identityconnectors.framework.common.objects.filter.Filter; import org.identityconnectors.framework.common.objects.filter.FilterTranslator; +import org.identityconnectors.framework.common.objects.filter.FilterVisitor; import org.identityconnectors.framework.common.objects.filter.GreaterThanFilter; import org.identityconnectors.framework.common.objects.filter.GreaterThanOrEqualFilter; import org.identityconnectors.framework.common.objects.filter.LessThanFilter; @@ -289,6 +290,7 @@ public Uid create(final ObjectClass objectClass, final Set createAttr public Uid update(ObjectClass objectClass, Uid uid, Set replaceAttributes, OperationOptions options) { log.info("update::begin"); validate(objectClass); + validate(uid); try { @@ -514,6 +516,7 @@ public Uid update(ObjectClass objectClass, Uid uid, Set replaceAttrib */ public Uid addAttributeValues(ObjectClass objectClass, Uid uid, Set valuesToAdd, OperationOptions options) { validate(objectClass); + validate(uid); try { @@ -705,6 +708,7 @@ public Uid addAttributeValues(ObjectClass objectClass, Uid uid, Set v */ public Uid removeAttributeValues(ObjectClass objectClass, Uid uid, Set valuesToRemove, OperationOptions options) { validate(objectClass); + validate(uid); try { @@ -881,10 +885,12 @@ public Uid removeAttributeValues(ObjectClass objectClass, Uid uid, Set createFilterTranslator(ObjectClass objectClass, public void executeQuery(ObjectClass objectClass, Filter query, ResultsHandler handler, OperationOptions options) { log.info("executeQuery({0},{1},{2},{3})", objectClass, query, handler, options); validate(objectClass); + validate(query); notNull(handler, "Results handled object can't be null."); Collection attributesToGet = getAttrsToGet(options); @@ -1442,7 +1449,11 @@ public void sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler ha if (configuration.getUidMode().equals(DummyConfiguration.UID_MODE_NAME)) { uid = new Uid(delta.getObjectName()); } else if (configuration.getUidMode().equals(DummyConfiguration.UID_MODE_UUID)) { - uid = new Uid(delta.getObjectId()); + if (nameHintChecksEnabled()) { + uid = new Uid(delta.getObjectId(), new Name(delta.getObjectName())); + } else { + uid = new Uid(delta.getObjectId()); + } } else { throw new IllegalStateException("Unknown UID mode "+configuration.getUidMode()); } @@ -1898,4 +1909,113 @@ private boolean attributesToGetHasAttribute(Collection attributesToGet, return attributesToGet.contains(attrName); } + public void validate(ObjectClass oc) { + if (oc == null) { + throw new IllegalArgumentException("Object class must not be null."); + } + } + + public void validate(Uid uid) { + if (uid == null) { + throw new IllegalArgumentException("Uid must not be null."); + } + if (nameHintChecksEnabled()) { + if (uid.getNameHint() == null) { + throw new IllegalArgumentException("Uid name hint must not be null."); + } + if (StringUtils.isBlank(uid.getNameHintValue())) { + throw new IllegalArgumentException("Uid name hint must not be empty."); + } + } + } + + private void validate(Filter filter) { + if (filter == null) { + return; + } + if (nameHintChecksEnabled()) { + filter.accept(new FilterVisitor() { + + @Override + public String visitAndFilter(String p, AndFilter filter) { + return null; + } + + @Override + public String visitContainsFilter(String p, ContainsFilter filter) { + return null; + } + + @Override + public String visitContainsAllValuesFilter(String p, ContainsAllValuesFilter filter) { + return null; + } + + @Override + public String visitEqualsFilter(String p, EqualsFilter filter) { + if (filter.getAttribute().is(Uid.NAME)) { + Uid uid = (Uid)filter.getAttribute(); + if (uid.getNameHint() == null) { + throw new IllegalArgumentException("Uid name hint must not be null in filter "+filter); + } + if (StringUtils.isBlank(uid.getNameHintValue())) { + throw new IllegalArgumentException("Uid name hint must not be empty in filter "+filter); + } + } + return null; + } + + @Override + public String visitExtendedFilter(String p, Filter filter) { + return null; + } + + @Override + public String visitGreaterThanFilter(String p, GreaterThanFilter filter) { + return null; + } + + @Override + public String visitGreaterThanOrEqualFilter(String p, GreaterThanOrEqualFilter filter) { + return null; + } + + @Override + public String visitLessThanFilter(String p, LessThanFilter filter) { + return null; + } + + @Override + public String visitLessThanOrEqualFilter(String p, LessThanOrEqualFilter filter) { + return null; + } + + @Override + public String visitNotFilter(String p, NotFilter filter) { + return null; + } + + @Override + public String visitOrFilter(String p, OrFilter filter) { + return null; + } + + @Override + public String visitStartsWithFilter(String p, StartsWithFilter filter) { + return null; + } + + @Override + public String visitEndsWithFilter(String p, EndsWithFilter filter) { + return null; + } + + }, null); + } + } + + private boolean nameHintChecksEnabled() { + return configuration.isRequireNameHint() && !resource.isDisableNameHintChecks(); + } + } diff --git a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/Utils.java b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/Utils.java index 4dbbaed6a5e..ea75f83075e 100644 --- a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/Utils.java +++ b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Evolveum + * Copyright (c) 2010-201ΕΎ Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,12 +58,6 @@ public static T getAttributeSingleValue(Set attributes, String at return null; } - public static void validate(ObjectClass oc) { - if (oc == null) { - throw new IllegalArgumentException("Object class must not be null."); - } - } - public static void notNull(Object object, String message) { if (object == null) { throw new IllegalArgumentException(message); diff --git a/icf-connectors/dummy-resource/src/main/java/com/evolveum/icf/dummy/resource/DummyResource.java b/icf-connectors/dummy-resource/src/main/java/com/evolveum/icf/dummy/resource/DummyResource.java index ade8ba64937..7907d7e0b13 100644 --- a/icf-connectors/dummy-resource/src/main/java/com/evolveum/icf/dummy/resource/DummyResource.java +++ b/icf-connectors/dummy-resource/src/main/java/com/evolveum/icf/dummy/resource/DummyResource.java @@ -110,6 +110,8 @@ public class DummyResource implements DebugDumpable { private boolean generateAccountDescriptionOnCreate = false; // simulates volatile behavior (on create) private boolean generateAccountDescriptionOnUpdate = false; // simulates volatile behavior (on update) + + private boolean disableNameHintChecks = false; // Following two properties are just copied from the connector // configuration and can be checked later. They are otherwise @@ -315,6 +317,14 @@ public void setGenerateAccountDescriptionOnUpdate(boolean generateAccountDescrip this.generateAccountDescriptionOnUpdate = generateAccountDescriptionOnUpdate; } + public boolean isDisableNameHintChecks() { + return disableNameHintChecks; + } + + public void setDisableNameHintChecks(boolean disableNameHintChecks) { + this.disableNameHintChecks = disableNameHintChecks; + } + public Collection getForbiddenNames() { return forbiddenNames; } diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/EntitlementConverter.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/EntitlementConverter.java index b99b7bfa89f..3ed20b7406c 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/EntitlementConverter.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/EntitlementConverter.java @@ -460,13 +460,6 @@ public void collectEntitlementsAsObjectOperationDelete(ProvisioningContext s ObjectQuery query = createQuery(assocDefType, assocAttrDef, valueAttr); - // ObjectFilter filter = EqualsFilter.createEqual(new ItemPath(ShadowType.F_ATTRIBUTES), assocAttrDef, valueAttr.getValue()); - // ObjectFilter filter = InFilter.createIn(new ItemPath(ShadowType.F_ATTRIBUTES, assocAttrDef.getName()), assocAttrDef, valueAttr.getValue()); - // ObjectFilter filter = EqualsFilter.createEqual(new ItemPath(ShadowType.F_ATTRIBUTES, assocAttrDef.getName()), assocAttrDef, valueAttr.getValue()); - // ObjectQuery query = ObjectQuery.createObjectQuery(filter); - // new ObjectQuery(); - // query.setFilter(filter); - AttributesToReturn attributesToReturn = ProvisioningUtil.createAttributesToReturn(entitlementCtx); SearchHierarchyConstraints searchHierarchyConstraints = null; @@ -482,13 +475,15 @@ public void collectEntitlementsAsObjectOperationDelete(ProvisioningContext s ResultHandler handler = new ResultHandler() { @Override public boolean handle(PrismObject entitlementShadow) { - Collection> identifiers = ShadowUtil.getPrimaryIdentifiers(entitlementShadow); - ResourceObjectDiscriminator disc = new ResourceObjectDiscriminator(entitlementOcDef.getTypeName(), identifiers); + Collection> primaryIdentifiers = ShadowUtil.getPrimaryIdentifiers(entitlementShadow); + ResourceObjectDiscriminator disc = new ResourceObjectDiscriminator(entitlementOcDef.getTypeName(), primaryIdentifiers); ResourceObjectOperations operations = roMap.get(disc); if (operations == null) { operations = new ResourceObjectOperations(); roMap.put(disc, operations); operations.setResourceObjectContext(entitlementCtx); + Collection> allIdentifiers = ShadowUtil.getAllIdentifiers(entitlementShadow); + operations.setAllIdentifiers(allIdentifiers); } PropertyDelta attributeDelta = null; @@ -706,7 +701,7 @@ private PrismObject collectEntitlementAsObjectOperation(Prov ResourceAttribute valueAttr = ShadowUtil.getAttribute(subjectShadow, valueAttrName); if (valueAttr == null) { if (!ShadowUtil.isFullShadow(subjectShadow)) { - Collection> subjectIdentifiers = ShadowUtil.getPrimaryIdentifiers(subjectShadow); + Collection> subjectIdentifiers = ShadowUtil.getAllIdentifiers(subjectShadow); LOGGER.trace("Fetching {} ({})", subjectShadow, subjectIdentifiers); subjectShadow = resourceObjectReferenceResolver.fetchResourceObject(subjectCtx, subjectIdentifiers, null, result); subjectShadowAfter = subjectShadow; diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningServiceImpl.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningServiceImpl.java index 2d6502f29ba..9d3829ce04c 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningServiceImpl.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningServiceImpl.java @@ -421,9 +421,8 @@ public int synchronize(ResourceShadowDiscriminator shadowCoordinates, Task task, SchemaDebugUtil.prettyPrint(tokenProperty)); } - LOGGER.trace("Calling shadow cache to synchronize"); processedChanges = getShadowCache(Mode.STANDARD).synchronize(shadowCoordinates, tokenProperty, task, result); - LOGGER.trace("shadow cache to synchronized {} entries", processedChanges); + LOGGER.debug("Synchronization of {} done, token {}, {} changes", resource, tokenProperty, processedChanges); } catch (ObjectNotFoundException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: object not found: " + e.getMessage(), e); 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 9e92ce0b1cb..aa89ce1656b 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 @@ -319,7 +319,7 @@ public void deleteResourceObject(ProvisioningContext ctx, PrismObject> identifiers = ShadowUtil - .getPrimaryIdentifiers(shadow); + .getAllIdentifiers(shadow); if (ProvisioningUtil.isProtectedShadow(ctx.getObjectClassDefinition(), shadow, matchingRuleRegistry)) { LOGGER.error("Attempt to delete protected resource object " + ctx.getObjectClassDefinition() + ": " @@ -392,7 +392,7 @@ public Collection> modifyResourceObject( RefinedObjectClassDefinition objectClassDefinition = ctx.getObjectClassDefinition(); Collection operations = new ArrayList(); - Collection> identifiers = ShadowUtil.getPrimaryIdentifiers(repoShadow); + Collection> identifiers = ShadowUtil.getAllIdentifiers(repoShadow); if (ProvisioningUtil.isProtectedShadow(ctx.getObjectClassDefinition(), repoShadow, matchingRuleRegistry)) { @@ -1016,17 +1016,22 @@ private void executeEntitlements(ProvisioningContext subjectCtx, for (Entry entry: roMap.entrySet()) { ResourceObjectDiscriminator disc = entry.getKey(); ProvisioningContext entitlementCtx = entry.getValue().getResourceObjectContext(); - Collection> identifiers = disc.getIdentifiers(); - Collection operations = entry.getValue().getOperations(); + Collection> primaryIdentifiers = disc.getPrimaryIdentifiers(); + ResourceObjectOperations resourceObjectOperations = entry.getValue(); + Collection> allIdentifiers = resourceObjectOperations.getAllIdentifiers(); + if (allIdentifiers == null || allIdentifiers.isEmpty()) { + allIdentifiers = primaryIdentifiers; + } + Collection operations = resourceObjectOperations.getOperations(); if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Excuting entitlement change identifiers={}:", identifiers, DebugUtil.debugDump(operations, 1)); + LOGGER.trace("Excuting entitlement change identifiers={}:", allIdentifiers, DebugUtil.debugDump(operations, 1)); } OperationResult result = parentResult.createMinorSubresult(OPERATION_MODIFY_ENTITLEMENT); try { - executeModify(entitlementCtx, entry.getValue().getCurrentShadow(), identifiers, operations, result); + executeModify(entitlementCtx, entry.getValue().getCurrentShadow(), allIdentifiers, operations, result); result.recordSuccess(); @@ -1040,6 +1045,10 @@ private void executeEntitlements(ProvisioningContext subjectCtx, // properly record the operation in the result. LOGGER.error("Error while modifying entitlement {} of {}: {}", entitlementCtx, subjectCtx, e.getMessage(), e); result.recordFatalError(e); + } catch (RuntimeException | Error e) { + LOGGER.error("Error while modifying entitlement {} of {}: {}", entitlementCtx, subjectCtx, e.getMessage(), e); + result.recordFatalError(e); + throw e; } } diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectDiscriminator.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectDiscriminator.java index dd18a82e587..79a0ed3a01e 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectDiscriminator.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectDiscriminator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2015 Evolveum + * Copyright (c) 2010-2016 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,20 +33,20 @@ public class ResourceObjectDiscriminator { private QName objectClass; - private Collection> identifiers; + private Collection> primaryIdentifiers; - public ResourceObjectDiscriminator(QName objectClass, Collection> identifiers) { + public ResourceObjectDiscriminator(QName objectClass, Collection> primaryIdentifiers) { super(); this.objectClass = objectClass; - this.identifiers = identifiers; + this.primaryIdentifiers = primaryIdentifiers; } public QName getObjectClass() { return objectClass; } - public Collection> getIdentifiers() { - return identifiers; + public Collection> getPrimaryIdentifiers() { + return primaryIdentifiers; } public boolean matches(PrismObject shadow) { @@ -55,14 +55,14 @@ public boolean matches(PrismObject shadow) { return false; } Collection> shadowIdentifiers = ShadowUtil.getPrimaryIdentifiers(shadow); - return PrismProperty.compareCollectionRealValues(identifiers, shadowIdentifiers); + return PrismProperty.compareCollectionRealValues(primaryIdentifiers, shadowIdentifiers); } @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((identifiers == null) ? 0 : identifiers.hashCode()); + result = prime * result + ((primaryIdentifiers == null) ? 0 : primaryIdentifiers.hashCode()); result = prime * result + ((objectClass == null) ? 0 : objectClass.hashCode()); return result; } @@ -76,10 +76,10 @@ public boolean equals(Object obj) { if (getClass() != obj.getClass()) return false; ResourceObjectDiscriminator other = (ResourceObjectDiscriminator) obj; - if (identifiers == null) { - if (other.identifiers != null) + if (primaryIdentifiers == null) { + if (other.primaryIdentifiers != null) return false; - } else if (!identifiers.equals(other.identifiers)) + } else if (!primaryIdentifiers.equals(other.primaryIdentifiers)) return false; if (objectClass == null) { if (other.objectClass != null) @@ -91,7 +91,7 @@ public boolean equals(Object obj) { @Override public String toString() { - return "ResourceObjectDiscriminator(" + objectClass + ": " + identifiers + ")"; + return "ResourceObjectDiscriminator(" + objectClass + ": " + primaryIdentifiers + ")"; } } diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectOperations.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectOperations.java index 846fba232a6..a121feca3ed 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectOperations.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectOperations.java @@ -35,6 +35,7 @@ public class ResourceObjectOperations { private Collection operations = new ArrayList<>(); private PrismObject currentShadow = null; private ProvisioningContext resourceObjectContext = null; + private Collection> allIdentifiers; public PrismObject getCurrentShadow() { return currentShadow; @@ -56,6 +57,14 @@ public Collection getOperations() { return operations; } + public Collection> getAllIdentifiers() { + return allIdentifiers; + } + + public void setAllIdentifiers(Collection> allIdentifiers) { + this.allIdentifiers = allIdentifiers; + } + @Override public String toString() { return "ResourceObjectOperations(operations=" + operations + ", currentShadow=" + currentShadow diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java index a6e29b59710..903947a00e8 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java @@ -255,7 +255,7 @@ public PrismObject getShadow(String oid, PrismObject rep // Let's get all the identifiers from the Shadow part Collection> identifiers = ShadowUtil - .getPrimaryIdentifiers(repositoryShadow); + .getAllIdentifiers(repositoryShadow); if (identifiers == null || identifiers.isEmpty()) { // check if the account is not only partially created (exist diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowManager.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowManager.java index acac88eda66..5afb9ad818c 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowManager.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowManager.java @@ -210,7 +210,7 @@ public PrismObject lookupShadowInRepository(ProvisioningContext ctx, OperationResult parentResult) throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException { - ObjectQuery query = createSearchShadowQuery(ctx, identifierContainer.getValue().getItems(), prismContext, + ObjectQuery query = createSearchShadowQuery(ctx, identifierContainer.getValue().getItems(), false, prismContext, parentResult); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Searching for shadow using filter (repo):\n{}", @@ -578,23 +578,24 @@ private PrismObject createNewAccountFromChange(ProvisioningContext c private List> searchShadowByIdenifiers(ProvisioningContext ctx, Change change, OperationResult parentResult) throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException { - ObjectQuery query = createSearchShadowQuery(ctx, change.getIdentifiers(), prismContext, parentResult); + Collection> identifiers = change.getIdentifiers(); + ObjectQuery query = createSearchShadowQuery(ctx, identifiers, true, prismContext, parentResult); List> accountList = null; try { accountList = repositoryService.searchObjects(ShadowType.class, query, null, parentResult); } catch (SchemaException ex) { parentResult.recordFatalError( - "Failed to search shadow according to the identifiers: " + change.getIdentifiers() + ". Reason: " + "Failed to search shadow according to the identifiers: " + identifiers + ". Reason: " + ex.getMessage(), ex); throw new SchemaException("Failed to search shadow according to the identifiers: " - + change.getIdentifiers() + ". Reason: " + ex.getMessage(), ex); + + identifiers + ". Reason: " + ex.getMessage(), ex); } MiscSchemaUtil.reduceSearchResult(accountList); return accountList; } - private ObjectQuery createSearchShadowQuery(ProvisioningContext ctx, Collection> identifiers, + private ObjectQuery createSearchShadowQuery(ProvisioningContext ctx, Collection> identifiers, boolean primaryIdentifiersOnly, PrismContext prismContext, OperationResult parentResult) throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException { List conditions = new ArrayList(); RefinedObjectClassDefinition objectClassDefinition = ctx.getObjectClassDefinition(); @@ -607,9 +608,16 @@ private ObjectQuery createSearchShadowQuery(ProvisioningContext ctx, Collection< // definition from any of them. RefinedObjectClassDefinition anyDefinition = ctx.getRefinedSchema().getRefinedDefinitions().iterator().next(); rAttrDef = anyDefinition.findAttributeDefinition(identifier.getElementName()); + if (primaryIdentifiersOnly && !anyDefinition.isPrimaryIdentifier(identifier.getElementName())) { + continue; + } } else { + if (primaryIdentifiersOnly && !objectClassDefinition.isPrimaryIdentifier(identifier.getElementName())) { + continue; + } rAttrDef = objectClassDefinition.findAttributeDefinition(identifier.getElementName()); } + String normalizedIdentifierValue = (String) getNormalizedAttributeValue(identifierValue, rAttrDef); //new ItemPath(ShadowType.F_ATTRIBUTES) PrismPropertyDefinition def = (PrismPropertyDefinition) identifier.getDefinition(); diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/ConnectorInstanceIcfImpl.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/ConnectorInstanceIcfImpl.java index 7e6209aa437..106699170f5 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/ConnectorInstanceIcfImpl.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/ConnectorInstanceIcfImpl.java @@ -210,7 +210,7 @@ public class ConnectorInstanceIcfImpl implements ConnectorInstance { ConnectorInfo cinfo; ConnectorType connectorType; - ConnectorFacade icfConnectorFacade; + ConnectorFacade connIdConnectorFacade; String resourceSchemaNamespace; private APIConfiguration apiConfig = null; @@ -303,7 +303,7 @@ public void configure(PrismContainerValue configuration, OperationResult pare } // Create new connector instance using the transformed configuration - icfConnectorFacade = ConnectorFacadeFactory.getInstance().newInstance(apiConfig); + connIdConnectorFacade = ConnectorFacadeFactory.getInstance().newInstance(apiConfig); result.recordSuccess(); } catch (Throwable ex) { @@ -548,7 +548,7 @@ public void initialize(ResourceSchema resourceSchema, Collection capabil result.addContext("connector", connectorType); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ConnectorFactoryIcfImpl.class); - if (icfConnectorFacade == null) { + if (connIdConnectorFacade == null) { result.recordFatalError("Attempt to use unconfigured connector"); throw new IllegalStateException("Attempt to use unconfigured connector " + ObjectTypeUtil.toShortString(connectorType)); @@ -644,7 +644,7 @@ private void retrieveResourceSchema(List generateObjectClasses, Operation // Connector operation cannot create result for itself, so we need to // create result for it OperationResult icfResult = parentResult.createSubresult(ConnectorFacade.class.getName() + ".schema"); - icfResult.addContext("connector", icfConnectorFacade.getClass()); + icfResult.addContext("connector", connIdConnectorFacade.getClass()); org.identityconnectors.framework.common.objects.Schema icfSchema = null; try { @@ -654,7 +654,7 @@ private void retrieveResourceSchema(List generateObjectClasses, Operation InternalMonitor.recordConnectorOperation("schema"); // TODO have context present //recordIcfOperationStart(reporter, ProvisioningOperation.ICF_GET_SCHEMA, null); - icfSchema = icfConnectorFacade.schema(); + icfSchema = connIdConnectorFacade.schema(); //recordIcfOperationEnd(reporter, ProvisioningOperation.ICF_GET_SCHEMA, null); icfResult.recordSuccess(); @@ -1014,7 +1014,7 @@ private void parseResourceSchema(org.identityconnectors.framework.common.objects // Create capabilities from supported connector operations InternalMonitor.recordConnectorOperation("getSupportedOperations"); - Set> supportedOperations = icfConnectorFacade.getSupportedOperations(); + Set> supportedOperations = connIdConnectorFacade.getSupportedOperations(); LOGGER.trace("Connector supported operations: {}", supportedOperations); @@ -1153,7 +1153,7 @@ public PrismObject fetchObject(Class type, Resource result.addCollectionOfSerializablesAsParam("identifiers", identifiers); result.addContext("connector", connectorType); - if (icfConnectorFacade == null) { + if (connIdConnectorFacade == null) { result.recordFatalError("Attempt to use unconfigured connector"); throw new IllegalStateException("Attempt to use unconfigured connector " + ObjectTypeUtil.toShortString(connectorType) + " " + description); @@ -1262,7 +1262,7 @@ private ConnectorObject fetchConnectorObject(StateReporter reporter, ObjectClass icfResult.addParam("objectClass", icfObjectClass.toString()); icfResult.addParam("uid", uid.getUidValue()); icfResult.addArbitraryObjectAsParam("options", options); - icfResult.addContext("connector", icfConnectorFacade.getClass()); + icfResult.addContext("connector", connIdConnectorFacade.getClass()); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Fetching connector object ObjectClass={}, UID={}, options={}", @@ -1275,7 +1275,7 @@ private ConnectorObject fetchConnectorObject(StateReporter reporter, ObjectClass // Invoke the ICF connector InternalMonitor.recordConnectorOperation("getObject"); recordIcfOperationStart(reporter, ProvisioningOperation.ICF_GET, objectClassDefinition, uid); - co = icfConnectorFacade.getObject(icfObjectClass, uid, options); + co = connIdConnectorFacade.getObject(icfObjectClass, uid, options); recordIcfOperationEnd(reporter, ProvisioningOperation.ICF_GET, objectClassDefinition, uid); icfResult.recordSuccess(); @@ -1471,12 +1471,12 @@ public Collection> addObject(PrismObject> addObject(PrismObject> addObject(PrismObject> identifiers = IcfUtil.convertToIdentifiers(uid, + attributesContainer.getDefinition().getComplexTypeDefinition(), resourceSchema); + for (ResourceAttribute identifier: identifiers) { + attributesContainer.getValue().addReplaceExisting(identifier); } - ResourceAttribute attribute = IcfUtil.createUidAttribute(uid, uidDefinition); - attributesContainer.getValue().addReplaceExisting(attribute); - icfResult.recordSuccess(); + connIdResult.recordSuccess(); result.recordSuccess(); return attributesContainer.getAttributes(); @@ -1774,16 +1773,16 @@ public Set modifyObject(ObjectClassComplexTypeDef checkAndExecuteAdditionalOperation(reporter, additionalOperations, BeforeAfterType.BEFORE, result); - OperationResult icfResult = null; + OperationResult connIdResult = null; try { if (!attributesToAdd.isEmpty()) { OperationOptions options = new OperationOptionsBuilder().build(); - icfResult = result.createSubresult(ConnectorFacade.class.getName() + ".addAttributeValues"); - icfResult.addParam("objectClass", objectClassDef); - icfResult.addParam("uid", uid.getUidValue()); - icfResult.addArbitraryCollectionAsParam("attributes", attributesToAdd); - icfResult.addArbitraryObjectAsParam("options", options); - icfResult.addContext("connector", icfConnectorFacade.getClass()); + connIdResult = result.createSubresult(ConnectorFacade.class.getName() + ".addAttributeValues"); + connIdResult.addParam("objectClass", objectClassDef); + connIdResult.addParam("uid", uid.getUidValue()); + connIdResult.addArbitraryCollectionAsParam("attributes", attributesToAdd); + connIdResult.addArbitraryObjectAsParam("options", options); + connIdResult.addContext("connector", connIdConnectorFacade.getClass()); if (LOGGER.isTraceEnabled()) { LOGGER.trace( @@ -1795,15 +1794,15 @@ public Set modifyObject(ObjectClassComplexTypeDef // Invoking ConnId recordIcfOperationStart(reporter, ProvisioningOperation.ICF_UPDATE, objectClassDef, uid); - uid = icfConnectorFacade.addAttributeValues(objClass, uid, attributesToAdd, options); + uid = connIdConnectorFacade.addAttributeValues(objClass, uid, attributesToAdd, options); recordIcfOperationEnd(reporter, ProvisioningOperation.ICF_UPDATE, objectClassDef, null, uid); - icfResult.recordSuccess(); + connIdResult.recordSuccess(); } } catch (Throwable ex) { recordIcfOperationEnd(reporter, ProvisioningOperation.ICF_UPDATE, objectClassDef, ex, uid); String desc = this.getHumanReadableName() + " while adding attribute values to object identified by ICF UID '"+uid.getUidValue()+"'"; - Throwable midpointEx = processIcfException(ex, desc, icfResult); + Throwable midpointEx = processIcfException(ex, desc, connIdResult); result.computeStatus("Adding attribute values failed"); // Do some kind of acrobatics to do proper throwing of checked // exception @@ -1812,7 +1811,7 @@ public Set modifyObject(ObjectClassComplexTypeDef } else if (midpointEx instanceof CommunicationException) { //in this situation this is not a critical error, becasue we know to handle it..so mute the error and sign it as expected result.muteError(); - icfResult.muteError(); + connIdResult.muteError(); throw (CommunicationException) midpointEx; } else if (midpointEx instanceof GenericFrameworkException) { throw (GenericFrameworkException) midpointEx; @@ -1858,12 +1857,12 @@ public Set modifyObject(ObjectClassComplexTypeDef if (!attributesToUpdate.isEmpty()) { OperationOptions options = new OperationOptionsBuilder().build(); - icfResult = result.createSubresult(ConnectorFacade.class.getName() + ".update"); - icfResult.addParam("objectClass", objectClassDef); - icfResult.addParam("uid", uid==null?"null":uid.getUidValue()); - icfResult.addArbitraryCollectionAsParam("attributes", attributesToUpdate); - icfResult.addArbitraryObjectAsParam("options", options); - icfResult.addContext("connector", icfConnectorFacade.getClass()); + connIdResult = result.createSubresult(ConnectorFacade.class.getName() + ".update"); + connIdResult.addParam("objectClass", objectClassDef); + connIdResult.addParam("uid", uid==null?"null":uid.getUidValue()); + connIdResult.addArbitraryCollectionAsParam("attributes", attributesToUpdate); + connIdResult.addArbitraryObjectAsParam("options", options); + connIdResult.addContext("connector", connIdConnectorFacade.getClass()); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Invoking ICF update(), objectclass={}, uid={}, attributes: {}", new Object[] { @@ -1874,14 +1873,14 @@ public Set modifyObject(ObjectClassComplexTypeDef // Call ICF InternalMonitor.recordConnectorOperation("update"); recordIcfOperationStart(reporter, ProvisioningOperation.ICF_UPDATE, objectClassDef, uid); - uid = icfConnectorFacade.update(objClass, uid, attributesToUpdate, options); + uid = connIdConnectorFacade.update(objClass, uid, attributesToUpdate, options); recordIcfOperationEnd(reporter, ProvisioningOperation.ICF_UPDATE, objectClassDef, null, uid); - icfResult.recordSuccess(); + connIdResult.recordSuccess(); } catch (Throwable ex) { recordIcfOperationEnd(reporter, ProvisioningOperation.ICF_UPDATE, objectClassDef, ex, uid); String desc = this.getHumanReadableName() + " while updating object identified by ICF UID '"+uid.getUidValue()+"'"; - Throwable midpointEx = processIcfException(ex, desc, icfResult); + Throwable midpointEx = processIcfException(ex, desc, connIdResult); result.computeStatus("Update failed"); // Do some kind of acrobatics to do proper throwing of checked // exception @@ -1890,7 +1889,7 @@ public Set modifyObject(ObjectClassComplexTypeDef } else if (midpointEx instanceof CommunicationException) { //in this situation this is not a critical error, becasue we know to handle it..so mute the error and sign it as expected result.muteError(); - icfResult.muteError(); + connIdResult.muteError(); throw (CommunicationException) midpointEx; } else if (midpointEx instanceof GenericFrameworkException) { throw (GenericFrameworkException) midpointEx; @@ -1914,12 +1913,12 @@ public Set modifyObject(ObjectClassComplexTypeDef try { if (!attributesToRemove.isEmpty()) { OperationOptions options = new OperationOptionsBuilder().build(); - icfResult = result.createSubresult(ConnectorFacade.class.getName() + ".removeAttributeValues"); - icfResult.addParam("objectClass", objectClassDef); - icfResult.addParam("uid", uid.getUidValue()); - icfResult.addArbitraryCollectionAsParam("attributes", attributesToRemove); - icfResult.addArbitraryObjectAsParam("options", options); - icfResult.addContext("connector", icfConnectorFacade.getClass()); + connIdResult = result.createSubresult(ConnectorFacade.class.getName() + ".removeAttributeValues"); + connIdResult.addParam("objectClass", objectClassDef); + connIdResult.addParam("uid", uid.getUidValue()); + connIdResult.addArbitraryCollectionAsParam("attributes", attributesToRemove); + connIdResult.addArbitraryObjectAsParam("options", options); + connIdResult.addContext("connector", connIdConnectorFacade.getClass()); if (LOGGER.isTraceEnabled()) { LOGGER.trace( @@ -1929,14 +1928,14 @@ public Set modifyObject(ObjectClassComplexTypeDef InternalMonitor.recordConnectorOperation("removeAttributeValues"); recordIcfOperationStart(reporter, ProvisioningOperation.ICF_UPDATE, objectClassDef, uid); - uid = icfConnectorFacade.removeAttributeValues(objClass, uid, attributesToRemove, options); + uid = connIdConnectorFacade.removeAttributeValues(objClass, uid, attributesToRemove, options); recordIcfOperationEnd(reporter, ProvisioningOperation.ICF_UPDATE, objectClassDef, null, uid); - icfResult.recordSuccess(); + connIdResult.recordSuccess(); } } catch (Throwable ex) { recordIcfOperationEnd(reporter, ProvisioningOperation.ICF_UPDATE, objectClassDef, ex, uid); String desc = this.getHumanReadableName() + " while removing attribute values from object identified by ICF UID '"+uid.getUidValue()+"'"; - Throwable midpointEx = processIcfException(ex, desc, icfResult); + Throwable midpointEx = processIcfException(ex, desc, connIdResult); result.computeStatus("Removing attribute values failed"); // Do some kind of acrobatics to do proper throwing of checked // exception @@ -1945,7 +1944,7 @@ public Set modifyObject(ObjectClassComplexTypeDef } else if (midpointEx instanceof CommunicationException) { //in this situation this is not a critical error, becasue we know to handle it..so mute the error and sign it as expected result.muteError(); - icfResult.muteError(); + connIdResult.muteError(); throw (CommunicationException) midpointEx; } else if (midpointEx instanceof GenericFrameworkException) { throw (GenericFrameworkException) midpointEx; @@ -2036,13 +2035,13 @@ public void deleteObject(ObjectClassComplexTypeDefinition objectClass, Collectio OperationResult icfResult = result.createSubresult(ConnectorFacade.class.getName() + ".delete"); icfResult.addArbitraryObjectAsParam("uid", uid); icfResult.addArbitraryObjectAsParam("objectClass", objClass); - icfResult.addContext("connector", icfConnectorFacade.getClass()); + icfResult.addContext("connector", connIdConnectorFacade.getClass()); try { InternalMonitor.recordConnectorOperation("delete"); recordIcfOperationStart(reporter, ProvisioningOperation.ICF_DELETE, objectClass, uid); - icfConnectorFacade.delete(objClass, uid, new OperationOptionsBuilder().build()); + connIdConnectorFacade.delete(objClass, uid, new OperationOptionsBuilder().build()); recordIcfOperationEnd(reporter, ProvisioningOperation.ICF_DELETE, objectClass, null, uid); icfResult.recordSuccess(); @@ -2098,14 +2097,14 @@ public PrismProperty fetchCurrentToken(ObjectClassComplexTypeDefinition o } OperationResult icfResult = result.createSubresult(ConnectorFacade.class.getName() + ".sync"); - icfResult.addContext("connector", icfConnectorFacade.getClass()); + icfResult.addContext("connector", connIdConnectorFacade.getClass()); icfResult.addArbitraryObjectAsParam("icfObjectClass", icfObjectClass); SyncToken syncToken = null; try { InternalMonitor.recordConnectorOperation("getLatestSyncToken"); recordIcfOperationStart(reporter, ProvisioningOperation.ICF_GET_LATEST_SYNC_TOKEN, objectClassDef); - syncToken = icfConnectorFacade.getLatestSyncToken(icfObjectClass); + syncToken = connIdConnectorFacade.getLatestSyncToken(icfObjectClass); recordIcfOperationEnd(reporter, ProvisioningOperation.ICF_GET_LATEST_SYNC_TOKEN, objectClassDef); icfResult.recordSuccess(); icfResult.addReturn("syncToken", syncToken==null?null:String.valueOf(syncToken.getValue())); @@ -2181,24 +2180,24 @@ public boolean handle(SyncDelta delta) { } }; - OperationResult icfResult = result.createSubresult(ConnectorFacade.class.getName() + ".sync"); - icfResult.addContext("connector", icfConnectorFacade.getClass()); - icfResult.addArbitraryObjectAsParam("icfObjectClass", icfObjectClass); - icfResult.addArbitraryObjectAsParam("syncToken", syncToken); - icfResult.addArbitraryObjectAsParam("syncHandler", syncHandler); + OperationResult connIdResult = result.createSubresult(ConnectorFacade.class.getName() + ".sync"); + connIdResult.addContext("connector", connIdConnectorFacade.getClass()); + connIdResult.addArbitraryObjectAsParam("connIdObjectClass", icfObjectClass); + connIdResult.addArbitraryObjectAsParam("syncToken", syncToken); + connIdResult.addArbitraryObjectAsParam("syncHandler", syncHandler); SyncToken lastReceivedToken; try { InternalMonitor.recordConnectorOperation("sync"); recordIcfOperationStart(reporter, ProvisioningOperation.ICF_SYNC, objectClass); - lastReceivedToken = icfConnectorFacade.sync(icfObjectClass, syncToken, syncHandler, + lastReceivedToken = connIdConnectorFacade.sync(icfObjectClass, syncToken, syncHandler, options); recordIcfOperationEnd(reporter, ProvisioningOperation.ICF_SYNC, objectClass); - icfResult.recordSuccess(); - icfResult.addReturn(OperationResult.RETURN_COUNT, syncDeltas.size()); + connIdResult.recordSuccess(); + connIdResult.addReturn(OperationResult.RETURN_COUNT, syncDeltas.size()); } catch (Throwable ex) { recordIcfOperationEnd(reporter, ProvisioningOperation.ICF_SYNC, objectClass, ex); - Throwable midpointEx = processIcfException(ex, this, icfResult); + Throwable midpointEx = processIcfException(ex, this, connIdResult); result.computeStatus(); // Do some kind of acrobatics to do proper throwing of checked // exception @@ -2246,7 +2245,7 @@ public void test(OperationResult parentResult) { try { InternalMonitor.recordConnectorOperation("test"); - icfConnectorFacade.test(); + connIdConnectorFacade.test(); connectionResult.recordSuccess(); } catch (UnsupportedOperationException ex) { // Connector does not support test connection. @@ -2421,14 +2420,14 @@ private void recordResume() { // create result for it OperationResult icfResult = result.createSubresult(ConnectorFacade.class.getName() + ".search"); icfResult.addArbitraryObjectAsParam("objectClass", icfObjectClass); - icfResult.addContext("connector", icfConnectorFacade.getClass()); + icfResult.addContext("connector", connIdConnectorFacade.getClass()); SearchResult icfSearchResult; try { InternalMonitor.recordConnectorOperation("search"); recordIcfOperationStart(reporter, ProvisioningOperation.ICF_SEARCH, objectClassDefinition); - icfSearchResult = icfConnectorFacade.search(icfObjectClass, filter, icfHandler, options); + icfSearchResult = connIdConnectorFacade.search(icfObjectClass, filter, icfHandler, options); recordIcfOperationEnd(reporter, ProvisioningOperation.ICF_SEARCH, objectClassDefinition); icfResult.recordSuccess(); @@ -2527,7 +2526,7 @@ public int count(ObjectClassComplexTypeDefinition objectClassDefinition, final O // create result for it OperationResult icfResult = result.createSubresult(ConnectorFacade.class.getName() + ".search"); icfResult.addArbitraryObjectAsParam("objectClass", icfObjectClass); - icfResult.addContext("connector", icfConnectorFacade.getClass()); + icfResult.addContext("connector", connIdConnectorFacade.getClass()); int retval; @@ -2545,7 +2544,7 @@ public boolean handle(ConnectorObject connectorObject) { }; InternalMonitor.recordConnectorOperation("search"); recordIcfOperationStart(reporter, ProvisioningOperation.ICF_SEARCH, objectClassDefinition); - SearchResult searchResult = icfConnectorFacade.search(icfObjectClass, filter, icfHandler, options); + SearchResult searchResult = connIdConnectorFacade.search(icfObjectClass, filter, icfHandler, options); recordIcfOperationEnd(reporter, ProvisioningOperation.ICF_SEARCH, objectClassDefinition); if (searchResult == null || searchResult.getRemainingPagedResults() == -1) { @@ -2613,13 +2612,13 @@ private Filter convertFilterToIcf(ObjectQuery query, ObjectClassComplexTypeDefin /** - * Looks up ICF Uid identifier in a (potentially multi-valued) set of + * Looks up ConnId Uid identifier in a (potentially multi-valued) set of * identifiers. Handy method to convert midPoint identifier style to an ICF * identifier style. * * @param identifiers * midPoint resource object identifiers - * @return ICF UID or null + * @return ConnId UID or null */ private Uid getUid(ObjectClassComplexTypeDefinition objectClass, Collection> identifiers) throws SchemaException { if (identifiers.size() == 0) { @@ -2632,9 +2631,21 @@ private Uid getUid(ObjectClassComplexTypeDefinition objectClass, Collection attr : identifiers) { if (objectClass.isPrimaryIdentifier(attr.getElementName())) { - return new Uid(((ResourceAttribute) attr).getValue().getValue()); + uidValue = ((ResourceAttribute) attr).getValue().getValue(); + } + if (objectClass.isSecondaryIdentifier(attr.getElementName())) { + nameValue = ((ResourceAttribute) attr).getValue().getValue(); + } + } + if (uidValue != null) { + if (nameValue == null) { + return new Uid(uidValue); + } else { + return new Uid(uidValue, new Name(nameValue)); } } // fallback, compatibility @@ -2775,21 +2786,21 @@ private void addConvertedValues(Collection> pvals, attributes.add(ab.build()); } - private List> getChangesFromSyncDeltas(ObjectClass icfObjClass, Collection icfDeltas, PrismSchema schema, - OperationResult parentResult) + private List> getChangesFromSyncDeltas(ObjectClass connIdObjClass, Collection connIdDeltas, + PrismSchema schema, OperationResult parentResult) throws SchemaException, GenericFrameworkException { List> changeList = new ArrayList>(); - QName objectClass = icfNameMapper.objectClassToQname(icfObjClass, getSchemaNamespace(), legacySchema); + QName objectClass = icfNameMapper.objectClassToQname(connIdObjClass, getSchemaNamespace(), legacySchema); ObjectClassComplexTypeDefinition objClassDefinition = null; if (objectClass != null) { objClassDefinition = (ObjectClassComplexTypeDefinition) schema.findComplexTypeDefinition(objectClass); } - Validate.notNull(icfDeltas, "Sync result must not be null."); - for (SyncDelta icfDelta : icfDeltas) { + Validate.notNull(connIdDeltas, "Sync result must not be null."); + for (SyncDelta icfDelta : connIdDeltas) { - ObjectClass deltaIcfObjClass = icfObjClass; + ObjectClass deltaIcfObjClass = connIdObjClass; QName deltaObjectClass = objectClass; ObjectClassComplexTypeDefinition deltaObjClassDefinition = objClassDefinition; if (objectClass == null) { @@ -2812,11 +2823,8 @@ private List> getChangesFromSyncDeltas(ObjectCl LOGGER.trace("START creating delta of type DELETE"); ObjectDelta objectDelta = new ObjectDelta( ShadowType.class, ChangeType.DELETE, prismContext); - ResourceAttribute uidAttribute = IcfUtil.createUidAttribute( - icfDelta.getUid(), - IcfUtil.getUidDefinition(deltaObjClassDefinition, resourceSchema)); - Collection> identifiers = new ArrayList>(1); - identifiers.add(uidAttribute); + Collection> identifiers = IcfUtil.convertToIdentifiers(icfDelta.getUid(), + deltaObjClassDefinition, resourceSchema); Change change = new Change(identifiers, objectDelta, getToken(icfDelta.getToken())); change.setObjectClassDefinition(deltaObjClassDefinition); changeList.add(change); @@ -2834,7 +2842,7 @@ private List> getChangesFromSyncDeltas(ObjectCl LOGGER.trace("Got current shadow: {}", currentShadow.debugDump()); } - Collection> identifiers = ShadowUtil.getPrimaryIdentifiers(currentShadow); + Collection> identifiers = ShadowUtil.getAllIdentifiers(currentShadow); ObjectDelta objectDelta = new ObjectDelta( ShadowType.class, ChangeType.ADD, prismContext); @@ -2858,7 +2866,7 @@ private List> getChangesFromSyncDeltas(ObjectCl LOGGER.trace("Got current shadow: {}", currentShadow.debugDump()); } - Collection> identifiers = ShadowUtil.getPrimaryIdentifiers(currentShadow); + Collection> identifiers = ShadowUtil.getAllIdentifiers(currentShadow); Change change = new Change(identifiers, currentShadow, getToken(icfDelta.getToken())); change.setObjectClassDefinition(deltaObjClassDefinition); @@ -2974,7 +2982,7 @@ private Object executeScriptIcf(StateReporter reporter, ExecuteProvisioningScrip ScriptContext scriptContext = convertToScriptContext(scriptOperation); OperationResult icfResult = result.createSubresult(ConnectorFacade.class.getName() + "." + icfOpName); - icfResult.addContext("connector", icfConnectorFacade.getClass()); + icfResult.addContext("connector", connIdConnectorFacade.getClass()); Object output = null; @@ -2985,10 +2993,10 @@ private Object executeScriptIcf(StateReporter reporter, ExecuteProvisioningScrip recordIcfOperationStart(reporter, ProvisioningOperation.ICF_SCRIPT, null); if (scriptOperation.isConnectorHost()) { InternalMonitor.recordConnectorOperation("runScriptOnConnector"); - output = icfConnectorFacade.runScriptOnConnector(scriptContext, new OperationOptionsBuilder().build()); + output = connIdConnectorFacade.runScriptOnConnector(scriptContext, new OperationOptionsBuilder().build()); } else if (scriptOperation.isResourceHost()) { InternalMonitor.recordConnectorOperation("runScriptOnResource"); - output = icfConnectorFacade.runScriptOnResource(scriptContext, new OperationOptionsBuilder().build()); + output = connIdConnectorFacade.runScriptOnResource(scriptContext, new OperationOptionsBuilder().build()); } recordIcfOperationEnd(reporter, ProvisioningOperation.ICF_SCRIPT, null); 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 49644ac9834..db52ea01db7 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 @@ -291,10 +291,18 @@ PrismObject convertToResourceObject(ConnectorObject co } - // Add Uid if it is not there already. It can be already present, e.g. if Uid and Name represent the same attribute + // Add Uid if it is not there already. It can be already present, + // e.g. if Uid and Name represent the same attribute Uid uid = co.getUid(); - ResourceAttribute uidRoa = IcfUtil.createUidAttribute(uid, IcfUtil.getUidDefinition(attributesContainerDefinition.getComplexTypeDefinition())); - if (attributesContainer.getValue().findItem(uidRoa.getElementName()) == null) { + ObjectClassComplexTypeDefinition ocDef = attributesContainerDefinition.getComplexTypeDefinition(); + ResourceAttributeDefinition uidDefinition = IcfUtil.getUidDefinition(ocDef); + if (uidDefinition == null) { + throw new SchemaException("No definition for ConnId UID attribute found in definition " + + ocDef); + } + if (attributesContainer.getValue().findItem(uidDefinition.getName()) == null) { + ResourceAttribute uidRoa = uidDefinition.instantiate(); + uidRoa.setValue(new PrismPropertyValue(uid.getUidValue())); attributesContainer.getValue().add(uidRoa); } diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/IcfUtil.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/IcfUtil.java index 522a495a16f..05dc3eccc4e 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/IcfUtil.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/IcfUtil.java @@ -25,6 +25,7 @@ import java.net.UnknownHostException; import java.sql.SQLException; import java.sql.SQLSyntaxErrorException; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; @@ -119,12 +120,12 @@ static Throwable processIcfException(Throwable icfException, ConnectorInstanceIc * * @param icfException * exception from the ICF - * @param icfResult + * @param connIdResult * OperationResult to record failure * @return reasonable midPoint exception */ static Throwable processIcfException(Throwable icfException, String desc, - OperationResult icfResult) { + OperationResult connIdResult) { // Whole exception handling in this case is a black magic. // ICF does not define any exceptions and there is no "best practice" // how to handle ICF errors @@ -132,7 +133,7 @@ static Throwable processIcfException(Throwable icfException, String desc, // we can do. if (icfException == null) { - icfResult.recordFatalError("Null exception while processing ICF exception "); + connIdResult.recordFatalError("Null exception while processing ICF exception "); throw new IllegalArgumentException("Null exception while processing ICF exception "); } @@ -165,12 +166,12 @@ static Throwable processIcfException(Throwable icfException, String desc, // NPE with a message text is in fact not a NPE but an application exception // this usually means that some parameter is missing Exception newEx = new SchemaException(createMessageFromAllExceptions("Required attribute is missing",icfException)); - icfResult.recordFatalError("Required attribute is missing: "+icfException.getMessage(),newEx); + connIdResult.recordFatalError("Required attribute is missing: "+icfException.getMessage(),newEx); return newEx; } else if (icfException instanceof IllegalArgumentException) { // Let's assume this must be a configuration problem Exception newEx = new com.evolveum.midpoint.util.exception.ConfigurationException(createMessageFromInnermostException("Configuration error", icfException)); - icfResult.recordFatalError("Configuration error: "+icfException.getMessage(), newEx); + connIdResult.recordFatalError("Configuration error: "+icfException.getMessage(), newEx); return newEx; } //fix of MiD-2645 @@ -180,30 +181,30 @@ static Throwable processIcfException(Throwable icfException, String desc, String exCauseClassName = icfException.getCause().getClass().getSimpleName(); if (exCauseClassName.equals(CONNECTIONS_EXCEPTION_CLASS_NAME) ){ Exception newEx = new CommunicationException(createMessageFromAllExceptions("Connect error", icfException)); - icfResult.recordFatalError("Connect error: " + icfException.getMessage(), newEx); + connIdResult.recordFatalError("Connect error: " + icfException.getMessage(), newEx); return newEx; } } if (icfException.getClass().getPackage().equals(NullPointerException.class.getPackage())) { // There are java.lang exceptions, they are safe to pass through - icfResult.recordFatalError(icfException); + connIdResult.recordFatalError(icfException); return icfException; } if (icfException.getClass().getPackage().equals(SchemaException.class.getPackage())) { // Common midPoint exceptions, pass through - icfResult.recordFatalError(icfException); + connIdResult.recordFatalError(icfException); return icfException; } - if (icfResult == null) { + if (connIdResult == null) { throw new IllegalArgumentException(createMessageFromAllExceptions("Null parent result while processing ICF exception",icfException)); } // Introspect the inner exceptions and look for known causes - Exception knownCause = lookForKnownCause(icfException, icfException, icfResult); + Exception knownCause = lookForKnownCause(icfException, icfException, connIdResult); if (knownCause != null) { - icfResult.recordFatalError(knownCause); + connIdResult.recordFatalError(knownCause); return knownCause; } @@ -216,67 +217,67 @@ static Throwable processIcfException(Throwable icfException, String desc, if (icfException instanceof IllegalArgumentException) { // This is most likely missing attribute or similar schema thing Exception newEx = new SchemaException(createMessageFromAllExceptions("Schema violation (most likely)", icfException)); - icfResult.recordFatalError("Schema violation: "+icfException.getMessage(), newEx); + connIdResult.recordFatalError("Schema violation: "+icfException.getMessage(), newEx); return newEx; } else if (icfException instanceof ConfigurationException) { Exception newEx = new com.evolveum.midpoint.util.exception.ConfigurationException(createMessageFromInnermostException("Configuration error", icfException)); - icfResult.recordFatalError("Configuration error: "+icfException.getMessage(), newEx); + connIdResult.recordFatalError("Configuration error: "+icfException.getMessage(), newEx); return newEx; } else if (icfException instanceof AlreadyExistsException) { Exception newEx = new ObjectAlreadyExistsException(createMessageFromAllExceptions(null, icfException)); - icfResult.recordFatalError("Object already exists: "+icfException.getMessage(), newEx); + connIdResult.recordFatalError("Object already exists: "+icfException.getMessage(), newEx); return newEx; } else if (icfException instanceof PermissionDeniedException) { Exception newEx = new SecurityViolationException(createMessageFromAllExceptions(null, icfException)); - icfResult.recordFatalError("Security violation: "+icfException.getMessage(), newEx); + connIdResult.recordFatalError("Security violation: "+icfException.getMessage(), newEx); return newEx; } else if (icfException instanceof ConnectionBrokenException) { Exception newEx = new CommunicationException(createMessageFromAllExceptions("Connection broken", icfException)); - icfResult.recordFatalError("Connection broken: "+icfException.getMessage(), newEx); + connIdResult.recordFatalError("Connection broken: "+icfException.getMessage(), newEx); return newEx; } else if (icfException instanceof ConnectionFailedException) { Exception newEx = new CommunicationException(createMessageFromAllExceptions("Connection failed", icfException)); - icfResult.recordFatalError("Connection failed: "+icfException.getMessage(), newEx); + connIdResult.recordFatalError("Connection failed: "+icfException.getMessage(), newEx); return newEx; } else if (icfException instanceof UnknownHostException) { Exception newEx = new CommunicationException(createMessageFromAllExceptions("Unknown host", icfException)); - icfResult.recordFatalError("Unknown host: "+icfException.getMessage(), newEx); + connIdResult.recordFatalError("Unknown host: "+icfException.getMessage(), newEx); return newEx; } else if (icfException instanceof ConnectorIOException) { Exception newEx = new CommunicationException(createMessageFromAllExceptions("IO error", icfException)); - icfResult.recordFatalError("IO error: "+icfException.getMessage(), newEx); + connIdResult.recordFatalError("IO error: "+icfException.getMessage(), newEx); return newEx; } else if (icfException instanceof InvalidCredentialException) { Exception newEx = new GenericFrameworkException(createMessageFromAllExceptions("Invalid credentials", icfException)); - icfResult.recordFatalError("Invalid credentials: "+icfException.getMessage(), newEx); + connIdResult.recordFatalError("Invalid credentials: "+icfException.getMessage(), newEx); return newEx; } else if (icfException instanceof OperationTimeoutException) { Exception newEx = new CommunicationException(createMessageFromAllExceptions("Operation timed out", icfException)); - icfResult.recordFatalError("Operation timed out: "+icfException.getMessage(), newEx); + connIdResult.recordFatalError("Operation timed out: "+icfException.getMessage(), newEx); return newEx; } else if (icfException instanceof UnknownUidException) { Exception newEx = new ObjectNotFoundException(createMessageFromAllExceptions(null, icfException)); - icfResult.recordFatalError("Unknown UID: "+icfException.getMessage(), newEx); + connIdResult.recordFatalError("Unknown UID: "+icfException.getMessage(), newEx); return newEx; } else if (icfException instanceof InvalidAttributeValueException) { Exception newEx = new SchemaException(createMessageFromAllExceptions(null, icfException)); - icfResult.recordFatalError("Schema violation: "+icfException.getMessage(), newEx); + connIdResult.recordFatalError("Schema violation: "+icfException.getMessage(), newEx); return newEx; } else if (icfException instanceof RetryableException) { Exception newEx = new CommunicationException(createMessageFromAllExceptions(null, icfException)); - icfResult.recordFatalError("Retryable errror: "+icfException.getMessage(), newEx); + connIdResult.recordFatalError("Retryable errror: "+icfException.getMessage(), newEx); return newEx; } else if (icfException instanceof ConnectorSecurityException) { @@ -286,7 +287,7 @@ static Throwable processIcfException(Throwable icfException, String desc, // Maybe we need special exception for security? Exception newEx = new SecurityViolationException(createMessageFromAllExceptions("Security violation",icfException)); - icfResult.recordFatalError( + connIdResult.recordFatalError( "Security violation: " + icfException.getMessage(), newEx); return newEx; @@ -294,7 +295,7 @@ static Throwable processIcfException(Throwable icfException, String desc, // Fallback Exception newEx = new GenericFrameworkException(createMessageFromAllExceptions(null,icfException)); - icfResult.recordFatalError(newEx); + connIdResult.recordFatalError(newEx); return newEx; } @@ -488,39 +489,82 @@ private static void addInnermostExceptionsToMessage(StringBuilder sb, Throwable } public static ResourceAttributeDefinition getUidDefinition(ObjectClassComplexTypeDefinition def, ResourceSchema schema) { + ObjectClassComplexTypeDefinition concreteObjectClassDefinition = getConcreteObjectClassDefinition(def, schema); + if (concreteObjectClassDefinition == null) { + return null; + } else { + return getUidDefinition(concreteObjectClassDefinition); + } + } + + public static ObjectClassComplexTypeDefinition getConcreteObjectClassDefinition(ObjectClassComplexTypeDefinition def, ResourceSchema schema) { if (def == null) { // Return definition from any structural object class. If there is no specific object class definition then // the UID definition must be the same in all structural object classes and that means that we can use // definition from any structural object class. for (ObjectClassComplexTypeDefinition objectClassDefinition: schema.getObjectClassDefinitions()) { if (!objectClassDefinition.isAuxiliary()) { - return getUidDefinition(objectClassDefinition); + return objectClassDefinition; } } return null; } else { - return getUidDefinition(def); + return def; } } - public static ResourceAttributeDefinition getUidDefinition(ObjectClassComplexTypeDefinition def) { - Collection identifiers = def.getPrimaryIdentifiers(); - if (identifiers.size() > 1) { + Collection primaryIdentifiers = def.getPrimaryIdentifiers(); + if (primaryIdentifiers.size() > 1) { throw new UnsupportedOperationException("Multiple primary identifiers are not supported"); } - if (identifiers.size() == 1) { - return identifiers.iterator().next(); + if (primaryIdentifiers.size() == 1) { + return primaryIdentifiers.iterator().next(); } else { // fallback, compatibility return def.findAttributeDefinition(ConnectorFactoryIcfImpl.ICFS_UID); } } - public static ResourceAttribute createUidAttribute(Uid uid, ResourceAttributeDefinition uidDefinition) { + public static ResourceAttributeDefinition getNameDefinition(ObjectClassComplexTypeDefinition def) { + Collection secondaryIdentifiers = def.getSecondaryIdentifiers(); + if (secondaryIdentifiers.size() > 1) { + throw new UnsupportedOperationException("Multiple secondary identifiers are not supported"); + } + if (secondaryIdentifiers.size() == 1) { + return secondaryIdentifiers.iterator().next(); + } else { + // fallback, compatibility + return def.findAttributeDefinition(ConnectorFactoryIcfImpl.ICFS_NAME); + } + } + + public static Collection> convertToIdentifiers(Uid uid, + ObjectClassComplexTypeDefinition ocDef, ResourceSchema resourceSchema) throws SchemaException { + ObjectClassComplexTypeDefinition concreteObjectClassDefinition = getConcreteObjectClassDefinition(ocDef, resourceSchema); + if (concreteObjectClassDefinition == null) { + throw new SchemaException("Concrete object class of "+ocDef+" cannot be found"); + } + ResourceAttributeDefinition uidDefinition = getUidDefinition(concreteObjectClassDefinition); + if (uidDefinition == null) { + throw new SchemaException("No definition for ConnId UID attribute found in definition " + + ocDef); + } + Collection> identifiers = new ArrayList>(2); ResourceAttribute uidRoa = uidDefinition.instantiate(); uidRoa.setValue(new PrismPropertyValue(uid.getUidValue())); - return uidRoa; + identifiers.add(uidRoa); + if (uid.getNameHint() != null) { + ResourceAttributeDefinition nameDefinition = getNameDefinition(concreteObjectClassDefinition); + if (nameDefinition == null) { + throw new SchemaException("No definition for ConnId NAME attribute found in definition " + + ocDef); + } + ResourceAttribute nameRoa = nameDefinition.instantiate(); + nameRoa.setValue(new PrismPropertyValue(uid.getNameHintValue())); + identifiers.add(nameRoa); + } + return identifiers; } public static GuardedString toGuardedString(ProtectedStringType ps, String propertyName, Protector protector) { diff --git a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummy.java b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummy.java index 915f657b072..dc90c082090 100644 --- a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummy.java +++ b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummy.java @@ -2686,9 +2686,11 @@ public void test173SearchWeaponCutlass() throws Exception { public void test175SearchUidExact() throws Exception { final String TEST_NAME = "test175SearchUidExact"; TestUtil.displayTestTile(TEST_NAME); + dummyResource.setDisableNameHintChecks(true); testSeachIterativeSingleAttrFilter(TEST_NAME, ConnectorFactoryIcfImpl.ICFS_UID, willIcfUid, null, true, transformNameFromResource("Will")); + dummyResource.setDisableNameHintChecks(false); } @Test diff --git a/provisioning/provisioning-impl/src/test/resources/impl/dummy-uuid/resource-dummy.xml b/provisioning/provisioning-impl/src/test/resources/impl/dummy-uuid/resource-dummy.xml index 9f57f29d0e0..3ca10b71273 100644 --- a/provisioning/provisioning-impl/src/test/resources/impl/dummy-uuid/resource-dummy.xml +++ b/provisioning/provisioning-impl/src/test/resources/impl/dummy-uuid/resource-dummy.xml @@ -1,6 +1,6 @@ + + Localhost OpenDJ Unsafe + + + + + + c:connectorType + com.evolveum.polygon.connector.ldap.LdapConnector + + + + + + + + 10389 + localhost + dc=example,dc=com + uid=idm,ou=Administrators,dc=example,dc=com + secret + auto + uid + ds-pwp-account-disabled + isMemberOf + createTimestamp + true + + + false + false + false + + + + + + default + Default Account + true + ri:inetOrgPerson + + ri:dn + Distinguished Name + + + $user/name + + + + + + + + ri:entryUUID + Entry UUID + + + ri:cn + Common Name + + 1 + + + + fullName + + + + + fullName + + + + + ri:sn + Surname + + + familyName + + + + + familyName + + + + + ri:givenName + Given Name + + + givenName + + + + + givenName + + + + + ri:uid + Login Name + + weak + + name + + + + + + + + name + + + + + ri:employeeType + + + employeeType + + + + + ri:isMemberOf + explicit + + + + ri:group + LDAP Group Membership + entitlement + ldapGroup + objectToSubject + ri:uniqueMember + ri:dn + ri:isMemberOf + ri:dn + true + + + + 5 + + + + + + http://prism.evolveum.com/xml/ns/public/matching-rule-3#distinguishedName + attributes/ri:dn + uid=idm,ou=Administrators,dc=example,dc=com + + + + + + + + + + + + + + + strong + + + + + + + + + + entitlement + ldapGroup + LDAP Group + ri:groupOfUniqueNames + + ri:dn + + + + $focus/name + + + + + + + + ri:uniqueMember + + + ri:cn + + weak + + $focus/name + + + + + name + + + + + ri:description + + + description + + + + + description + + + + + + + + + + + + ri:ds-pwp-account-disabled + + true + + + + + + + false + + + + + + Account sync + ri:inetOrgPerson + account + default + UserType + true + + + + c:employeeNumber + + declare namespace ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance/10000000-0000-0000-0000-000000000003"; + $shadow/attributes/ri:employeeNumber + + + + + c:employeeNumber + + + + + + + linked + true + + + deleted + + + + unlinked + + + + unmatched + + + + + + + Group sync + ri:groupOfUniqueNames + entitlement + ldapGroup + RoleType + true + + + http://prism.evolveum.com/xml/ns/public/matching-rule-3#polyStringNorm + c:name + + $shadow/attributes/cn + + + + + + linked + true + + + deleted + + + + unlinked + + + + unmatched + + + + + + \ No newline at end of file diff --git a/testing/conntest/testng-integration.xml b/testing/conntest/testng-integration.xml index 0aaaf2247e8..6f843db4989 100644 --- a/testing/conntest/testng-integration.xml +++ b/testing/conntest/testng-integration.xml @@ -23,6 +23,7 @@ +