From 34f4f3a64300da5644f3243dd33feea67b83a274 Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Mon, 3 Oct 2016 19:47:41 +0200 Subject: [PATCH 1/6] Support for name hint in ConnId Uid. Improved handling of secondary identifier in provisioning. (MID-2926) --- build-system/pom.xml | 4 +- .../dummy/connector/DummyConfiguration.java | 10 + .../icf/dummy/connector/DummyConnector.java | 122 +++++- .../evolveum/icf/dummy/connector/Utils.java | 8 +- .../icf/dummy/resource/DummyResource.java | 10 + .../impl/EntitlementConverter.java | 15 +- .../impl/ProvisioningServiceImpl.java | 3 +- .../impl/ResourceObjectConverter.java | 21 +- .../impl/ResourceObjectDiscriminator.java | 24 +- .../impl/ResourceObjectOperations.java | 9 + .../provisioning/impl/ShadowCache.java | 2 +- .../provisioning/impl/ShadowManager.java | 18 +- .../ucf/impl/ConnectorInstanceIcfImpl.java | 194 ++++----- .../provisioning/ucf/impl/IcfConvertor.java | 14 +- .../provisioning/ucf/impl/IcfUtil.java | 114 ++++-- .../provisioning/impl/dummy/TestDummy.java | 2 + .../impl/dummy-uuid/resource-dummy.xml | 3 +- .../src/test/resources/ucf/connector-ldap.xml | 2 +- .../midpoint/test/IntegrationTestTools.java | 1 + .../conntest/AbstractLdapConnTest.java | 1 + .../testing/conntest/TestOpenDjUnsafe.java | 39 ++ .../test/resources/opendj/resource-unsafe.xml | 373 ++++++++++++++++++ testing/conntest/testng-integration.xml | 1 + 23 files changed, 811 insertions(+), 179 deletions(-) create mode 100644 testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/TestOpenDjUnsafe.java create mode 100644 testing/conntest/src/test/resources/opendj/resource-unsafe.xml 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 @@ + From 86ba69e256429c38be86987cd8409c000b230653 Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Wed, 5 Oct 2016 19:48:38 +0200 Subject: [PATCH 2/6] Fixing handling of secondary identifiers (MID-2926) --- .../icf/dummy/connector/DummyConnector.java | 8 +-- .../schema/internals/InternalMonitor.java | 18 +++--- .../ResourceObjectIdentification.java | 63 +++++++++++++++++-- .../src/test/resources/logback-test.xml | 21 ++++--- .../impl/CommunicationExceptionHandler.java | 5 +- .../impl/ConfigurationExceptionHandler.java | 31 +++++++-- .../impl/ObjectNotFoundHandler.java | 17 ++++- .../impl/ResourceObjectConverter.java | 11 ++-- .../impl/ResourceObjectReferenceResolver.java | 8 ++- .../provisioning/impl/ShadowCache.java | 15 +++-- .../provisioning/impl/ShadowManager.java | 3 + .../ucf/impl/ConnectorInstanceIcfImpl.java | 43 +++++++++---- .../provisioning/impl/dummy/TestDummy.java | 8 ++- .../provisioning/impl/ucf/TestUcfOpenDj.java | 6 +- .../testing/consistency/ConsistencyTest.java | 7 +++ .../src/test/resources/logback-test.xml | 2 +- 16 files changed, 200 insertions(+), 66 deletions(-) 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 4f1e211758f..df61fee7842 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 @@ -1921,10 +1921,10 @@ public void validate(Uid uid) { } if (nameHintChecksEnabled()) { if (uid.getNameHint() == null) { - throw new IllegalArgumentException("Uid name hint must not be null."); + throw new InvalidAttributeValueException("Uid name hint must not be null."); } if (StringUtils.isBlank(uid.getNameHintValue())) { - throw new IllegalArgumentException("Uid name hint must not be empty."); + throw new InvalidAttributeValueException("Uid name hint must not be empty."); } } } @@ -1956,10 +1956,10 @@ 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); + throw new InvalidAttributeValueException("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); + throw new InvalidAttributeValueException("Uid name hint must not be empty in filter "+filter); } } return null; diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/internals/InternalMonitor.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/internals/InternalMonitor.java index 1a126af1146..36eb93da575 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/internals/InternalMonitor.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/internals/InternalMonitor.java @@ -72,7 +72,7 @@ public static long getResourceSchemaParseCount() { public synchronized static void recordResourceSchemaParse() { resourceSchemaParseCount++; if (traceShadowFetchOperation) { - traceOperation("resource schema parse", resourceSchemaParseCount); + traceOperation("resource schema parse", resourceSchemaParseCount, true); } } @@ -92,7 +92,7 @@ public synchronized static void recordResourceSchemaFetch() { resourceSchemaFetchCount++; provisioningAllExtOperationCount++; if (traceShadowFetchOperation) { - traceOperation("resource schema fetch", resourceSchemaFetchCount); + traceOperation("resource schema fetch", resourceSchemaFetchCount, true); } } @@ -153,7 +153,7 @@ public static void recordShadowFetchOperation() { shadowFetchOperationCount++; provisioningAllExtOperationCount++; if (traceShadowFetchOperation) { - traceOperation("shadow fetch", shadowFetchOperationCount); + traceOperation("shadow fetch", shadowFetchOperationCount, true); } } @@ -192,7 +192,7 @@ public static long getConnectorOperationCount() { public static void recordConnectorOperation(String name) { connectorOperationCount++; if (traceConnectorOperation) { - traceOperation("connector "+name, connectorOperationCount); + traceOperation("connector "+name, connectorOperationCount, true); } } @@ -203,7 +203,7 @@ public static long getConnectorSimulatedPagingSearchCount() { public static void recordConnectorSimulatedPagingSearchCount() { connectorSimulatedPagingSearchCount++; if (traceConnectorOperation) { - traceOperation("simulated paged search", connectorSimulatedPagingSearchCount); + traceOperation("simulated paged search", connectorSimulatedPagingSearchCount, true); } } @@ -258,7 +258,7 @@ public synchronized void afterObjectClone(PrismObject LOGGER.debug("MONITOR prism object clone end: {}", orig); } if (tracePrismObjectClone) { - traceOperation("prism object clone", prismObjectCloneCount); + traceOperation("prism object clone", prismObjectCloneCount, false); } } @@ -288,7 +288,7 @@ public static void reset() { connectorOperationCount = 0; } - private static void traceOperation(String opName, long counter) { + private static void traceOperation(String opName, long counter, boolean traceAndDebug) { LOGGER.info("MONITOR {} ({})", opName, counter); if (LOGGER.isDebugEnabled()) { StackTraceElement[] fullStack = Thread.currentThread().getStackTrace(); @@ -308,7 +308,9 @@ private static void traceOperation(String opName, long counter) { sb.append(stackElement.toString()); sb.append("\n"); } - LOGGER.debug("MONITOR {} ({}): {} {}", new Object[]{opName, counter, immediateClass, immediateMethod}); + if (traceAndDebug) { + LOGGER.debug("MONITOR {} ({}): {} {}", new Object[]{opName, counter, immediateClass, immediateMethod}); + } LOGGER.trace("MONITOR {} ({}):\n{}", new Object[]{opName, counter, sb}); } } diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/processor/ResourceObjectIdentification.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/processor/ResourceObjectIdentification.java index eaa233667db..2bee3b86837 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/processor/ResourceObjectIdentification.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/processor/ResourceObjectIdentification.java @@ -15,6 +15,8 @@ */ package com.evolveum.midpoint.schema.processor; +import java.io.Serializable; +import java.util.ArrayList; import java.util.Collection; import com.evolveum.midpoint.util.PrettyPrinter; @@ -24,8 +26,9 @@ * @author semancik * */ -public class ResourceObjectIdentification { - +public class ResourceObjectIdentification implements Serializable { + private static final long serialVersionUID = 1L; + private ObjectClassComplexTypeDefinition objectClassDefinition; private Collection> primaryIdentifiers; private Collection> secondaryIdentifiers; @@ -43,28 +46,28 @@ public Collection> getPrimaryIdentifiers() { return primaryIdentifiers; } - public ResourceAttribute getPrimaryIdentifier() throws SchemaException { + public ResourceAttribute getPrimaryIdentifier() throws SchemaException { if (primaryIdentifiers == null || primaryIdentifiers.isEmpty()) { return null; } if (primaryIdentifiers.size() > 1) { throw new SchemaException("More than one primary identifier in "+this); } - return primaryIdentifiers.iterator().next(); + return (ResourceAttribute) primaryIdentifiers.iterator().next(); } public Collection> getSecondaryIdentifiers() { return secondaryIdentifiers; } - public ResourceAttribute getSecondaryIdentifier() throws SchemaException { + public ResourceAttribute getSecondaryIdentifier() throws SchemaException { if (secondaryIdentifiers == null || secondaryIdentifiers.isEmpty()) { return null; } if (secondaryIdentifiers.size() > 1) { throw new SchemaException("More than one secondary identifier in "+this); } - return secondaryIdentifiers.iterator().next(); + return (ResourceAttribute) secondaryIdentifiers.iterator().next(); } @@ -72,6 +75,54 @@ public ObjectClassComplexTypeDefinition getObjectClassDefinition() { return objectClassDefinition; } + public static ResourceObjectIdentification create(ObjectClassComplexTypeDefinition objectClassDefinition, + Collection> allIdentifiers) throws SchemaException { + Collection> primaryIdentifiers = null; + Collection> secondaryIdentifiers = null; + for (ResourceAttribute identifier: allIdentifiers) { + if (objectClassDefinition.isPrimaryIdentifier(identifier.getElementName())) { + if (primaryIdentifiers == null) { + primaryIdentifiers = new ArrayList<>(); + } + ((Collection)primaryIdentifiers).add(identifier); + } else if (objectClassDefinition.isSecondaryIdentifier(identifier.getElementName())) { + if (secondaryIdentifiers == null) { + secondaryIdentifiers = new ArrayList<>(); + } + ((Collection)secondaryIdentifiers).add(identifier); + } else { + throw new SchemaException("Attribute "+identifier+" is neither primary not secondary identifier in object class "+objectClassDefinition); + } + } + return new ResourceObjectIdentification(objectClassDefinition, primaryIdentifiers, secondaryIdentifiers); + } + + public static ResourceObjectIdentification createFromAttributes(ObjectClassComplexTypeDefinition objectClassDefinition, + Collection> attributes) throws SchemaException { + Collection> primaryIdentifiers = null; + Collection> secondaryIdentifiers = null; + for (ResourceAttribute identifier: attributes) { + if (objectClassDefinition.isPrimaryIdentifier(identifier.getElementName())) { + if (primaryIdentifiers == null) { + primaryIdentifiers = new ArrayList<>(); + } + ((Collection)primaryIdentifiers).add(identifier); + } else if (objectClassDefinition.isSecondaryIdentifier(identifier.getElementName())) { + if (secondaryIdentifiers == null) { + secondaryIdentifiers = new ArrayList<>(); + } + ((Collection)secondaryIdentifiers).add(identifier); + } + } + return new ResourceObjectIdentification(objectClassDefinition, primaryIdentifiers, secondaryIdentifiers); + } + + public void validatePrimaryIdenfiers() { + if (primaryIdentifiers == null || primaryIdentifiers.isEmpty()) { + throw new IllegalStateException("No primary identifiers in " + this); + } + } + @Override public int hashCode() { final int prime = 31; diff --git a/model/model-intest/src/test/resources/logback-test.xml b/model/model-intest/src/test/resources/logback-test.xml index 997d32efa32..c1e26eb4089 100644 --- a/model/model-intest/src/test/resources/logback-test.xml +++ b/model/model-intest/src/test/resources/logback-test.xml @@ -36,8 +36,8 @@ - - + + @@ -47,7 +47,7 @@ - + @@ -72,15 +72,14 @@ + - - + + - - @@ -96,11 +95,13 @@ - + + + - - + + diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/consistency/impl/CommunicationExceptionHandler.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/consistency/impl/CommunicationExceptionHandler.java index 8a3258cffef..b660bf49a32 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/consistency/impl/CommunicationExceptionHandler.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/consistency/impl/CommunicationExceptionHandler.java @@ -113,7 +113,7 @@ public T handleError(T shadow, FailedOperation op, Except ResourceOperationDescription operationDescription = null; switch (op) { case ADD: - // if it is firt time, just store the whole account to the repo + // if it is first time, just store the whole account to the repo LOGGER.trace("Postponing ADD operation for {}", ObjectTypeUtil.toShortString(shadow)); ResourceType resource = shadow.getResource(); if (shadow.getFailedOperationType() == null) { @@ -137,6 +137,9 @@ public T handleError(T shadow, FailedOperation op, Except // It is needed for shadow creation during error recovery. String oid = cacheRepositoryService.addObject(shadow.asPrismObject(), null, operationResult); shadow.setOid(oid); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Stored new shadow for unfinished operation:\n{}", shadow.asPrismObject().debugDump(1)); + } // if it is seccond time ,just increade the attempt number } else { diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/consistency/impl/ConfigurationExceptionHandler.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/consistency/impl/ConfigurationExceptionHandler.java index b0f75b0e97d..41b87901466 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/consistency/impl/ConfigurationExceptionHandler.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/consistency/impl/ConfigurationExceptionHandler.java @@ -18,6 +18,7 @@ import java.util.Collection; +import org.jfree.util.Log; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @@ -38,11 +39,15 @@ import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType; @Component public class ConfigurationExceptionHandler extends ErrorHandler { + + private static final Trace LOGGER = TraceManager.getTrace(ConfigurationExceptionHandler.class); @Autowired @Qualifier("cacheRepositoryService") @@ -86,14 +91,14 @@ public T handleError(T shadow, FailedOperation op, Except return shadow; } - if (op != FailedOperation.GET){ -// Task task = taskManager.createTaskInstance(); - ResourceOperationDescription operationDescription = createOperationDescription(shadow, ex, shadow.getResource(), - delta, task, parentResult); - changeNotificationDispatcher.notifyFailure(operationDescription, task, parentResult); + if (op != FailedOperation.GET) { + // Task task = taskManager.createTaskInstance(); + ResourceOperationDescription operationDescription = createOperationDescription(shadow, ex, shadow.getResource(), + delta, task, parentResult); + changeNotificationDispatcher.notifyFailure(operationDescription, task, parentResult); } - if (shadow.getOid() == null){ + if (shadow.getOid() == null) { throw new ConfigurationException("Configuration error: "+ex.getMessage(), ex); } @@ -104,6 +109,20 @@ public T handleError(T shadow, FailedOperation op, Except modification, parentResult); } catch (Exception e) { //this should not happen. But if it happens, we should return original exception + LOGGER.error("Unexpected error while modifying shadow {}: {}", shadow, e.getMessage(), e); + if (ex instanceof SchemaException) { + throw ((SchemaException)ex); + } else if (ex instanceof GenericFrameworkException) { + throw ((GenericFrameworkException)ex); + } else if (ex instanceof CommunicationException) { + throw ((CommunicationException)ex); + } else if (ex instanceof ObjectNotFoundException) { + throw ((ObjectNotFoundException)ex); + } else if (ex instanceof ObjectAlreadyExistsException) { + throw ((ObjectAlreadyExistsException)ex); + } else if (ex instanceof ConfigurationException) { + throw ((ConfigurationException)ex); + } } parentResult.recordFatalError("Configuration error: " + ex.getMessage(), ex); diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/consistency/impl/ObjectNotFoundHandler.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/consistency/impl/ObjectNotFoundHandler.java index 05deeb84034..563fb1fb48a 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/consistency/impl/ObjectNotFoundHandler.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/consistency/impl/ObjectNotFoundHandler.java @@ -38,6 +38,8 @@ import com.evolveum.midpoint.provisioning.ucf.api.GenericFrameworkException; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.schema.DeltaConvertor; +import com.evolveum.midpoint.schema.GetOperationOptions; +import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; @@ -109,7 +111,7 @@ public T handleError(T shadow, FailedOperation op, Except result.addParam("exception", ex.getMessage()); } - LOGGER.trace("Start compensating object not found situation while execution operation: {}", op.name()); + LOGGER.trace("Start compensating object not found situation while execution operation: {}", op.name(), ex); // Task task = taskManager.createTaskInstance(); ObjectDelta delta = null; switch (op) { @@ -180,6 +182,7 @@ public T handleError(T shadow, FailedOperation op, Except try { ProvisioningOperationOptions options = new ProvisioningOperationOptions(); options.setCompletePostponed(false); + options.setDoDiscovery(false); provisioningService.modifyObject(ShadowType.class, oid, modifications, null, options, task, result); parentResult.recordHandledError( @@ -282,8 +285,18 @@ public T handleError(T shadow, FailedOperation op, Except PrismObject newShadow; try { LOGGER.trace("DISCOVERY: retrieving shadow {}", oid); - newShadow = provisioningService.getObject(shadow.getClass(), oid, null, task, result); + Collection> options = + SelectorOptions.createCollection(GetOperationOptions.createDoNotDiscovery()); + newShadow = provisioningService.getObject(shadow.getClass(), oid, options, task, result); LOGGER.trace("DISCOVERY: retrieved {}", newShadow); + } catch (ObjectNotFoundException e) { + String msg = "Strange thing did happen: new shadow ("+oid + +") was supposedly created for old shadow "+shadow+", however the new shadow was not found: " + +e.getMessage(); + LOGGER.error(msg); + result.recordFatalError(msg, e); + parentResult.recordFatalError(msg); + throw new ObjectNotFoundException(msg, ex); } finally { result.computeStatus(); } 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 aa89ce1656b..afb6fe9da01 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 @@ -393,7 +393,7 @@ public Collection> modifyResourceObject( Collection operations = new ArrayList(); Collection> identifiers = ShadowUtil.getAllIdentifiers(repoShadow); - + Collection> primaryIdentifiers = ShadowUtil.getPrimaryIdentifiers(repoShadow); if (ProvisioningUtil.isProtectedShadow(ctx.getObjectClassDefinition(), repoShadow, matchingRuleRegistry)) { if (hasChangesOnResource(itemDeltas)) { @@ -457,7 +457,7 @@ public Collection> modifyResourceObject( Collection sideEffectOperations = null; //check identifier if it is not null - if (identifiers.isEmpty() && repoShadow.asObjectable().getFailedOperationType()!= null){ + if (primaryIdentifiers.isEmpty() && repoShadow.asObjectable().getFailedOperationType()!= null){ throw new GenericConnectorException( "Unable to modify object in the resource. Probably it has not been created yet because of previous unavailability of the resource."); } @@ -575,7 +575,7 @@ private Collection executeModify(ProvisioningCont if (!ShadowUtil.hasPrimaryIdentifier(identifiers, objectClassDefinition)) { Collection> primaryIdentifiers = resourceObjectReferenceResolver.resolvePrimaryIdentifier(ctx, identifiers, "modification of resource object "+identifiers, parentResult); - if (primaryIdentifiers == null) { + if (primaryIdentifiers == null || primaryIdentifiers.isEmpty()) { throw new ObjectNotFoundException("Cannot find repository shadow for identifiers "+identifiers); } identifiers = primaryIdentifiers; @@ -1621,8 +1621,9 @@ public List> fetchChanges(ProvisioningContext ctx, PrismPrope if (ctx.isWildcard()) { if (!MiscUtil.equals(shadowAttrsToReturn, attrsToReturn)) { // re-fetch the shadow if necessary (if attributesToGet does not match) - ResourceObjectIdentification identification = new ResourceObjectIdentification(shadowCtx.getObjectClassDefinition(), - change.getIdentifiers(), null); + ResourceObjectIdentification identification = ResourceObjectIdentification.create(shadowCtx.getObjectClassDefinition(), + change.getIdentifiers()); + identification.validatePrimaryIdenfiers(); LOGGER.trace("Re-fetching object {} because of attrsToReturn", identification); currentShadow = connector.fetchObject(ShadowType.class, identification, shadowAttrsToReturn, ctx, parentResult); } diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectReferenceResolver.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectReferenceResolver.java index cbe385a3dc7..aba42c3db43 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectReferenceResolver.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectReferenceResolver.java @@ -56,6 +56,8 @@ import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SecurityViolationException; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceObjectReferenceResolutionFrequencyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceObjectReferenceType; @@ -70,6 +72,8 @@ @Component public class ResourceObjectReferenceResolver { + private static final Trace LOGGER = TraceManager.getTrace(ResourceObjectReferenceResolver.class); + @Autowired(required = true) private PrismContext prismContext; @@ -171,6 +175,7 @@ Collection> resolvePrimaryIdentifier(Provisioning primaryIdentifiers.add(primaryIdentifier); } } + LOGGER.trace("Resolved identifiers {} to primary identifiers {} (object class {})", identifiers, primaryIdentifiers, ocDef); return primaryIdentifiers; } @@ -190,7 +195,8 @@ public PrismObject fetchResourceObject(ProvisioningContext ctx, throw new UnsupportedOperationException("Resource does not support 'read' operation"); } - ResourceObjectIdentification identification = new ResourceObjectIdentification(objectClassDefinition, identifiers, null); + ResourceObjectIdentification identification = ResourceObjectIdentification.create(objectClassDefinition, identifiers); + identification.validatePrimaryIdenfiers(); return connector.fetchObject(ShadowType.class, identification, attributesToReturn, ctx, parentResult); } catch (ObjectNotFoundException e) { 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 903947a00e8..a24967700d4 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 @@ -253,24 +253,27 @@ public PrismObject getShadow(String oid, PrismObject rep PrismObject resourceShadow = null; try { - // Let's get all the identifiers from the Shadow part - Collection> identifiers = ShadowUtil - .getAllIdentifiers(repositoryShadow); + Collection> primaryIdentifiers = ShadowUtil + .getPrimaryIdentifiers(repositoryShadow); - if (identifiers == null || identifiers.isEmpty()) { + if (primaryIdentifiers == null || primaryIdentifiers.isEmpty()) { // check if the account is not only partially created (exist // only in repo so far) if (repositoryShadow.asObjectable().getFailedOperationType() != null) { throw new GenericConnectorException( "Unable to get object from the resource. Probably it has not been created yet because of previous unavailability of the resource."); } + // No identifiers found - SchemaException ex = new SchemaException("No identifiers found in the repository shadow " + SchemaException ex = new SchemaException("No primary identifiers found in the repository shadow " + repositoryShadow + " with respect to " + resource); parentResult.recordFatalError( - "No identifiers found in the repository shadow " + repositoryShadow, ex); + "No prmary identifiers found in the repository shadow " + repositoryShadow, ex); throw ex; } + + Collection> identifiers = ShadowUtil + .getAllIdentifiers(repositoryShadow); resourceShadow = resouceObjectConverter.getResourceObject(ctx, identifiers, true, parentResult); 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 5afb9ad818c..131c655f146 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 @@ -349,6 +349,9 @@ private List> lookupShadowsBySecondaryIdentifiers( MiscSchemaUtil.reduceSearchResult(results); LOGGER.trace("lookupShadow found {} objects", results.size()); + if (LOGGER.isTraceEnabled() && results.size() == 1) { + LOGGER.trace("lookupShadow found\n{}", results.get(0).debugDump(1)); + } return results; 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 106699170f5..d429491e9ba 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 @@ -1144,13 +1144,13 @@ public PrismObject fetchObject(Class type, Resource throws ObjectNotFoundException, CommunicationException, GenericFrameworkException, SchemaException, SecurityViolationException, ConfigurationException { - Collection> identifiers = resourceObjectIdentification.getPrimaryIdentifiers(); + Validate.notNull(resourceObjectIdentification, "Null primary identifiers"); ObjectClassComplexTypeDefinition objectClassDefinition = resourceObjectIdentification.getObjectClassDefinition(); // Result type for this operation OperationResult result = parentResult.createMinorSubresult(ConnectorInstance.class.getName() + ".fetchObject"); result.addParam("resourceObjectDefinition", objectClassDefinition); - result.addCollectionOfSerializablesAsParam("identifiers", identifiers); + result.addParam("identification", resourceObjectIdentification); result.addContext("connector", connectorType); if (connIdConnectorFacade == null) { @@ -1162,28 +1162,28 @@ public PrismObject fetchObject(Class type, Resource // Get UID from the set of identifiers Uid uid; try { - uid = getUid(objectClassDefinition, identifiers); + uid = getUid(resourceObjectIdentification); } catch (SchemaException e) { result.recordFatalError(e); throw e; } if (uid == null) { result.recordFatalError("Required attribute UID not found in identification set while attempting to fetch object identified by " - + identifiers + " from " + description); + + resourceObjectIdentification + " from " + description); throw new IllegalArgumentException( "Required attribute UID not found in identification set while attempting to fetch object identified by " - + identifiers + " from " + description); + + resourceObjectIdentification + " from " + description); } ObjectClass icfObjectClass = icfNameMapper.objectClassToIcf(objectClassDefinition, getSchemaNamespace(), connectorType, legacySchema); if (icfObjectClass == null) { result.recordFatalError("Unable to determine object class from QName " + objectClassDefinition.getTypeName() - + " while attempting to fetch object identified by " + identifiers + " from " + + " while attempting to fetch object identified by " + resourceObjectIdentification + " from " + description); throw new IllegalArgumentException("Unable to determine object class from QName " + objectClassDefinition.getTypeName() - + " while attempting to fetch object identified by " + identifiers + " from " + + " while attempting to fetch object identified by " + resourceObjectIdentification + " from " + description); } @@ -1217,7 +1217,7 @@ public PrismObject fetchObject(Class type, Resource throw ex; } catch (ObjectNotFoundException ex) { result.recordFatalError("Object not found"); - throw new ObjectNotFoundException("Object identified by " + identifiers + " (ConnId UID "+uid+"), objectClass " + objectClassDefinition.getTypeName() + " was not found in " + throw new ObjectNotFoundException("Object identified by " + resourceObjectIdentification + " (ConnId UID "+uid+"), objectClass " + objectClassDefinition.getTypeName() + " was not found in " + description); } catch (SchemaException ex) { result.recordFatalError(ex); @@ -1229,7 +1229,7 @@ public PrismObject fetchObject(Class type, Resource if (co == null) { result.recordFatalError("Object not found"); - throw new ObjectNotFoundException("Object identified by " + identifiers + " (ConnId UID "+uid+"), objectClass " + objectClassDefinition.getTypeName() + " was not in " + throw new ObjectNotFoundException("Object identified by " + resourceObjectIdentification + " (ConnId UID "+uid+"), objectClass " + objectClassDefinition.getTypeName() + " was not in " + description); } @@ -1593,8 +1593,8 @@ public Set modifyObject(ObjectClassComplexTypeDef } if (uid == null) { - result.recordFatalError("No UID in identifiers: " + identifiers); - throw new IllegalArgumentException("No UID in identifiers: " + identifiers); + result.recordFatalError("Cannot detemine UID from identifiers: " + identifiers); + throw new IllegalArgumentException("Cannot detemine UID from identifiers: " + identifiers); } String originalUid = uid.getUidValue(); @@ -2609,7 +2609,26 @@ private Filter convertFilterToIcf(ObjectQuery query, ObjectClassComplexTypeDefin // UTILITY METHODS - + private Uid getUid(ResourceObjectIdentification resourceObjectIdentification) throws SchemaException { + ResourceAttribute primaryIdentifier = resourceObjectIdentification.getPrimaryIdentifier(); + if (primaryIdentifier == null) { + return null; + } + String uidValue = primaryIdentifier.getRealValue(); + String nameValue = null; + Collection> secondaryIdentifiers = resourceObjectIdentification.getSecondaryIdentifiers(); + if (secondaryIdentifiers != null && secondaryIdentifiers.size() == 1) { + nameValue = (String) secondaryIdentifiers.iterator().next().getRealValue(); + } + if (uidValue != null) { + if (nameValue == null) { + return new Uid(uidValue); + } else { + return new Uid(uidValue, new Name(nameValue)); + } + } + return null; + } /** * Looks up ConnId Uid identifier in a (potentially multi-valued) set of 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 dc90c082090..b5677e3347f 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 @@ -1082,9 +1082,11 @@ public void test100AddAccount() throws Exception { display("Adding shadow", account); // WHEN + TestUtil.displayWhen(TEST_NAME); String addedObjectOid = provisioningService.addObject(account, null, null, task, result); // THEN + TestUtil.displayThen(TEST_NAME); result.computeStatus(); display("add object result", result); TestUtil.assertSuccess("addObject has failed (result)", result); @@ -1119,8 +1121,10 @@ public void test100AddAccount() throws Exception { ActivationType activationProvisioning = accountTypeProvisioning.getActivation(); if (supportsActivation()) { assertNotNull("No activation in "+accountProvisioning+" (provisioning)", activationProvisioning); - assertEquals("Wrong activation administrativeStatus in "+accountProvisioning+" (provisioning)", ActivationStatusType.ENABLED, activationProvisioning.getAdministrativeStatus()); - TestUtil.assertEqualsTimestamp("Wrong activation enableTimestamp in "+accountProvisioning+" (provisioning)", ACCOUNT_WILL_ENABLE_TIMESTAMP, activationProvisioning.getEnableTimestamp()); + assertEquals("Wrong activation administrativeStatus in "+accountProvisioning+" (provisioning)", + ActivationStatusType.ENABLED, activationProvisioning.getAdministrativeStatus()); + TestUtil.assertEqualsTimestamp("Wrong activation enableTimestamp in "+accountProvisioning+" (provisioning)", + ACCOUNT_WILL_ENABLE_TIMESTAMP, activationProvisioning.getEnableTimestamp()); } else { assertNull("Activation sneaked in (provisioning)", activationProvisioning); } diff --git a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/ucf/TestUcfOpenDj.java b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/ucf/TestUcfOpenDj.java index b1a70d6c8eb..d471fd9c567 100644 --- a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/ucf/TestUcfOpenDj.java +++ b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/ucf/TestUcfOpenDj.java @@ -332,7 +332,8 @@ public void test100AddDeleteObject() throws Exception { cc.deleteObject(accountDefinition, null, identifiers, null, result); - ResourceObjectIdentification identification = new ResourceObjectIdentification(accountDefinition, identifiers, null); + ResourceObjectIdentification identification = ResourceObjectIdentification.createFromAttributes( + accountDefinition, identifiers); PrismObject resObj = null; try { resObj = cc.fetchObject(ShadowType.class, identification, null, null, @@ -364,7 +365,8 @@ public void test110ChangeModifyObject() throws Exception { cc.modifyObject(accountDefinition, identifiers, changes, null, result); - ResourceObjectIdentification identification = new ResourceObjectIdentification(accountDefinition, identifiers, null); + ResourceObjectIdentification identification = ResourceObjectIdentification.createFromAttributes( + accountDefinition, identifiers); PrismObject shadow = cc.fetchObject(ShadowType.class, identification, null, null, result); ResourceAttributeContainer resObj = ShadowUtil.getAttributesContainer(shadow); diff --git a/testing/consistency-mechanism/src/test/java/com/evolveum/midpoint/testing/consistency/ConsistencyTest.java b/testing/consistency-mechanism/src/test/java/com/evolveum/midpoint/testing/consistency/ConsistencyTest.java index b0c65e99852..b86cb6a5229 100644 --- a/testing/consistency-mechanism/src/test/java/com/evolveum/midpoint/testing/consistency/ConsistencyTest.java +++ b/testing/consistency-mechanism/src/test/java/com/evolveum/midpoint/testing/consistency/ConsistencyTest.java @@ -1127,9 +1127,11 @@ public void test212AddModifyObjectCommunicationProblem() throws Exception { String accountOid = assertUserOneAccountRef(USER_E_OID); //WHEN + TestUtil.displayWhen(TEST_NAME); requestToExecuteChanges(REQUEST_ACCOUNT_MODIFY_COMMUNICATION_PROBLEM, accountOid, ShadowType.class, task, null, parentResult); //THEN + TestUtil.displayThen(TEST_NAME); checkPostponedAccountWithAttributes(accountOid, "e", "Jackkk", "e", "e", "emp4321", FailedOperationTypeType.ADD, false, task, parentResult); } @@ -1145,9 +1147,13 @@ public void test214ModifyObjectCommunicationProblem() throws Exception { String accountOid = assertUserOneAccountRef(USER_JACK_OID); Task task = taskManager.createTaskInstance(); + //WHEN + TestUtil.displayWhen(TEST_NAME); requestToExecuteChanges(REQUEST_ACCOUNT_MODIFY_COMMUNICATION_PROBLEM, accountOid, ShadowType.class, task, null, parentResult); + //THEN + TestUtil.displayThen(TEST_NAME); checkPostponedAccountBasic(accountOid, FailedOperationTypeType.MODIFY, true, parentResult); } @@ -2518,6 +2524,7 @@ private void assertAttributes(ShadowType shadow, String uid, String givenName, S private void checkPostponedAccountWithAttributes(String accountOid, String uid, String givenName, String sn, String cn, String employeeNumber, FailedOperationTypeType failedOperation, boolean modify, Task task, OperationResult parentResult) throws Exception{ ShadowType account = checkPostponedAccountWithAttributes(accountOid, uid, givenName, sn, cn, failedOperation, modify, task, parentResult); + display("account shadow (postponed operation)", account); assertAttribute(account, resourceTypeOpenDjrepo, "employeeNumber", employeeNumber); } diff --git a/testing/consistency-mechanism/src/test/resources/logback-test.xml b/testing/consistency-mechanism/src/test/resources/logback-test.xml index a4fb1369189..7c9a75582f1 100644 --- a/testing/consistency-mechanism/src/test/resources/logback-test.xml +++ b/testing/consistency-mechanism/src/test/resources/logback-test.xml @@ -31,7 +31,7 @@ - + From 2efd55e9b5f7d2c042de934c8407f0e376b8e0c9 Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Thu, 6 Oct 2016 18:41:46 +0200 Subject: [PATCH 3/6] Extended conntests and some provisioning fixes (secondary identifiers and rename, MID-2926) --- .../ResourceObjectIdentification.java | 9 +- .../impl/EntitlementConverter.java | 10 +- .../impl/ResourceObjectConverter.java | 17 +- .../impl/ResourceObjectReferenceResolver.java | 40 + .../AbstractAdLdapMultidomainTest.java | 1422 +++++++++++++++++ .../conntest/AbstractAdLdapRawTest.java | 1 + .../testing/conntest/AbstractAdLdapTest.java | 93 +- .../testing/conntest/AbstractLdapTest.java | 83 +- .../midpoint/testing/conntest/AdUtils.java | 137 ++ .../testing/conntest/TestAdLdapChimera.java | 66 + .../conntest/UserLdapConnectionConfig.java | 54 + .../ad-ldap-multidomain/resource-chimera.xml | 645 ++++++++ .../ad-ldap-multidomain/role-meta-org.xml | 54 + .../ad-ldap-multidomain/role-pirate.xml | 45 + .../ad-ldap-multidomain/role-submissive.xml | 31 + .../ad-ldap-multidomain/user-subman.xml | 44 + .../resources/common/system-configuration.xml | 6 +- .../src/test/resources/truststore.jks | Bin 2901 -> 6073 bytes 18 files changed, 2643 insertions(+), 114 deletions(-) create mode 100644 testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractAdLdapMultidomainTest.java create mode 100644 testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AdUtils.java create mode 100644 testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/TestAdLdapChimera.java create mode 100644 testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/UserLdapConnectionConfig.java create mode 100644 testing/conntest/src/test/resources/ad-ldap-multidomain/resource-chimera.xml create mode 100644 testing/conntest/src/test/resources/ad-ldap-multidomain/role-meta-org.xml create mode 100644 testing/conntest/src/test/resources/ad-ldap-multidomain/role-pirate.xml create mode 100644 testing/conntest/src/test/resources/ad-ldap-multidomain/role-submissive.xml create mode 100644 testing/conntest/src/test/resources/ad-ldap-multidomain/user-subman.xml diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/processor/ResourceObjectIdentification.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/processor/ResourceObjectIdentification.java index 2bee3b86837..60f5f030bf9 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/processor/ResourceObjectIdentification.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/processor/ResourceObjectIdentification.java @@ -77,6 +77,9 @@ public ObjectClassComplexTypeDefinition getObjectClassDefinition() { public static ResourceObjectIdentification create(ObjectClassComplexTypeDefinition objectClassDefinition, Collection> allIdentifiers) throws SchemaException { + if (allIdentifiers == null) { + throw new IllegalArgumentException("Cannot create ResourceObjectIdentification with null identifiers"); + } Collection> primaryIdentifiers = null; Collection> secondaryIdentifiers = null; for (ResourceAttribute identifier: allIdentifiers) { @@ -118,11 +121,15 @@ public static ResourceObjectIdentification createFromAttributes(ObjectClassCompl } public void validatePrimaryIdenfiers() { - if (primaryIdentifiers == null || primaryIdentifiers.isEmpty()) { + if (!hasPrimaryIdentifiers()) { throw new IllegalStateException("No primary identifiers in " + this); } } + public boolean hasPrimaryIdentifiers() { + return primaryIdentifiers != null && !primaryIdentifiers.isEmpty(); + } + @Override public int hashCode() { final int prime = 31; 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 3ed20b7406c..e8085b42f5e 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 @@ -199,8 +199,8 @@ private void postProcessEntitlementSubjectToEntitlement ResourceAttributeContainer identifiersContainer = new ResourceAttributeContainer( ShadowAssociationType.F_IDENTIFIERS, entitlementDef.toResourceAttributeContainerDefinition(), prismContext); associationCVal.add(identifiersContainer); - LOGGER.trace("Assocciation attribute value resolved to valueAtrribute {} and identifiers container {}", valueAttribute, identifiersContainer); identifiersContainer.add(valueAttribute); + LOGGER.trace("Assocciation attribute value resolved to valueAtrribute {} and identifiers container {}", valueAttribute, identifiersContainer); } } @@ -660,9 +660,9 @@ private PrismObject collectEntitlementAsObjectOperation(Prov ResourceAttributeContainer identifiersContainer = ShadowUtil.getAttributesContainer(associationCVal, ShadowAssociationType.F_IDENTIFIERS); - Collection> entitlementIdentifiers = identifiersContainer.getAttributes(); + Collection> entitlementIdentifiersFromAssociation = identifiersContainer.getAttributes(); - ResourceObjectDiscriminator disc = new ResourceObjectDiscriminator(entitlementOcDef.getTypeName(), entitlementIdentifiers); + ResourceObjectDiscriminator disc = new ResourceObjectDiscriminator(entitlementOcDef.getTypeName(), entitlementIdentifiersFromAssociation); ResourceObjectOperations operations = roMap.get(disc); if (operations == null) { operations = new ResourceObjectOperations(); @@ -741,8 +741,8 @@ private PrismObject collectEntitlementAsObjectOperation(Prov if (ResourceTypeUtil.isAvoidDuplicateValues(resource)) { PrismObject currentObjectShadow = operations.getCurrentShadow(); if (currentObjectShadow == null) { - LOGGER.trace("Fetching entitlement shadow {} to avoid value duplication (intent={})", entitlementIdentifiers, entitlementIntent); - currentObjectShadow = resourceObjectReferenceResolver.fetchResourceObject(entitlementCtx, entitlementIdentifiers, null, result); + LOGGER.trace("Fetching entitlement shadow {} to avoid value duplication (intent={})", entitlementIdentifiersFromAssociation, entitlementIntent); + currentObjectShadow = resourceObjectReferenceResolver.fetchResourceObject(entitlementCtx, entitlementIdentifiersFromAssociation, null, result); operations.setCurrentShadow(currentObjectShadow); } // TODO it seems that duplicate values are checked twice: once here and the second time in ResourceObjectConverter.executeModify 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 afb6fe9da01..cef3fcd58ff 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 @@ -922,9 +922,14 @@ private PrismObject executeEntitlementChangesModify(ProvisioningCont Map roMap = new HashMap<>(); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("executeEntitlementChangesModify, old shadow:\n{}", subjectShadowBefore.debugDump(1)); + } + for (ItemDelta subjectDelta : subjectDeltas) { + ItemPath subjectItemPath = subjectDelta.getPath(); - if (new ItemPath(ShadowType.F_ASSOCIATION).equivalent(subjectDelta.getPath())) { + if (new ItemPath(ShadowType.F_ASSOCIATION).equivalent(subjectItemPath)) { ContainerDelta containerDelta = (ContainerDelta)subjectDelta; subjectShadowAfter = entitlementConverter.collectEntitlementsAsObjectOperation(ctx, roMap, containerDelta, subjectShadowBefore, subjectShadowAfter, parentResult); @@ -934,9 +939,12 @@ private PrismObject executeEntitlementChangesModify(ProvisioningCont ContainerDelta associationDelta = ContainerDelta.createDelta(ShadowType.F_ASSOCIATION, subjectShadowBefore.getDefinition()); PrismContainer associationContainer = subjectShadowBefore.findContainer(ShadowType.F_ASSOCIATION); if (associationContainer == null || associationContainer.isEmpty()){ - LOGGER.trace("No shadow association container in old shadow. Skipping processing entitlements change."); + LOGGER.trace("No shadow association container in old shadow. Skipping processing entitlements change for {}.", subjectItemPath); continue; } + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Processing association container in old shadow for {}:\n{}", subjectItemPath, associationContainer.debugDump(1)); + } // Delete + re-add association values that should ensure correct functioning in case of rename // This has to be done only for associations that require explicit referential integrity. @@ -954,13 +962,16 @@ private PrismObject executeEntitlementChangesModify(ProvisioningCont continue; } QName valueAttributeName = associationDefinition.getResourceObjectAssociationType().getValueAttribute(); - if (!ShadowUtil.matchesAttribute(subjectDelta.getPath(), valueAttributeName)) { + if (!ShadowUtil.matchesAttribute(subjectItemPath, valueAttributeName)) { continue; } LOGGER.trace("Processing association {} on rename", associationName); associationDelta.addValuesToDelete(associationValue.clone()); associationDelta.addValuesToAdd(associationValue.clone()); } + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Resulting association delta for {}:\n{}", subjectItemPath, associationDelta.debugDump(1)); + } if (!associationDelta.isEmpty()) { entitlementConverter.collectEntitlementsAsObjectOperation(ctx, roMap, associationDelta, subjectShadowBefore, subjectShadowAfter, parentResult); } diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectReferenceResolver.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectReferenceResolver.java index aba42c3db43..c7e7452a7b3 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectReferenceResolver.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectReferenceResolver.java @@ -179,6 +179,45 @@ Collection> resolvePrimaryIdentifier(Provisioning return primaryIdentifiers; } + /** + * Resolve primary identifier from a collection of identifiers that may contain only secondary identifiers. + */ + private ResourceObjectIdentification resolvePrimaryIdentifiers(ProvisioningContext ctx, + ResourceObjectIdentification identification, OperationResult result) + throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, + SecurityViolationException { + if (identification == null) { + return identification; + } + if (identification.hasPrimaryIdentifiers()) { + return identification; + } + Collection> secondaryIdentifiers = (Collection>) identification.getSecondaryIdentifiers(); + PrismObject repoShadow = shadowManager.lookupShadowBySecondaryIdentifiers(ctx, secondaryIdentifiers, result); + if (repoShadow == null) { + // TODO: we should attempt resource search here + throw new ObjectNotFoundException("No repository shadow for "+secondaryIdentifiers+", cannot resolve identifiers"); + } + PrismContainer attributesContainer = repoShadow.findContainer(ShadowType.F_ATTRIBUTES); + if (attributesContainer == null) { + throw new SchemaException("No attributes in "+repoShadow+", cannot resolve identifiers "+secondaryIdentifiers); + } + RefinedObjectClassDefinition ocDef = ctx.getObjectClassDefinition(); + Collection primaryIdentifiers = new ArrayList<>(); + for (PrismProperty property: attributesContainer.getValue().getProperties()) { + if (ocDef.isPrimaryIdentifier(property.getElementName())) { + RefinedAttributeDefinition attrDef = ocDef.findAttributeDefinition(property.getElementName()); + ResourceAttribute primaryIdentifier = new ResourceAttribute<>(property.getElementName(), + attrDef, prismContext); + primaryIdentifier.setRealValue(property.getRealValue()); + primaryIdentifiers.add(primaryIdentifier); + } + } + LOGGER.trace("Resolved {} to primary identifiers {} (object class {})", identification, primaryIdentifiers, ocDef); + return new ResourceObjectIdentification(identification.getObjectClassDefinition(), primaryIdentifiers, + identification.getSecondaryIdentifiers()); + } + public PrismObject fetchResourceObject(ProvisioningContext ctx, Collection> identifiers, @@ -196,6 +235,7 @@ public PrismObject fetchResourceObject(ProvisioningContext ctx, } ResourceObjectIdentification identification = ResourceObjectIdentification.create(objectClassDefinition, identifiers); + identification = resolvePrimaryIdentifiers(ctx, identification, parentResult); identification.validatePrimaryIdenfiers(); return connector.fetchObject(ShadowType.class, identification, attributesToReturn, ctx, parentResult); diff --git a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractAdLdapMultidomainTest.java b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractAdLdapMultidomainTest.java new file mode 100644 index 00000000000..4f5dfe49e7c --- /dev/null +++ b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractAdLdapMultidomainTest.java @@ -0,0 +1,1422 @@ +/** + * Copyright (c) 2015-2016 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.testing.conntest; + +import static com.evolveum.midpoint.test.IntegrationTestTools.display; +import static com.evolveum.midpoint.testing.conntest.AdUtils.*; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Collection; +import java.util.List; + +import javax.xml.namespace.QName; + +import org.apache.commons.lang.StringUtils; +import org.apache.directory.api.ldap.model.cursor.CursorException; +import org.apache.directory.api.ldap.model.entry.DefaultEntry; +import org.apache.directory.api.ldap.model.entry.DefaultModification; +import org.apache.directory.api.ldap.model.entry.Entry; +import org.apache.directory.api.ldap.model.entry.Modification; +import org.apache.directory.api.ldap.model.entry.ModificationOperation; +import org.apache.directory.api.ldap.model.exception.LdapException; +import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException; +import org.apache.directory.api.ldap.model.name.Ava; +import org.apache.directory.api.ldap.model.name.Rdn; +import org.apache.directory.ldap.client.api.LdapNetworkConnection; +import org.testng.AssertJUnit; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import com.evolveum.midpoint.common.refinery.RefinedResourceSchema; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismProperty; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.prism.delta.PropertyDelta; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.query.ObjectPaging; +import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.prism.query.OrderDirection; +import com.evolveum.midpoint.prism.util.PrismAsserts; +import com.evolveum.midpoint.prism.util.PrismTestUtil; +import com.evolveum.midpoint.schema.SearchResultList; +import com.evolveum.midpoint.schema.SearchResultMetadata; +import com.evolveum.midpoint.schema.constants.MidPointConstants; +import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.schema.processor.ResourceAttribute; +import com.evolveum.midpoint.schema.processor.ResourceAttributeDefinition; +import com.evolveum.midpoint.schema.processor.ResourceSchema; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.MiscSchemaUtil; +import com.evolveum.midpoint.schema.util.ObjectQueryUtil; +import com.evolveum.midpoint.schema.util.ShadowUtil; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.AbstractIntegrationTest; +import com.evolveum.midpoint.test.IntegrationTestTools; +import com.evolveum.midpoint.test.util.MidPointTestConstants; +import com.evolveum.midpoint.test.util.TestUtil; +import com.evolveum.midpoint.util.DOMUtil; +import com.evolveum.midpoint.util.MiscUtil; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SystemException; +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.AssignmentType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; +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.PolyStringType; +import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; + +/** + * @author semancik + * + */ +@Listeners({com.evolveum.midpoint.tools.testng.AlphabeticalMethodInterceptor.class}) +public abstract class AbstractAdLdapMultidomainTest extends AbstractLdapTest { + + protected static final File TEST_DIR = new File(MidPointTestConstants.TEST_RESOURCES_DIR, "ad-ldap-multidomain"); + + protected static final File ROLE_PIRATES_FILE = new File(TEST_DIR, "role-pirate.xml"); + protected static final String ROLE_PIRATES_OID = "5dd034e8-41d2-11e5-a123-001e8c717e5b"; + + protected static final File ROLE_SUBMISSIVE_FILE = new File(TEST_DIR, "role-submissive.xml"); + protected static final String ROLE_SUBMISSIVE_OID = "0c0c81b2-d0a1-11e5-b51e-0309a826745e"; + + protected static final File ROLE_META_ORG_FILE = new File(TEST_DIR, "role-meta-org.xml"); + protected static final String ROLE_META_ORG_OID = "f2ad0ace-45d7-11e5-af54-001e8c717e5b"; + + public static final String ATTRIBUTE_OBJECT_GUID_NAME = "objectGUID"; + public static final String ATTRIBUTE_SAM_ACCOUNT_NAME_NAME = "sAMAccountName"; + public static final String ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME = "userAccountControl"; + public static final QName ATTRIBUTE_USER_ACCOUNT_CONTROL_QNAME = new QName(MidPointConstants.NS_RI, ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME); + public static final String ATTRIBUTE_UNICODE_PWD_NAME = "unicodePwd"; + + protected static final String ACCOUNT_JACK_SAM_ACCOUNT_NAME = "jack"; + protected static final String ACCOUNT_JACK_FULL_NAME = "Jack Sparrow"; + protected static final String ACCOUNT_JACK_PASSWORD = "qwe.123"; + + protected static final String USER_CPTBARBOSSA_FULL_NAME = "Captain Hector Barbossa"; + + private static final String GROUP_PIRATES_NAME = "pirates"; + private static final String GROUP_MELEE_ISLAND_NAME = "MΓͺlΓ©e Island"; + + protected static final int NUMBER_OF_ACCOUNTS = 7; + private static final String ASSOCIATION_GROUP_NAME = "group"; + + private static final String NS_EXTENSION = "http://whatever.com/my"; + private static final QName EXTENSION_SHOW_IN_ADVANCED_VIEW_ONLY_QNAME = new QName(NS_EXTENSION, "showInAdvancedViewOnly"); + + protected static final File USER_SUBMAN_FILE = new File(TEST_DIR, "user-subman.xml"); + private static final String USER_SUBMAN_OID ="910ac45a-8bd6-11e6-9122-ef88d95095f0"; + private static final String USER_SUBMAN_NAME = "subman"; + private static final String USER_SUBMAN_GIVEN_NAME = "Sub"; + private static final String USER_SUBMAN_FAMILY_NAME = "Man"; + private static final String USER_SUBMAN_FULL_NAME = "Sub Man"; + private static final String USER_SUBMAN_PASSWORD = "sub.123"; + + + private boolean allowDuplicateSearchResults = false; + + protected String jackAccountOid; + protected String groupPiratesOid; + protected long jackLockoutTimestamp; + protected String accountBarbossaOid; + protected String orgMeleeIslandOid; + protected String groupMeleeOid; + + private String accountSubmanOid; + + @Override + public String getStartSystemCommand() { + return null; + } + + @Override + public String getStopSystemCommand() { + return null; + } + + @Override + protected File getBaseDir() { + return TEST_DIR; + } + + @Override + protected String getSyncTaskOid() { + return "cd1e0ff2-0099-11e5-9e22-001e8c717e5b"; + } + + @Override + protected boolean useSsl() { + return true; + } + + @Override + protected String getLdapSuffix() { + return "DC=ad,DC=evolveum,DC=com"; + } + + @Override + protected String getLdapBindDn() { + return "CN=midpoint,CN=Users,DC=ad,DC=evolveum,DC=com"; + } + + @Override + protected String getLdapBindPassword() { + return "qwe.123"; + } + + @Override + protected int getSearchSizeLimit() { + return -1; + } + + @Override + public String getPrimaryIdentifierAttributeName() { + return "objectGUID"; + } + + @Override + protected String getPeopleLdapSuffix() { + return "CN=Users,"+getLdapSuffix(); + } + + @Override + protected String getGroupsLdapSuffix() { + return "CN=Users,"+getLdapSuffix(); + } + + protected String getLdapSubSuffix() { + return "DC=sub,DC=ad,DC=evolveum,DC=com"; + } + + protected String getPeopleLdapSubSuffix() { + return "CN=Users,"+getLdapSubSuffix(); + } + + @Override + protected String getLdapAccountObjectClass() { + return "user"; + } + + @Override + protected String getLdapGroupObjectClass() { + return "group"; + } + + @Override + protected String getLdapGroupMemberAttribute() { + return "member"; + } + + private QName getAssociationGroupQName() { + return new QName(MidPointConstants.NS_RI, ASSOCIATION_GROUP_NAME); + } + + @Override + protected boolean allowDuplicateSearchResults() { + return allowDuplicateSearchResults; + } + + @Override + protected boolean isGroupMemberMandatory() { + return false; + } + + private UserLdapConnectionConfig getSubLdapConnectionConfig() { + UserLdapConnectionConfig config = new UserLdapConnectionConfig(); + config.setLdapHost("hydra.ad.evolveum.com"); + config.setLdapPort(getLdapServerPort()); + config.setBindDn("CN=midpoint,CN=Users,DC=sub,DC=ad,DC=evolveum,DC=com"); + config.setBindPassword(getLdapBindPassword()); + config.setBaseContext(getLdapSubSuffix()); + return config; + } + + @Override + public void initSystem(Task initTask, OperationResult initResult) throws Exception { + super.initSystem(initTask, initResult); + + binaryAttributeDetector.addBinaryAttribute(ATTRIBUTE_OBJECT_GUID_NAME); + binaryAttributeDetector.addBinaryAttribute(ATTRIBUTE_UNICODE_PWD_NAME); + + // Users + repoAddObjectFromFile(USER_BARBOSSA_FILE, UserType.class, initResult); + repoAddObjectFromFile(USER_GUYBRUSH_FILE, UserType.class, initResult); + repoAddObjectFromFile(USER_SUBMAN_FILE, UserType.class, initResult); + + // Roles + repoAddObjectFromFile(ROLE_PIRATES_FILE, RoleType.class, initResult); + repoAddObjectFromFile(ROLE_SUBMISSIVE_FILE, RoleType.class, initResult); + repoAddObjectFromFile(ROLE_META_ORG_FILE, RoleType.class, initResult); + + } + + @Test + public void test000Sanity() throws Exception { + final String TEST_NAME = "test000Sanity"; + TestUtil.displayTestTile(this, TEST_NAME); + + assertLdapPassword(ACCOUNT_JACK_SAM_ACCOUNT_NAME, ACCOUNT_JACK_FULL_NAME, ACCOUNT_JACK_PASSWORD); + cleanupDelete(toAccountDn(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME)); + cleanupDelete(toAccountDn(USER_CPTBARBOSSA_USERNAME, USER_CPTBARBOSSA_FULL_NAME)); + cleanupDelete(toAccountDn(USER_GUYBRUSH_USERNAME, USER_GUYBRUSH_FULL_NAME)); + cleanupDelete(getSubLdapConnectionConfig(), toAccountSubDn(USER_SUBMAN_NAME, USER_SUBMAN_FULL_NAME)); + cleanupDelete(toGroupDn(GROUP_MELEE_ISLAND_NAME)); + } + + @Test + @Override + public void test020Schema() throws Exception { + final String TEST_NAME = "test020Schema"; + TestUtil.displayTestTile(this, TEST_NAME); + + accountObjectClassDefinition = AdUtils.assertAdSchema(resource, getAccountObjectClass(), prismContext); + + assertLdapConnectorInstances(1); + } + + @Test + public void test100SeachJackBySamAccountName() throws Exception { + final String TEST_NAME = "test100SeachJackBySamAccountName"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + ObjectQuery query = createSamAccountNameQuery(ACCOUNT_JACK_SAM_ACCOUNT_NAME); + + rememberConnectorOperationCount(); + rememberConnectorSimulatedPagingSearchCount(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + SearchResultList> shadows = modelService.searchObjects(ShadowType.class, query, null, task, result); + + // THEN + result.computeStatus(); + TestUtil.assertSuccess(result); + + assertEquals("Unexpected search result: "+shadows, 1, shadows.size()); + + PrismObject shadow = shadows.get(0); + display("Shadow", shadow); + assertAccountShadow(shadow, toAccountDn(ACCOUNT_JACK_SAM_ACCOUNT_NAME, ACCOUNT_JACK_FULL_NAME)); + jackAccountOid = shadow.getOid(); + +// assertConnectorOperationIncrement(2); + assertConnectorSimulatedPagingSearchIncrement(0); + + SearchResultMetadata metadata = shadows.getMetadata(); + if (metadata != null) { + assertFalse(metadata.isPartialResults()); + } + + assertLdapConnectorInstances(1); + } + + @Test + public void test105SeachPiratesByCn() throws Exception { + final String TEST_NAME = "test105SeachPiratesByCn"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + ObjectQuery query = ObjectQueryUtil.createResourceAndObjectClassQuery(getResourceOid(), getGroupObjectClass(), prismContext); + ObjectQueryUtil.filterAnd(query.getFilter(), createAttributeFilter("cn", GROUP_PIRATES_NAME)); + + rememberConnectorOperationCount(); + rememberConnectorSimulatedPagingSearchCount(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + SearchResultList> shadows = modelService.searchObjects(ShadowType.class, query, null, task, result); + + // THEN + result.computeStatus(); + TestUtil.assertSuccess(result); + + assertEquals("Unexpected search result: "+shadows, 1, shadows.size()); + + PrismObject shadow = shadows.get(0); + display("Shadow", shadow); + groupPiratesOid = shadow.getOid(); + +// assertConnectorOperationIncrement(1); + assertConnectorSimulatedPagingSearchIncrement(0); + + SearchResultMetadata metadata = shadows.getMetadata(); + if (metadata != null) { + assertFalse(metadata.isPartialResults()); + } + + assertLdapConnectorInstances(1); + } + + @Test + public void test110GetJack() throws Exception { + final String TEST_NAME = "test110GetJack"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + rememberConnectorOperationCount(); + rememberConnectorSimulatedPagingSearchCount(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + PrismObject shadow = modelService.getObject(ShadowType.class, jackAccountOid, null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + display("Shadow", shadow); + assertAccountShadow(shadow, toAccountDn(ACCOUNT_JACK_SAM_ACCOUNT_NAME, ACCOUNT_JACK_FULL_NAME)); + jackAccountOid = shadow.getOid(); + + IntegrationTestTools.assertAssociation(shadow, getAssociationGroupQName(), groupPiratesOid); + + assertAttribute(shadow, "dn", "CN=Jack Sparrow,CN=Users,DC=ad,DC=evolveum,DC=com"); + assertAttribute(shadow, "cn", ACCOUNT_JACK_FULL_NAME); + assertAttribute(shadow, "sn", "Sparrow"); + assertAttribute(shadow, "description", "The best pirate the world has ever seen"); + assertAttribute(shadow, "sAMAccountName", ACCOUNT_JACK_SAM_ACCOUNT_NAME); + assertAttribute(shadow, "lastLogon", 0L); + + assertConnectorOperationIncrement(1); + assertConnectorSimulatedPagingSearchIncrement(0); + + assertLdapConnectorInstances(1); + } + + + /** + * No paging. It should return all accounts. + */ + @Test + public void test150SeachAllAccounts() throws Exception { + final String TEST_NAME = "test150SeachAllAccounts"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + ObjectQuery query = ObjectQueryUtil.createResourceAndObjectClassQuery(getResourceOid(), getAccountObjectClass(), prismContext); + + rememberConnectorOperationCount(); + rememberConnectorSimulatedPagingSearchCount(); + + // WHEN + SearchResultList> searchResultList = doSearch(TEST_NAME, query, + NUMBER_OF_ACCOUNTS, task, result); + + // TODO: why 11? should be 1 +// assertConnectorOperationIncrement(11); + assertConnectorSimulatedPagingSearchIncrement(0); + + SearchResultMetadata metadata = searchResultList.getMetadata(); + if (metadata != null) { + assertFalse(metadata.isPartialResults()); + } + +// assertLdapConnectorInstances(2); + } + + /** + * This is in one block. + */ + @Test + public void test152SeachFirst2Accounts() throws Exception { + final String TEST_NAME = "test152SeachFirst2Accounts"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + ObjectQuery query = ObjectQueryUtil.createResourceAndObjectClassQuery(getResourceOid(), getAccountObjectClass(), prismContext); + + ObjectPaging paging = ObjectPaging.createEmptyPaging(); + paging.setMaxSize(2); + query.setPaging(paging); + + SearchResultList> searchResultList = doSearch(TEST_NAME, query, 2, task, result); + +// assertConnectorOperationIncrement(1); + assertConnectorSimulatedPagingSearchIncrement(0); + + SearchResultMetadata metadata = searchResultList.getMetadata(); + if (metadata != null) { + assertFalse(metadata.isPartialResults()); + } + +// assertLdapConnectorInstances(2); + } + +// /** +// * Blocksize is 5, so this gets more than two blocks. +// */ +// @Test +// public void test154SeachFirst11Accounts() throws Exception { +// final String TEST_NAME = "test154SeachFirst11Accounts"; +// TestUtil.displayTestTile(this, TEST_NAME); +// +// // GIVEN +// Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); +// OperationResult result = task.getResult(); +// +// ObjectQuery query = ObjectQueryUtil.createResourceAndObjectClassQuery(getResourceOid(), getAccountObjectClass(), prismContext); +// +// ObjectPaging paging = ObjectPaging.createEmptyPaging(); +// paging.setMaxSize(11); +// query.setPaging(paging); +// +// SearchResultList> searchResultList = doSearch(TEST_NAME, query, 11, task, result); +// +// assertConnectorOperationIncrement(1); +// assertConnectorSimulatedPagingSearchIncrement(0); +// +// SearchResultMetadata metadata = searchResultList.getMetadata(); +// if (metadata != null) { +// assertFalse(metadata.isPartialResults()); +// } +// +// assertLdapConnectorInstances(2); +// } +// +// @Test +// public void test162SeachFirst2AccountsOffset0() throws Exception { +// final String TEST_NAME = "test162SeachFirst2AccountsOffset0"; +// TestUtil.displayTestTile(this, TEST_NAME); +// +// // GIVEN +// Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); +// OperationResult result = task.getResult(); +// +// ObjectQuery query = ObjectQueryUtil.createResourceAndObjectClassQuery(getResourceOid(), getAccountObjectClass(), prismContext); +// +// ObjectPaging paging = ObjectPaging.createEmptyPaging(); +// paging.setOffset(0); +// paging.setMaxSize(2); +// query.setPaging(paging); +// +// SearchResultList> searchResultList = doSearch(TEST_NAME, query, 2, task, result); +// +// assertConnectorOperationIncrement(1); +// assertConnectorSimulatedPagingSearchIncrement(0); +// +// SearchResultMetadata metadata = searchResultList.getMetadata(); +// if (metadata != null) { +// assertFalse(metadata.isPartialResults()); +// } +// +// assertLdapConnectorInstances(2); +// } +// +// /** +// * Blocksize is 5, so this is in one block. +// * There is offset, so VLV should be used. +// * No explicit sorting. +// */ +// @Test +// public void test172Search2AccountsOffset1() throws Exception { +// final String TEST_NAME = "test172Search2AccountsOffset1"; +// TestUtil.displayTestTile(this, TEST_NAME); +// +// // GIVEN +// Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); +// OperationResult result = task.getResult(); +// +// ObjectQuery query = ObjectQueryUtil.createResourceAndObjectClassQuery(getResourceOid(), getAccountObjectClass(), prismContext); +// +// ObjectPaging paging = ObjectPaging.createPaging(1, 2); +// query.setPaging(paging); +// +// SearchResultList> searchResultList = doSearch(TEST_NAME, query, 2, task, result); +// +// assertConnectorOperationIncrement(1); +// assertConnectorSimulatedPagingSearchIncrement(0); +// +// SearchResultMetadata metadata = searchResultList.getMetadata(); +// if (metadata != null) { +// assertFalse(metadata.isPartialResults()); +// } +// +// assertLdapConnectorInstances(2); +// } +// +// /** +// * Blocksize is 5, so this gets more than two blocks. +// * There is offset, so VLV should be used. +// * No explicit sorting. +// */ +// @Test +// public void test174SeachFirst11AccountsOffset2() throws Exception { +// final String TEST_NAME = "test174SeachFirst11AccountsOffset2"; +// TestUtil.displayTestTile(this, TEST_NAME); +// +// // GIVEN +// Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); +// OperationResult result = task.getResult(); +// +// ObjectQuery query = ObjectQueryUtil.createResourceAndObjectClassQuery(getResourceOid(), getAccountObjectClass(), prismContext); +// +// ObjectPaging paging = ObjectPaging.createPaging(2, 11); +// query.setPaging(paging); +// +// allowDuplicateSearchResults = true; +// +// // WHEN +// SearchResultList> searchResultList = doSearch(TEST_NAME, query, 11, task, result); +// +// // THEN +// allowDuplicateSearchResults = false; +// +// assertConnectorOperationIncrement(1); +// assertConnectorSimulatedPagingSearchIncrement(0); +// +// SearchResultMetadata metadata = searchResultList.getMetadata(); +// if (metadata != null) { +// assertFalse(metadata.isPartialResults()); +// } +// +// assertLdapConnectorInstances(2); +// } +// +// /** +// * Blocksize is 5, so this is in one block. +// * There is offset, so VLV should be used. +// * Explicit sorting. +// */ +// @Test +// public void test182Search2AccountsOffset1SortCn() throws Exception { +// final String TEST_NAME = "test182Search2AccountsOffset1SortCn"; +// TestUtil.displayTestTile(this, TEST_NAME); +// +// // GIVEN +// Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); +// OperationResult result = task.getResult(); +// +// ObjectQuery query = ObjectQueryUtil.createResourceAndObjectClassQuery(getResourceOid(), getAccountObjectClass(), prismContext); +// +// ObjectPaging paging = ObjectPaging.createPaging(1, 2); +// paging.setOrdering(getAttributePath(resource, "cn"), OrderDirection.ASCENDING); +// query.setPaging(paging); +// +// SearchResultList> shadows = doSearch(TEST_NAME, query, 2, task, result); +// +// assertAccountShadow(shadows.get(0), "CN=Adalbert Meduza,OU=evolveum,DC=win,DC=evolveum,DC=com"); +// assertAccountShadow(shadows.get(1), "CN=Adalbert Meduza1,OU=evolveum,DC=win,DC=evolveum,DC=com"); +// +// assertConnectorOperationIncrement(1); +// assertConnectorSimulatedPagingSearchIncrement(0); +// +// SearchResultMetadata metadata = shadows.getMetadata(); +// if (metadata != null) { +// assertFalse(metadata.isPartialResults()); +// } +// +// assertLdapConnectorInstances(2); +// } + + @Test + public void test200AssignAccountBarbossa() throws Exception { + final String TEST_NAME = "test200AssignAccountBarbossa"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + long tsStart = System.currentTimeMillis(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + assignAccount(USER_BARBOSSA_OID, getResourceOid(), null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + long tsEnd = System.currentTimeMillis(); + + Entry entry = assertLdapAccount(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + assertAttribute(entry, "title", null); + + PrismObject user = getUser(USER_BARBOSSA_OID); + String shadowOid = getSingleLinkOid(user); + PrismObject shadow = getShadowModel(shadowOid); + display("Shadow (model)", shadow); + accountBarbossaOid = shadow.getOid(); + Collection> identifiers = ShadowUtil.getPrimaryIdentifiers(shadow); + String accountBarbossaIcfUid = (String) identifiers.iterator().next().getRealValue(); + assertNotNull("No identifier in "+shadow, accountBarbossaIcfUid); + + assertEquals("Wrong ICFS UID", + AdUtils.formatGuidToDashedNotation(MiscUtil.binaryToHex(entry.get(getPrimaryIdentifierAttributeName()).getBytes())), + accountBarbossaIcfUid); + + assertLdapPassword(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME, USER_BARBOSSA_PASSWORD); + + assertAttribute(entry, ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME, "512"); + + ResourceAttribute createTimestampAttribute = ShadowUtil.getAttribute(shadow, new QName(MidPointConstants.NS_RI, "createTimeStamp")); + assertNotNull("No createTimestamp in "+shadow, createTimestampAttribute); + Long createTimestamp = createTimestampAttribute.getRealValue(); + // LDAP server may be on a different host. Allow for some clock offset. + TestUtil.assertBetween("Wrong createTimestamp in "+shadow, roundTsDown(tsStart)-120000, roundTsUp(tsEnd)+120000, createTimestamp); + +// assertLdapConnectorInstances(2); + } + + @Test + public void test210ModifyAccountBarbossaTitle() throws Exception { + final String TEST_NAME = "test210ModifyAccountBarbossaTitle"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + ObjectDelta delta = ObjectDelta.createEmptyModifyDelta(ShadowType.class, accountBarbossaOid, prismContext); + QName attrQName = new QName(MidPointConstants.NS_RI, "title"); + ResourceAttributeDefinition attrDef = accountObjectClassDefinition.findAttributeDefinition(attrQName); + PropertyDelta attrDelta = PropertyDelta.createModificationReplaceProperty( + new ItemPath(ShadowType.F_ATTRIBUTES, attrQName), attrDef, "Captain"); + delta.addModification(attrDelta); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + modelService.executeChanges(MiscSchemaUtil.createCollection(delta), null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + assertAttribute(entry, "title", "Captain"); + assertAttribute(entry, ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME, "512"); + + PrismObject user = getUser(USER_BARBOSSA_OID); + String shadowOid = getSingleLinkOid(user); + assertEquals("Shadows have moved", accountBarbossaOid, shadowOid); + +// assertLdapConnectorInstances(2); + } + + @Test + public void test212ModifyAccountBarbossaShowInAdvancedViewOnlyTrue() throws Exception { + final String TEST_NAME = "test212ModifyAccountBarbossaShowInAdvancedViewOnlyTrue"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + ObjectDelta delta = ObjectDelta.createEmptyModifyDelta(ShadowType.class, accountBarbossaOid, prismContext); + QName attrQName = new QName(MidPointConstants.NS_RI, "showInAdvancedViewOnly"); + ResourceAttributeDefinition attrDef = accountObjectClassDefinition.findAttributeDefinition(attrQName); + PropertyDelta attrDelta = PropertyDelta.createModificationReplaceProperty( + new ItemPath(ShadowType.F_ATTRIBUTES, attrQName), attrDef, Boolean.TRUE); + delta.addModification(attrDelta); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + modelService.executeChanges(MiscSchemaUtil.createCollection(delta), null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + assertAttribute(entry, "showInAdvancedViewOnly", "TRUE"); + assertAttribute(entry, ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME, "512"); + + PrismObject user = getUser(USER_BARBOSSA_OID); + String shadowOid = getSingleLinkOid(user); + assertEquals("Shadows have moved", accountBarbossaOid, shadowOid); + +// assertLdapConnectorInstances(2); + } + + /** + * Modify USER, test boolean value mapping. + */ + @Test + public void test213ModifyUserBarbossaShowInAdvancedViewOnlyFalse() throws Exception { + final String TEST_NAME = "test213ModifyUserBarbossaShowInAdvancedViewOnlyFalse"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + + ObjectDelta delta = ObjectDelta.createEmptyModifyDelta(ShadowType.class, accountBarbossaOid, prismContext); + QName attrQName = new QName(MidPointConstants.NS_RI, "showInAdvancedViewOnly"); + ResourceAttributeDefinition attrDef = accountObjectClassDefinition.findAttributeDefinition(attrQName); + PropertyDelta attrDelta = PropertyDelta.createModificationReplaceProperty( + new ItemPath(ShadowType.F_ATTRIBUTES, attrQName), attrDef, Boolean.TRUE); + delta.addModification(attrDelta); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + modifyUserReplace(USER_BARBOSSA_OID, + new ItemPath(UserType.F_EXTENSION, EXTENSION_SHOW_IN_ADVANCED_VIEW_ONLY_QNAME), + task, result, Boolean.FALSE); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + assertAttribute(entry, "showInAdvancedViewOnly", "FALSE"); + assertAttribute(entry, ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME, "512"); + + PrismObject user = getUser(USER_BARBOSSA_OID); + String shadowOid = getSingleLinkOid(user); + assertEquals("Shadows have moved", accountBarbossaOid, shadowOid); + +// assertLdapConnectorInstances(2); + } + + @Test + public void test220ModifyUserBarbossaPassword() throws Exception { + final String TEST_NAME = "test220ModifyUserBarbossaPassword"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + ProtectedStringType userPasswordPs = new ProtectedStringType(); + userPasswordPs.setClearValue("here.There.Be.Monsters"); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + modifyUserReplace(USER_BARBOSSA_OID, + new ItemPath(UserType.F_CREDENTIALS, CredentialsType.F_PASSWORD, PasswordType.F_VALUE), + task, result, userPasswordPs); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + assertAttribute(entry, "title", "Captain"); + assertLdapPassword(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME, "here.There.Be.Monsters"); + assertAttribute(entry, ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME, "512"); + + PrismObject user = getUser(USER_BARBOSSA_OID); + String shadowOid = getSingleLinkOid(user); + assertEquals("Shadows have moved", accountBarbossaOid, shadowOid); + +// assertLdapConnectorInstances(2); + } + + @Test + public void test230DisableUserBarbossa() throws Exception { + final String TEST_NAME = "test230DisableUserBarbossa"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + modifyUserReplace(USER_BARBOSSA_OID, + new ItemPath(UserType.F_ACTIVATION, ActivationType.F_ADMINISTRATIVE_STATUS), + task, result, ActivationStatusType.DISABLED); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + +// assertLdapConnectorInstances(2); + + PrismObject user = getUser(USER_BARBOSSA_OID); + assertAdministrativeStatus(user, ActivationStatusType.DISABLED); + + Entry entry = assertLdapAccount(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + assertAttribute(entry, ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME, "514"); + + String shadowOid = getSingleLinkOid(user); + PrismObject shadow = getObject(ShadowType.class, shadowOid); + assertAccountDisabled(shadow); + + try { + assertLdapPassword(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME, "here.There.Be.Monsters"); + AssertJUnit.fail("Password authentication works, but it should fail"); + } catch (SecurityException e) { + // this is expected + } + +// assertLdapConnectorInstances(2); + } + + @Test + public void test239EnableUserBarbossa() throws Exception { + final String TEST_NAME = "test239EnableUserBarbossa"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + modifyUserReplace(USER_BARBOSSA_OID, + new ItemPath(UserType.F_ACTIVATION, ActivationType.F_ADMINISTRATIVE_STATUS), + task, result, ActivationStatusType.ENABLED); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + PrismObject user = getUser(USER_BARBOSSA_OID); + assertAdministrativeStatus(user, ActivationStatusType.ENABLED); + + Entry entry = assertLdapAccount(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + assertAttribute(entry, ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME, "512"); + + String shadowOid = getSingleLinkOid(user); + PrismObject shadow = getObject(ShadowType.class, shadowOid); + assertAccountEnabled(shadow); + +// assertLdapConnectorInstances(2); + } + + /** + * This should create account with a group. And disabled. + */ + @Test + public void test250AssignGuybrushPirates() throws Exception { + final String TEST_NAME = "test250AssignGuybrushPirates"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + modifyUserReplace(USER_GUYBRUSH_OID, + new ItemPath(UserType.F_ACTIVATION, ActivationType.F_ADMINISTRATIVE_STATUS), + task, result, ActivationStatusType.DISABLED); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + assignRole(USER_GUYBRUSH_OID, ROLE_PIRATES_OID, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_GUYBRUSH_USERNAME, USER_GUYBRUSH_FULL_NAME); + display("Entry", entry); + assertAttribute(entry, ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME, "514"); + + assertLdapGroupMember(entry, GROUP_PIRATES_NAME); + + PrismObject user = getUser(USER_GUYBRUSH_OID); + assertAdministrativeStatus(user, ActivationStatusType.DISABLED); + String shadowOid = getSingleLinkOid(user); + + PrismObject shadow = getObject(ShadowType.class, shadowOid); + IntegrationTestTools.assertAssociation(shadow, getAssociationGroupQName(), groupPiratesOid); + assertAccountDisabled(shadow); + +// assertLdapConnectorInstances(2); + } + + @Test + public void test255ModifyUserGuybrushPassword() throws Exception { + final String TEST_NAME = "test255ModifyUserGuybrushPassword"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + ProtectedStringType userPasswordPs = new ProtectedStringType(); + userPasswordPs.setClearValue("wanna.be.a.123"); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + modifyUserReplace(USER_GUYBRUSH_OID, + new ItemPath(UserType.F_CREDENTIALS, CredentialsType.F_PASSWORD, PasswordType.F_VALUE), + task, result, userPasswordPs); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_GUYBRUSH_USERNAME, USER_GUYBRUSH_FULL_NAME); + assertAttribute(entry, ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME, "514"); + + try { + assertLdapPassword(USER_GUYBRUSH_USERNAME, USER_GUYBRUSH_FULL_NAME, "wanna.be.a.123"); + AssertJUnit.fail("Password authentication works, but it should fail"); + } catch (SecurityException e) { + // this is expected, account is disabled + } + +// assertLdapConnectorInstances(2); + } + + @Test + public void test260EnableGyubrush() throws Exception { + final String TEST_NAME = "test260EnableGyubrush"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + modifyUserReplace(USER_GUYBRUSH_OID, + new ItemPath(UserType.F_ACTIVATION, ActivationType.F_ADMINISTRATIVE_STATUS), + task, result, ActivationStatusType.ENABLED); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + PrismObject user = getUser(USER_GUYBRUSH_OID); + assertAdministrativeStatus(user, ActivationStatusType.ENABLED); + + Entry entry = assertLdapAccount(USER_GUYBRUSH_USERNAME, USER_GUYBRUSH_FULL_NAME); + assertAttribute(entry, ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME, "512"); + + String shadowOid = getSingleLinkOid(user); + PrismObject shadow = getObject(ShadowType.class, shadowOid); + assertAccountEnabled(shadow); + + assertLdapPassword(USER_GUYBRUSH_USERNAME, USER_GUYBRUSH_FULL_NAME, "wanna.be.a.123"); + +// assertLdapConnectorInstances(2); + } + + @Test + public void test300AssignBarbossaPirates() throws Exception { + final String TEST_NAME = "test300AssignBarbossaPirates"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + assignRole(USER_BARBOSSA_OID, ROLE_PIRATES_OID, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + display("Entry", entry); + assertAttribute(entry, "title", "Captain"); + + assertLdapGroupMember(entry, GROUP_PIRATES_NAME); + + PrismObject user = getUser(USER_BARBOSSA_OID); + String shadowOid = getSingleLinkOid(user); + assertEquals("Shadows have moved", accountBarbossaOid, shadowOid); + + PrismObject shadow = getObject(ShadowType.class, shadowOid); + IntegrationTestTools.assertAssociation(shadow, getAssociationGroupQName(), groupPiratesOid); + +// assertLdapConnectorInstances(2); + } + + @Test + public void test390ModifyUserBarbossaRename() throws Exception { + final String TEST_NAME = "test390ModifyUserBarbossaRename"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + ObjectDelta objectDelta = createModifyUserReplaceDelta(USER_BARBOSSA_OID, UserType.F_NAME, + PrismTestUtil.createPolyString(USER_CPTBARBOSSA_USERNAME)); + objectDelta.addModificationReplaceProperty(UserType.F_FULL_NAME, + PrismTestUtil.createPolyString(USER_CPTBARBOSSA_FULL_NAME)); + Collection> deltas = MiscSchemaUtil.createCollection(objectDelta); + + + // WHEN + TestUtil.displayWhen(TEST_NAME); + modelService.executeChanges(deltas, null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_CPTBARBOSSA_USERNAME, USER_CPTBARBOSSA_FULL_NAME); + assertAttribute(entry, "title", "Captain"); + + PrismObject user = getUser(USER_BARBOSSA_OID); + String shadowOid = getSingleLinkOid(user); + assertEquals("Shadows have moved", accountBarbossaOid, shadowOid); + PrismObject shadow = getObject(ShadowType.class, shadowOid); + display("Shadow after rename (model)", shadow); + + PrismObject repoShadow = repositoryService.getObject(ShadowType.class, shadowOid, null, result); + display("Shadow after rename (repo)", repoShadow); + + assertNoLdapAccount(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + +// assertLdapConnectorInstances(2); + } + + // TODO: create account with a group membership + + + @Test + public void test395UnAssignBarbossaPirates() throws Exception { + final String TEST_NAME = "test395UnAssignBarbossaPirates"; + TestUtil.displayTestTile(this, TEST_NAME); + + // TODO: do this on another account. There is a bad interference with rename. + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + unassignRole(USER_BARBOSSA_OID, ROLE_PIRATES_OID, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_CPTBARBOSSA_USERNAME, USER_CPTBARBOSSA_FULL_NAME); + display("Entry", entry); + assertAttribute(entry, "title", "Captain"); + + assertLdapNoGroupMember(entry, GROUP_PIRATES_NAME); + + PrismObject user = getUser(USER_BARBOSSA_OID); + String shadowOid = getSingleLinkOid(user); + assertEquals("Shadows have moved", accountBarbossaOid, shadowOid); + + PrismObject shadow = getObject(ShadowType.class, shadowOid); + IntegrationTestTools.assertNoAssociation(shadow, getAssociationGroupQName(), groupPiratesOid); + +// assertLdapConnectorInstances(2); + } + + @Test + public void test399UnAssignAccountBarbossa() throws Exception { + final String TEST_NAME = "test399UnAssignAccountBarbossa"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + unassignAccount(USER_BARBOSSA_OID, getResourceOid(), null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + assertNoLdapAccount(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME); + assertNoLdapAccount(USER_CPTBARBOSSA_USERNAME, USER_CPTBARBOSSA_FULL_NAME); + + PrismObject user = getUser(USER_BARBOSSA_OID); + assertNoLinkedAccount(user); + +// assertLdapConnectorInstances(2); + } + + @Test + public void test500AddOrgMeleeIsland() throws Exception { + final String TEST_NAME = "test500AddOrgMeleeIsland"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + PrismObject org = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(OrgType.class).instantiate(); + OrgType orgType = org.asObjectable(); + orgType.setName(new PolyStringType(GROUP_MELEE_ISLAND_NAME)); + AssignmentType metaroleAssignment = new AssignmentType(); + ObjectReferenceType metaroleRef = new ObjectReferenceType(); + metaroleRef.setOid(ROLE_META_ORG_OID); + metaroleRef.setType(RoleType.COMPLEX_TYPE); + metaroleAssignment.setTargetRef(metaroleRef); + orgType.getAssignment().add(metaroleAssignment); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + addObject(org, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + orgMeleeIslandOid = org.getOid(); + Entry entry = assertLdapGroup(GROUP_MELEE_ISLAND_NAME); + + org = getObject(OrgType.class, orgMeleeIslandOid); + groupMeleeOid = getSingleLinkOid(org); + PrismObject shadow = getShadowModel(groupMeleeOid); + display("Shadow (model)", shadow); + +// assertLdapConnectorInstances(2); + } + + @Test + public void test510AssignGuybrushMeleeIsland() throws Exception { + final String TEST_NAME = "test510AssignGuybrushMeleeIsland"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + assignOrg(USER_GUYBRUSH_OID, orgMeleeIslandOid, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + Entry entry = assertLdapAccount(USER_GUYBRUSH_USERNAME, USER_GUYBRUSH_FULL_NAME); + + PrismObject user = getUser(USER_GUYBRUSH_OID); + String shadowOid = getSingleLinkOid(user); + PrismObject shadow = getShadowModel(shadowOid); + display("Shadow (model)", shadow); + + assertLdapGroupMember(entry, GROUP_MELEE_ISLAND_NAME); + + IntegrationTestTools.assertAssociation(shadow, getAssociationGroupQName(), groupMeleeOid); + +// assertLdapConnectorInstances(2); + } + + @Test + public void test600AssignAccountSubman() throws Exception { + final String TEST_NAME = "test600AssignAccountSubman"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(this.getClass().getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + long tsStart = System.currentTimeMillis(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + assignRole(USER_SUBMAN_OID, ROLE_SUBMISSIVE_OID, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + long tsEnd = System.currentTimeMillis(); + + Entry entry = assertLdapSubAccount(USER_SUBMAN_NAME, USER_SUBMAN_FULL_NAME); + display("Sub entry", entry); + assertAttribute(entry, "title", null); + + PrismObject userAfter = getUser(USER_SUBMAN_OID); + String shadowOid = getSingleLinkOid(userAfter); + PrismObject shadow = getShadowModel(shadowOid); + display("Shadow (model)", shadow); + accountSubmanOid = shadow.getOid(); + Collection> identifiers = ShadowUtil.getPrimaryIdentifiers(shadow); + String accountBarbossaIcfUid = (String) identifiers.iterator().next().getRealValue(); + assertNotNull("No identifier in "+shadow, accountBarbossaIcfUid); + + assertEquals("Wrong ICFS UID", + AdUtils.formatGuidToDashedNotation(MiscUtil.binaryToHex(entry.get(getPrimaryIdentifierAttributeName()).getBytes())), + accountBarbossaIcfUid); + + assertLdapPassword(getSubLdapConnectionConfig(), USER_SUBMAN_NAME, USER_SUBMAN_FULL_NAME, USER_SUBMAN_PASSWORD); + + assertAttribute(entry, ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME, "512"); + + ResourceAttribute createTimestampAttribute = ShadowUtil.getAttribute(shadow, new QName(MidPointConstants.NS_RI, "createTimeStamp")); + assertNotNull("No createTimestamp in "+shadow, createTimestampAttribute); + Long createTimestamp = createTimestampAttribute.getRealValue(); + // LDAP server may be on a different host. Allow for some clock offset. + TestUtil.assertBetween("Wrong createTimestamp in "+shadow, roundTsDown(tsStart)-120000, roundTsUp(tsEnd)+120000, createTimestamp); + +// assertLdapConnectorInstances(2); + } + + @Override + protected void assertAccountShadow(PrismObject shadow, String dn) throws SchemaException { + super.assertAccountShadow(shadow, dn); + ResourceAttribute primaryIdAttr = ShadowUtil.getAttribute(shadow, getPrimaryIdentifierAttributeQName()); + assertNotNull("No primary identifier ("+getPrimaryIdentifierAttributeQName()+" in "+shadow, primaryIdAttr); + String primaryId = primaryIdAttr.getRealValue(); + assertTrue("Unexpected chars in primary ID: '"+primaryId+"'", primaryId.matches("[a-z0-9\\-]+")); + } + + @Override + protected Entry assertLdapAccount(String samAccountName, String cn) throws LdapException, IOException, CursorException { + Entry entry = searchLdapAccount("(cn="+cn+")"); + assertAttribute(entry, "cn", cn); + assertAttribute(entry, ATTRIBUTE_SAM_ACCOUNT_NAME_NAME, samAccountName); + return entry; + } + + protected Entry assertLdapSubAccount(String samAccountName, String cn) throws LdapException, IOException, CursorException { + Entry entry = searchLdapAccount(getSubLdapConnectionConfig(), "(cn="+cn+")"); + assertAttribute(entry, "cn", cn); + assertAttribute(entry, ATTRIBUTE_SAM_ACCOUNT_NAME_NAME, samAccountName); + return entry; + } + + @Override + protected void assertNoLdapAccount(String uid) throws LdapException, IOException, CursorException { + throw new UnsupportedOperationException("Boom! Cannot do this here. This is bloody AD! We need full name!"); + } + + protected void assertNoLdapAccount(String uid, String cn) throws LdapException, IOException, CursorException { + LdapNetworkConnection connection = ldapConnect(); + List entriesCn = ldapSearch(connection, "(cn="+cn+")"); + List entriesSamAccountName = ldapSearch(connection, "(sAMAccountName="+uid+")"); + ldapDisconnect(connection); + + assertEquals("Unexpected number of entries for cn="+cn+": "+entriesCn, 0, entriesCn.size()); + assertEquals("Unexpected number of entries for sAMAccountName="+uid+": "+entriesSamAccountName, 0, entriesSamAccountName.size()); + } + + @Override + protected String toAccountDn(String username) { + throw new UnsupportedOperationException("Boom! Cannot do this here. This is bloody AD! We need full name!"); + } + + @Override + protected String toAccountDn(String username, String fullName) { + return ("CN="+fullName+","+getPeopleLdapSuffix()); + } + + protected String toAccountSubDn(String username, String fullName) { + return ("CN="+fullName+","+getPeopleLdapSubSuffix()); + } + + @Override + protected Rdn toAccountRdn(String username, String fullName) { + try { + return new Rdn(new Ava("CN", fullName)); + } catch (LdapInvalidDnException e) { + throw new IllegalStateException(e.getMessage(),e); + } + } + + protected void assertLdapPassword(String uid, String fullName, String password) throws LdapException, IOException, CursorException { + assertLdapPassword(null, uid, fullName, password); + } + + protected void assertLdapPassword(UserLdapConnectionConfig config, String uid, String fullName, String password) throws LdapException, IOException, CursorException { + Entry entry = getLdapAccountByCn(config, fullName); + assertLdapPassword(config, entry, password); + } + + protected void assertLdapPassword(String uid, String password) throws LdapException, IOException, CursorException { + throw new UnsupportedOperationException("Boom! Cannot do this here. This is bloody AD! We need full name!"); + } + + protected ObjectQuery createSamAccountNameQuery(String samAccountName) throws SchemaException { + ObjectQuery query = ObjectQueryUtil.createResourceAndObjectClassQuery(getResourceOid(), getAccountObjectClass(), prismContext); + ObjectQueryUtil.filterAnd(query.getFilter(), createAttributeFilter(ATTRIBUTE_SAM_ACCOUNT_NAME_NAME, samAccountName)); + return query; + } + + @Override + protected Entry createAccountEntry(String uid, String cn, String givenName, String sn) throws LdapException { + byte[] password = encodePassword("Secret.123"); + Entry entry = new DefaultEntry(toAccountDn(uid, cn), + "objectclass", getLdapAccountObjectClass(), + ATTRIBUTE_SAM_ACCOUNT_NAME_NAME, uid, + "cn", cn, + "givenName", givenName, + "sn", sn, + ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME, "512", + ATTRIBUTE_UNICODE_PWD_NAME, password); + return entry; + } + + private byte[] encodePassword(String password) { + String quotedPassword = "\"" + password + "\""; + try { + return quotedPassword.getBytes("UTF-16LE"); + } catch (UnsupportedEncodingException e) { + throw new SystemException(e.getMessage(), e); + } + } + + public void assertAttribute(PrismObject shadow, String attrName, T... expectedValues) { + assertAttribute(shadow, new QName(getResourceNamespace(), attrName), expectedValues); + } + + public void assertAttribute(PrismObject shadow, QName attrQname, T... expectedValues) { + List actualValues = ShadowUtil.getAttributeValues(shadow, attrQname); + PrismAsserts.assertSets("attribute "+attrQname+" in " + shadow, actualValues, expectedValues); + } + + protected abstract void assertAccountDisabled(PrismObject shadow); + + protected abstract void assertAccountEnabled(PrismObject shadow); +} diff --git a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractAdLdapRawTest.java b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractAdLdapRawTest.java index a06b6f43daf..8cadc46b212 100644 --- a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractAdLdapRawTest.java +++ b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractAdLdapRawTest.java @@ -16,6 +16,7 @@ package com.evolveum.midpoint.testing.conntest; import static com.evolveum.midpoint.test.IntegrationTestTools.display; +import static com.evolveum.midpoint.testing.conntest.AdUtils.*; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; diff --git a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractAdLdapTest.java b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractAdLdapTest.java index 96df201f69c..1d46faeaef4 100644 --- a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractAdLdapTest.java +++ b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractAdLdapTest.java @@ -16,6 +16,7 @@ package com.evolveum.midpoint.testing.conntest; import static com.evolveum.midpoint.test.IntegrationTestTools.display; +import static com.evolveum.midpoint.testing.conntest.AdUtils.*; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; @@ -106,11 +107,7 @@ public abstract class AbstractAdLdapTest extends AbstractLdapSynchronizationTest protected static final File ROLE_META_ORG_FILE = new File(TEST_DIR, "role-meta-org.xml"); protected static final String ROLE_META_ORG_OID = "f2ad0ace-45d7-11e5-af54-001e8c717e5b"; - public static final String ATTRIBUTE_OBJECT_GUID_NAME = "objectGUID"; - public static final String ATTRIBUTE_SAM_ACCOUNT_NAME_NAME = "sAMAccountName"; - public static final String ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME = "userAccountControl"; - public static final QName ATTRIBUTE_USER_ACCOUNT_CONTROL_QNAME = new QName(MidPointConstants.NS_RI, ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME); - public static final String ATTRIBUTE_UNICODE_PWD_NAME = "unicodePwd"; + protected static final String ACCOUNT_JACK_SAM_ACCOUNT_NAME = "jack"; protected static final String ACCOUNT_JACK_FULL_NAME = "Jack Sparrow"; @@ -268,62 +265,7 @@ public void test020Schema() throws Exception { final String TEST_NAME = "test020Schema"; TestUtil.displayTestTile(this, TEST_NAME); - ResourceSchema resourceSchema = RefinedResourceSchema.getResourceSchema(resource, prismContext); - display("Resource schema", resourceSchema); - - RefinedResourceSchema refinedSchema = RefinedResourceSchema.getRefinedSchema(resource); - display("Refined schema", refinedSchema); - accountObjectClassDefinition = refinedSchema.findObjectClassDefinition(getAccountObjectClass()); - assertNotNull("No definition for object class "+getAccountObjectClass(), accountObjectClassDefinition); - display("Account object class def", accountObjectClassDefinition); - - ResourceAttributeDefinition cnDef = accountObjectClassDefinition.findAttributeDefinition("cn"); - PrismAsserts.assertDefinition(cnDef, new QName(MidPointConstants.NS_RI, "cn"), DOMUtil.XSD_STRING, 1, 1); - assertTrue("cn read", cnDef.canRead()); - assertTrue("cn modify", cnDef.canModify()); - assertTrue("cn add", cnDef.canAdd()); - - ResourceAttributeDefinition samAccountNameDef = accountObjectClassDefinition.findAttributeDefinition(ATTRIBUTE_SAM_ACCOUNT_NAME_NAME); - PrismAsserts.assertDefinition(samAccountNameDef, - new QName(MidPointConstants.NS_RI, ATTRIBUTE_SAM_ACCOUNT_NAME_NAME), DOMUtil.XSD_STRING, 0, 1); - assertTrue("samAccountNameDef read", samAccountNameDef.canRead()); - assertTrue("samAccountNameDef modify", samAccountNameDef.canModify()); - assertTrue("samAccountNameDef add", samAccountNameDef.canAdd()); - - - ResourceAttributeDefinition oDef = accountObjectClassDefinition.findAttributeDefinition("o"); - PrismAsserts.assertDefinition(oDef, new QName(MidPointConstants.NS_RI, "o"), DOMUtil.XSD_STRING, 0, -1); - assertTrue("o read", oDef.canRead()); - assertTrue("o modify", oDef.canModify()); - assertTrue("o add", oDef.canAdd()); - - ResourceAttributeDefinition createTimestampDef = accountObjectClassDefinition.findAttributeDefinition("createTimeStamp"); - PrismAsserts.assertDefinition(createTimestampDef, new QName(MidPointConstants.NS_RI, "createTimeStamp"), - DOMUtil.XSD_LONG, 0, 1); - assertTrue("createTimeStampDef read", createTimestampDef.canRead()); - assertFalse("createTimeStampDef modify", createTimestampDef.canModify()); - assertFalse("createTimeStampDef add", createTimestampDef.canAdd()); - - ResourceAttributeDefinition isCriticalSystemObjectDef = accountObjectClassDefinition.findAttributeDefinition("isCriticalSystemObject"); - PrismAsserts.assertDefinition(isCriticalSystemObjectDef, new QName(MidPointConstants.NS_RI, "isCriticalSystemObject"), - DOMUtil.XSD_BOOLEAN, 0, 1); - assertTrue("isCriticalSystemObject read", isCriticalSystemObjectDef.canRead()); - assertTrue("isCriticalSystemObject modify", isCriticalSystemObjectDef.canModify()); - assertTrue("isCriticalSystemObject add", isCriticalSystemObjectDef.canAdd()); - - ResourceAttributeDefinition nTSecurityDescriptorDef = accountObjectClassDefinition.findAttributeDefinition("nTSecurityDescriptor"); - PrismAsserts.assertDefinition(nTSecurityDescriptorDef, new QName(MidPointConstants.NS_RI, "nTSecurityDescriptor"), - DOMUtil.XSD_BASE64BINARY, 0, 1); - assertTrue("nTSecurityDescriptor read", nTSecurityDescriptorDef.canRead()); - assertTrue("nTSecurityDescriptor modify", nTSecurityDescriptorDef.canModify()); - assertTrue("nTSecurityDescriptor add", nTSecurityDescriptorDef.canAdd()); - - ResourceAttributeDefinition lastLogonDef = accountObjectClassDefinition.findAttributeDefinition("lastLogon"); - PrismAsserts.assertDefinition(lastLogonDef, new QName(MidPointConstants.NS_RI, "lastLogon"), - DOMUtil.XSD_LONG, 0, 1); - assertTrue("lastLogonDef read", lastLogonDef.canRead()); - assertTrue("lastLogonDef modify", lastLogonDef.canModify()); - assertTrue("lastLogonDef add", lastLogonDef.canAdd()); + accountObjectClassDefinition = AdUtils.assertAdSchema(resource, getAccountObjectClass(), prismContext); assertLdapConnectorInstances(1); } @@ -713,7 +655,7 @@ public void test200AssignAccountBarbossa() throws Exception { assertNotNull("No identifier in "+shadow, accountBarbossaIcfUid); assertEquals("Wrong ICFS UID", - formatGuidToDashedNotation(MiscUtil.binaryToHex(entry.get(getPrimaryIdentifierAttributeName()).getBytes())), + AdUtils.formatGuidToDashedNotation(MiscUtil.binaryToHex(entry.get(getPrimaryIdentifierAttributeName()).getBytes())), accountBarbossaIcfUid); assertLdapPassword(USER_BARBOSSA_USERNAME, USER_BARBOSSA_FULL_NAME, USER_BARBOSSA_PASSWORD); @@ -1404,33 +1346,6 @@ public void assertAttribute(PrismObject shadow, QName attrQname, PrismAsserts.assertSets("attribute "+attrQname+" in " + shadow, actualValues, expectedValues); } - /** - * Returns dashed GUID notation formatted from simple hex-encoded binary. - * - * E.g. "2f01c06bb1d0414e9a69dd3841a13506" -> "6bc0012f-d0b1-4e41-9a69-dd3841a13506" - */ - public String formatGuidToDashedNotation(String hexValue) { - if (hexValue == null) { - return null; - } - StringBuilder sb = new StringBuilder(); - sb.append(hexValue.substring(6, 8)); - sb.append(hexValue.substring(4, 6)); - sb.append(hexValue.substring(2, 4)); - sb.append(hexValue.substring(0, 2)); - sb.append('-'); - sb.append(hexValue.substring(10, 12)); - sb.append(hexValue.substring(8, 10)); - sb.append('-'); - sb.append(hexValue.substring(14, 16)); - sb.append(hexValue.substring(12, 14)); - sb.append('-'); - sb.append(hexValue.substring(16, 20)); - sb.append('-'); - sb.append(hexValue.substring(20, 32)); - return sb.toString(); - } - protected abstract void assertAccountDisabled(PrismObject shadow); protected abstract void assertAccountEnabled(PrismObject shadow); diff --git a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractLdapTest.java b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractLdapTest.java index a9c0f734aeb..aa5a3520e23 100644 --- a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractLdapTest.java +++ b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AbstractLdapTest.java @@ -514,12 +514,20 @@ protected Entry getLdapAccountByUid(String uid) throws LdapException, IOExceptio } protected Entry getLdapAccountByCn(String cn) throws LdapException, IOException, CursorException { - return searchLdapAccount("(cn="+cn+")"); + return getLdapAccountByCn(null, cn); + } + + protected Entry getLdapAccountByCn(UserLdapConnectionConfig config, String cn) throws LdapException, IOException, CursorException { + return searchLdapAccount(config, "(cn="+cn+")"); } protected Entry searchLdapAccount(String filter) throws LdapException, IOException, CursorException { - LdapNetworkConnection connection = ldapConnect(); - List entries = ldapSearch(connection, filter); + return searchLdapAccount(null, filter); + } + + protected Entry searchLdapAccount(UserLdapConnectionConfig config, String filter) throws LdapException, IOException, CursorException { + LdapNetworkConnection connection = ldapConnect(config); + List entries = ldapSearch(config, connection, filter); ldapDisconnect(connection); assertEquals("Unexpected number of entries for "+filter+": "+entries, 1, entries.size()); @@ -671,7 +679,15 @@ protected void assertLdapNoGroupMember(String accountEntryDn, String groupName) } protected List ldapSearch(LdapNetworkConnection connection, String filter) throws LdapException, CursorException { - return ldapSearch(connection, getLdapSuffix(), filter, SearchScope.SUBTREE, "*", "isMemberOf", "memberof", "isMemberOf", getPrimaryIdentifierAttributeName()); + return ldapSearch(null, connection, filter); + } + + protected List ldapSearch(UserLdapConnectionConfig config, LdapNetworkConnection connection, String filter) throws LdapException, CursorException { + String baseContext = getLdapSuffix(); + if (config != null && config.getBaseContext() != null) { + baseContext = config.getBaseContext(); + } + return ldapSearch(connection, baseContext, filter, SearchScope.SUBTREE, "*", "isMemberOf", "memberof", "isMemberOf", getPrimaryIdentifierAttributeName()); } protected List ldapSearch(LdapNetworkConnection connection, String baseDn, String filter, SearchScope scope, String... attributes) throws LdapException, CursorException { @@ -710,7 +726,11 @@ protected void assertLdapPassword(String uid, String password) throws LdapExcept } protected void assertLdapPassword(Entry entry, String password) throws LdapException, IOException, CursorException { - LdapNetworkConnection conn = ldapConnect(entry.getDn().toString(), password); + assertLdapPassword(null, entry, password); + } + + protected void assertLdapPassword(UserLdapConnectionConfig config, Entry entry, String password) throws LdapException, IOException, CursorException { + LdapNetworkConnection conn = ldapConnect(config, entry.getDn().toString(), password); assertTrue("Not connected", conn.isConnected()); assertTrue("Not authenticated", conn.isAuthenticated()); ldapDisconnect(conn); @@ -773,7 +793,14 @@ protected void deleteLdapEntry(String dn) throws LdapException, IOException { * Silent delete. Used to clean up after previous test runs. */ protected void cleanupDelete(String dn) throws LdapException, IOException, CursorException { - LdapNetworkConnection connection = ldapConnect(); + cleanupDelete(null, dn); + } + + /** + * Silent delete. Used to clean up after previous test runs. + */ + protected void cleanupDelete(UserLdapConnectionConfig config, String dn) throws LdapException, IOException, CursorException { + LdapNetworkConnection connection = ldapConnect(config); Entry entry = getLdapEntry(connection, dn); if (entry != null) { connection.delete(dn); @@ -815,11 +842,37 @@ protected LdapNetworkConnection ldapConnect() throws LdapException, IOException } protected LdapNetworkConnection ldapConnect(String bindDn, String bindPassword) throws LdapException, IOException { - LOGGER.trace("LDAP connect to {}:{} as {}", - getLdapServerHost(), getLdapServerPort(), bindDn); - LdapConnectionConfig config = new LdapConnectionConfig(); + UserLdapConnectionConfig config = new UserLdapConnectionConfig(); config.setLdapHost(getLdapServerHost()); config.setLdapPort(getLdapServerPort()); + config.setBindDn(bindDn); + config.setBindPassword(bindPassword); + + return ldapConnect(config); + } + + protected LdapNetworkConnection ldapConnect(UserLdapConnectionConfig config, String bindDn, String bindPassword) throws LdapException, IOException { + if (config == null) { + config = new UserLdapConnectionConfig(); + config.setLdapHost(getLdapServerHost()); + config.setLdapPort(getLdapServerPort()); + } + config.setBindDn(bindDn); + config.setBindPassword(bindPassword); + + return ldapConnect(config); + } + + protected LdapNetworkConnection ldapConnect(UserLdapConnectionConfig config) throws LdapException, IOException { + if (config == null) { + config = new UserLdapConnectionConfig(); + config.setLdapHost(getLdapServerHost()); + config.setLdapPort(getLdapServerPort()); + config.setBindDn(getLdapBindDn()); + config.setBindPassword(getLdapBindPassword()); + } + LOGGER.trace("LDAP connect to {}:{} as {}", + config.getLdapHost(), config.getLdapPort(), config.getBindDn()); if (useSsl()) { config.setUseSsl(true); @@ -843,21 +896,21 @@ public X509Certificate[] getAcceptedIssuers() { LdapNetworkConnection connection = new LdapNetworkConnection(config); boolean connected = connection.connect(); if (!connected) { - AssertJUnit.fail("Cannot connect to LDAP server "+getLdapServerHost()+":"+getLdapServerPort()); + AssertJUnit.fail("Cannot connect to LDAP server "+config.getLdapHost()+":"+config.getLdapPort()); } LOGGER.trace("LDAP connected to {}:{}, executing bind as {}", - getLdapServerHost(), getLdapServerPort(), bindDn); + config.getLdapHost(), config.getLdapPort(), config.getBindDn()); BindRequest bindRequest = new BindRequestImpl(); - bindRequest.setDn(new Dn(bindDn)); - bindRequest.setCredentials(bindPassword); + bindRequest.setDn(new Dn(config.getBindDn())); + bindRequest.setCredentials(config.getBindPassword()); bindRequest.setSimple(true); BindResponse bindResponse = connection.bind(bindRequest); if (bindResponse.getLdapResult().getResultCode() != ResultCodeEnum.SUCCESS) { ldapDisconnect(connection); - throw new SecurityException("Bind as "+bindDn+" failed: "+bindResponse.getLdapResult().getDiagnosticMessage()+" ("+bindResponse.getLdapResult().getResultCode()+")"); + throw new SecurityException("Bind as "+config.getBindDn()+" failed: "+bindResponse.getLdapResult().getDiagnosticMessage()+" ("+bindResponse.getLdapResult().getResultCode()+")"); } LOGGER.trace("LDAP connected to {}:{}, bound as {}", - getLdapServerHost(), getLdapServerPort(), bindDn); + config.getLdapHost(), config.getLdapPort(), config.getBindDn()); return connection; } diff --git a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AdUtils.java b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AdUtils.java new file mode 100644 index 00000000000..d638a9e17d5 --- /dev/null +++ b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/AdUtils.java @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2016 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.testing.conntest; + +import static com.evolveum.midpoint.test.IntegrationTestTools.display; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertTrue; + +import javax.xml.namespace.QName; + +import com.evolveum.midpoint.common.refinery.RefinedResourceSchema; +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.util.PrismAsserts; +import com.evolveum.midpoint.schema.constants.MidPointConstants; +import com.evolveum.midpoint.schema.processor.ObjectClassComplexTypeDefinition; +import com.evolveum.midpoint.schema.processor.ResourceAttributeDefinition; +import com.evolveum.midpoint.schema.processor.ResourceSchema; +import com.evolveum.midpoint.util.DOMUtil; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; + +/** + * @author semancik + * + */ +public class AdUtils { + + public static final String ATTRIBUTE_OBJECT_GUID_NAME = "objectGUID"; + public static final String ATTRIBUTE_SAM_ACCOUNT_NAME_NAME = "sAMAccountName"; + public static final String ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME = "userAccountControl"; + public static final QName ATTRIBUTE_USER_ACCOUNT_CONTROL_QNAME = new QName(MidPointConstants.NS_RI, ATTRIBUTE_USER_ACCOUNT_CONTROL_NAME); + public static final String ATTRIBUTE_UNICODE_PWD_NAME = "unicodePwd"; + + /** + * Returns dashed GUID notation formatted from simple hex-encoded binary. + * + * E.g. "2f01c06bb1d0414e9a69dd3841a13506" -> "6bc0012f-d0b1-4e41-9a69-dd3841a13506" + */ + public static String formatGuidToDashedNotation(String hexValue) { + if (hexValue == null) { + return null; + } + StringBuilder sb = new StringBuilder(); + sb.append(hexValue.substring(6, 8)); + sb.append(hexValue.substring(4, 6)); + sb.append(hexValue.substring(2, 4)); + sb.append(hexValue.substring(0, 2)); + sb.append('-'); + sb.append(hexValue.substring(10, 12)); + sb.append(hexValue.substring(8, 10)); + sb.append('-'); + sb.append(hexValue.substring(14, 16)); + sb.append(hexValue.substring(12, 14)); + sb.append('-'); + sb.append(hexValue.substring(16, 20)); + sb.append('-'); + sb.append(hexValue.substring(20, 32)); + return sb.toString(); + } + + public static ObjectClassComplexTypeDefinition assertAdSchema(PrismObject resource, QName accountObjectClass, PrismContext prismContext) throws SchemaException { + + ResourceSchema resourceSchema = RefinedResourceSchema.getResourceSchema(resource, prismContext); + display("Resource schema", resourceSchema); + + RefinedResourceSchema refinedSchema = RefinedResourceSchema.getRefinedSchema(resource); + display("Refined schema", refinedSchema); + ObjectClassComplexTypeDefinition accountObjectClassDefinition = refinedSchema.findObjectClassDefinition(accountObjectClass); + assertNotNull("No definition for object class "+accountObjectClass, accountObjectClassDefinition); + display("Account object class def", accountObjectClassDefinition); + + ResourceAttributeDefinition cnDef = accountObjectClassDefinition.findAttributeDefinition("cn"); + PrismAsserts.assertDefinition(cnDef, new QName(MidPointConstants.NS_RI, "cn"), DOMUtil.XSD_STRING, 1, 1); + assertTrue("cn read", cnDef.canRead()); + assertTrue("cn modify", cnDef.canModify()); + assertTrue("cn add", cnDef.canAdd()); + + ResourceAttributeDefinition samAccountNameDef = accountObjectClassDefinition.findAttributeDefinition(ATTRIBUTE_SAM_ACCOUNT_NAME_NAME); + PrismAsserts.assertDefinition(samAccountNameDef, + new QName(MidPointConstants.NS_RI, ATTRIBUTE_SAM_ACCOUNT_NAME_NAME), DOMUtil.XSD_STRING, 0, 1); + assertTrue("samAccountNameDef read", samAccountNameDef.canRead()); + assertTrue("samAccountNameDef modify", samAccountNameDef.canModify()); + assertTrue("samAccountNameDef add", samAccountNameDef.canAdd()); + + + ResourceAttributeDefinition oDef = accountObjectClassDefinition.findAttributeDefinition("o"); + PrismAsserts.assertDefinition(oDef, new QName(MidPointConstants.NS_RI, "o"), DOMUtil.XSD_STRING, 0, -1); + assertTrue("o read", oDef.canRead()); + assertTrue("o modify", oDef.canModify()); + assertTrue("o add", oDef.canAdd()); + + ResourceAttributeDefinition createTimestampDef = accountObjectClassDefinition.findAttributeDefinition("createTimeStamp"); + PrismAsserts.assertDefinition(createTimestampDef, new QName(MidPointConstants.NS_RI, "createTimeStamp"), + DOMUtil.XSD_LONG, 0, 1); + assertTrue("createTimeStampDef read", createTimestampDef.canRead()); + assertFalse("createTimeStampDef modify", createTimestampDef.canModify()); + assertFalse("createTimeStampDef add", createTimestampDef.canAdd()); + + ResourceAttributeDefinition isCriticalSystemObjectDef = accountObjectClassDefinition.findAttributeDefinition("isCriticalSystemObject"); + PrismAsserts.assertDefinition(isCriticalSystemObjectDef, new QName(MidPointConstants.NS_RI, "isCriticalSystemObject"), + DOMUtil.XSD_BOOLEAN, 0, 1); + assertTrue("isCriticalSystemObject read", isCriticalSystemObjectDef.canRead()); + assertTrue("isCriticalSystemObject modify", isCriticalSystemObjectDef.canModify()); + assertTrue("isCriticalSystemObject add", isCriticalSystemObjectDef.canAdd()); + + ResourceAttributeDefinition nTSecurityDescriptorDef = accountObjectClassDefinition.findAttributeDefinition("nTSecurityDescriptor"); + PrismAsserts.assertDefinition(nTSecurityDescriptorDef, new QName(MidPointConstants.NS_RI, "nTSecurityDescriptor"), + DOMUtil.XSD_BASE64BINARY, 0, 1); + assertTrue("nTSecurityDescriptor read", nTSecurityDescriptorDef.canRead()); + assertTrue("nTSecurityDescriptor modify", nTSecurityDescriptorDef.canModify()); + assertTrue("nTSecurityDescriptor add", nTSecurityDescriptorDef.canAdd()); + + ResourceAttributeDefinition lastLogonDef = accountObjectClassDefinition.findAttributeDefinition("lastLogon"); + PrismAsserts.assertDefinition(lastLogonDef, new QName(MidPointConstants.NS_RI, "lastLogon"), + DOMUtil.XSD_LONG, 0, 1); + assertTrue("lastLogonDef read", lastLogonDef.canRead()); + assertTrue("lastLogonDef modify", lastLogonDef.canModify()); + assertTrue("lastLogonDef add", lastLogonDef.canAdd()); + + return accountObjectClassDefinition; + } +} diff --git a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/TestAdLdapChimera.java b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/TestAdLdapChimera.java new file mode 100644 index 00000000000..31be909993c --- /dev/null +++ b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/TestAdLdapChimera.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2015-2016 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.testing.conntest; + +import java.io.File; + +import org.testng.annotations.AfterClass; +import org.testng.annotations.Listeners; + +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.test.util.MidPointTestConstants; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; + +/** + * @author semancik + * + */ +public class TestAdLdapChimera extends AbstractAdLdapMultidomainTest { + + @Override + protected String getResourceOid() { + return "eced6d24-73e3-11e5-8457-93eff15a6b85"; + } + + @Override + protected File getResourceFile() { + return new File(getBaseDir(), "resource-chimera.xml"); + } + + @Override + protected String getLdapServerHost() { + return "chimera.ad.evolveum.com"; + } + + @Override + protected int getLdapServerPort() { + return 636; + } + + @Override + protected void assertAccountDisabled(PrismObject shadow) { + assertAdministrativeStatus(shadow, ActivationStatusType.DISABLED); + } + + @Override + protected void assertAccountEnabled(PrismObject shadow) { + assertAdministrativeStatus(shadow, ActivationStatusType.ENABLED); + } + +} diff --git a/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/UserLdapConnectionConfig.java b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/UserLdapConnectionConfig.java new file mode 100644 index 00000000000..2b3e0f7f4e0 --- /dev/null +++ b/testing/conntest/src/test/java/com/evolveum/midpoint/testing/conntest/UserLdapConnectionConfig.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2016 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.testing.conntest; + +import org.apache.directory.ldap.client.api.LdapConnectionConfig; + +/** + * @author semancik + * + */ +public class UserLdapConnectionConfig extends LdapConnectionConfig { + + private String bindDn; + private String bindPassword; + private String baseContext; + + public String getBindDn() { + return bindDn; + } + + public void setBindDn(String bindDn) { + this.bindDn = bindDn; + } + + public String getBindPassword() { + return bindPassword; + } + + public void setBindPassword(String bindPassword) { + this.bindPassword = bindPassword; + } + + public String getBaseContext() { + return baseContext; + } + + public void setBaseContext(String baseContext) { + this.baseContext = baseContext; + } + +} diff --git a/testing/conntest/src/test/resources/ad-ldap-multidomain/resource-chimera.xml b/testing/conntest/src/test/resources/ad-ldap-multidomain/resource-chimera.xml new file mode 100644 index 00000000000..51a5f71aa4e --- /dev/null +++ b/testing/conntest/src/test/resources/ad-ldap-multidomain/resource-chimera.xml @@ -0,0 +1,645 @@ + + + + Active Directory Chimera (LDAP) + + + + + c:connectorType + com.evolveum.polygon.connector.ldap.ad.AdLdapConnector + + + + + + + chimera.ad.evolveum.com + 636 + DC=ad,DC=evolveum,DC=com + CN=midpoint,CN=Users,DC=ad,DC=evolveum,DC=com + ssl + + qwe.123 + + host=hydra.ad.evolveum.com; baseContext=DC=sub,DC=ad,DC=evolveum,DC=com; bindDn=CN=midpoint,CN=Users,DC=sub,DC=ad,DC=evolveum,DC=com + ignore + resolve + + + false + false + false + + + + + + ri:user + ri:group + ri:organizationalUnit + ri:domain + + + + + + + + account + Default Account + true + ri:user + + ri:organizationalUnit + + + attributes/dn + CN=Users,DC=ad,DC=evolveum,DC=com + + + + + ri:dn + Distinguished Name + mr:distinguishedName + + + $user/fullName + + + + + + + + + ri:sAMAccountName + Login name + + + $user/name + + + + + name + + + + + + ri:cn + + 0 + + + + fullName + + + + + fullName + + + + + + ri:sn + + 0 + + + + familyName + + + + + familyName + + + + + + ri:givenName + + + givenName + + + + + givenName + + + + + + ri:userPrincipalName + + + $user/name + + + + + + + + + ri:pwdLastSet + + + -1 + + + + + + ri:createTimeStamp + explicit + + + + ri:objectCategory + + + CN=Person,CN=Schema,CN=Configuration,DC=ad,DC=evolveum,DC=com + + + + + + ri:showInAdvancedViewOnly + + + extension/showInAdvancedViewOnly + + + + + + ri:group + AD Group Membership + entitlement + group + objectToSubject + ri:member + ri:dn + ri:memberOf + ri:dn + + + + + + + + + + + + + + + + + + generic + ou-top + Top Org + true + ri:organizationalUnit + + ri:organizationalUnit + + + attributes/dn + OU=Org,DC=ad,DC=evolveum,DC=com + + + + + ri:dn + Distinguished Name + mr:distinguishedName + + + name + + + + + + + + + ri:ou + + 0 + + + + name + + + + + + + ri:objectCategory + + + CN=Organizational-Unit,CN=Schema,CN=Configuration,DC=ad,DC=evolveum,DC=com + + + + + + + + + account + sub + Sub Account + false + ri:user + + ri:organizationalUnit + + + attributes/dn + CN=Users,DC=sub,DC=ad,DC=evolveum,DC=com + + + + + ri:dn + Distinguished Name + mr:distinguishedName + + + $user/fullName + + + + + + + + + ri:sAMAccountName + Login name + + + $user/name + + + + + name + + + + + + ri:cn + + 0 + + + + fullName + + + + + fullName + + + + + + ri:sn + + 0 + + + + familyName + + + + + familyName + + + + + + ri:givenName + + + givenName + + + + + givenName + + + + + + ri:userPrincipalName + + + $user/name + + + + + + + + + ri:pwdLastSet + + + -1 + + + + + + ri:createTimeStamp + explicit + + + + ri:nTSecurityDescriptor + + + 0 + + + + + ri:instanceType + + + 0 + + + + + ri:objectCategory + + + 0 + + + + CN=Person,CN=Schema,CN=Configuration,DC=ad,DC=evolveum,DC=com + + + + + + + ri:group + AD Group Membership + entitlement + group + objectToSubject + ri:member + ri:dn + ri:memberOf + ri:dn + + + + + + + + + + + + + + + + + + generic + ou-sub + Sub Org + ri:organizationalUnit + + ri:domain + + + attributes/dn + DC=sub,DC=ad,DC=evolveum,DC=com + + + + + ri:dn + Distinguished Name + mr:distinguishedName + + + name + + + + + + + + + ri:ou + + 0 + + + + name + + + + + + + ri:objectCategory + + + CN=Organizational-Unit,CN=Schema,CN=Configuration,DC=ad,DC=evolveum,DC=com + + + + + + + + + entitlement + group + AD Group + Groups in the parent (top) domain + true + ri:group + + ri:organizationalUnit + + + attributes/dn + CN=Users,DC=ad,DC=evolveum,DC=com + + + + + dn + mr:distinguishedName + + + $focus/name + + + + + + + + ri:cn + mr:stringIgnoreCase + + + $focus/name + + + + + name + + + + + ri:description + + strong + + description + + + + + description + + + + + + + + + true + + + + + + Account sync + ri:user + account + default + UserType + true + + + c:name + + $shadow/attributes/sAMAccountName + + + + + + linked + true + + + deleted + + + + unlinked + + + + unmatched + + + + + + + + diff --git a/testing/conntest/src/test/resources/ad-ldap-multidomain/role-meta-org.xml b/testing/conntest/src/test/resources/ad-ldap-multidomain/role-meta-org.xml new file mode 100644 index 00000000000..a8ea121408f --- /dev/null +++ b/testing/conntest/src/test/resources/ad-ldap-multidomain/role-meta-org.xml @@ -0,0 +1,54 @@ + + + + Org Metarole + + + + + entitlement + group + + + + + + + account + default + + ri:group + + + + + entitlement + group + + + + + + + 2 + + + diff --git a/testing/conntest/src/test/resources/ad-ldap-multidomain/role-pirate.xml b/testing/conntest/src/test/resources/ad-ldap-multidomain/role-pirate.xml new file mode 100644 index 00000000000..56a61fa3a0e --- /dev/null +++ b/testing/conntest/src/test/resources/ad-ldap-multidomain/role-pirate.xml @@ -0,0 +1,45 @@ + + + + Pirate + + + + + ri:group + false + + + + + + attributes/ri:cn + Pirates + + + true + + + + + + + \ No newline at end of file diff --git a/testing/conntest/src/test/resources/ad-ldap-multidomain/role-submissive.xml b/testing/conntest/src/test/resources/ad-ldap-multidomain/role-submissive.xml new file mode 100644 index 00000000000..a46a0d425c3 --- /dev/null +++ b/testing/conntest/src/test/resources/ad-ldap-multidomain/role-submissive.xml @@ -0,0 +1,31 @@ + + + + Submissive + Role that creates account in AD subdomain + + + + account + sub + + + diff --git a/testing/conntest/src/test/resources/ad-ldap-multidomain/user-subman.xml b/testing/conntest/src/test/resources/ad-ldap-multidomain/user-subman.xml new file mode 100644 index 00000000000..c0c2e7ffe34 --- /dev/null +++ b/testing/conntest/src/test/resources/ad-ldap-multidomain/user-subman.xml @@ -0,0 +1,44 @@ + + + + subman + + + enabled + + + Sub Man + Sub + Man + Subterra + + + + + sub.123 + + + + + diff --git a/testing/conntest/src/test/resources/common/system-configuration.xml b/testing/conntest/src/test/resources/common/system-configuration.xml index fce9b3ebcb8..b44049f52f1 100644 --- a/testing/conntest/src/test/resources/common/system-configuration.xml +++ b/testing/conntest/src/test/resources/common/system-configuration.xml @@ -1,6 +1,6 @@ + + submarine + + + enabled + + + Sub Marine + Sub + Marine + The Depths of Sea + + + + + bubble.123 + + + + + From 24379642d0153c7dd30a246add816a9d836e7b4f Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Fri, 7 Oct 2016 18:34:34 +0200 Subject: [PATCH 6/6] Switch to LDAP connector 1.4.2.19 --- build-system/pom.xml | 2 +- .../provisioning-impl/src/test/resources/ucf/connector-ldap.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-system/pom.xml b/build-system/pom.xml index 14dd39a2726..5bfee9d303f 100644 --- a/build-system/pom.xml +++ b/build-system/pom.xml @@ -603,7 +603,7 @@ com.evolveum.polygon connector-ldap - 1.4.3.0-SNAPSHOT + 1.4.2.19 xml-apis diff --git a/provisioning/provisioning-impl/src/test/resources/ucf/connector-ldap.xml b/provisioning/provisioning-impl/src/test/resources/ucf/connector-ldap.xml index b1087fe65c0..4dffe620233 100644 --- a/provisioning/provisioning-impl/src/test/resources/ucf/connector-ldap.xml +++ b/provisioning/provisioning-impl/src/test/resources/ucf/connector-ldap.xml @@ -23,7 +23,7 @@ ICF com.evolveum.polygon.connector.ldap.LdapConnector http://midpoint.evolveum.com/xml/ns/public/connector/icf-1 com.evolveum.polygon.connector.ldap.LdapConnector - 1.4.3.0-SNAPSHOT + 1.4.2.19 com.evolveum.polygon.connector-ldap http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/com.evolveum.polygon.connector-ldap/com.evolveum.polygon.connector.ldap.LdapConnector