[SYNCOPE-1300] ignoreCase checks implemented both during Pull and Propagation
authorFrancesco Chicchiriccò <ilgrosso@apache.org>
Fri, 20 Apr 2018 14:24:48 +0000 (16:24 +0200)
committerFrancesco Chicchiriccò <ilgrosso@apache.org>
Fri, 20 Apr 2018 14:24:48 +0000 (16:24 +0200)
40 files changed:
client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ObjectTypeTogglePanel.java
client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ProvisionWizardBuilder.java
client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceMappingPanel.java
client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceProvision.java
client/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ProvisionWizardBuilder$ObjectType.html
client/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ProvisionWizardBuilder$ObjectType.properties
client/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ProvisionWizardBuilder$ObjectType_it.properties
client/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ProvisionWizardBuilder$ObjectType_ja.properties
client/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ProvisionWizardBuilder$ObjectType_pt_BR.properties
client/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ProvisionWizardBuilder$ObjectType_ru.properties
common/lib/src/main/java/org/apache/syncope/common/lib/to/MappingTO.java
common/lib/src/main/java/org/apache/syncope/common/lib/to/OrgUnitTO.java
common/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisionTO.java
core/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/OrgUnit.java
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/Provision.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAOrgUnit.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAProvision.java
core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java
core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AsyncConnectorFacade.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandlerImpl.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActions.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2UserManager.java
pom.xml
src/main/asciidoc/reference-guide/concepts/externalresources.adoc
src/main/asciidoc/reference-guide/concepts/provisioning/pull.adoc

index 0f14284..d1f72ec 100644 (file)
@@ -36,15 +36,16 @@ public abstract class ObjectTypeTogglePanel extends TogglePanel<Serializable> {
 
     ObjectTypeTogglePanel(
             final String id,
-            final ResourceProvision item,
+            final ResourceProvision resourceProvision,
             final LoadableDetachableModel<List<String>> anyTypes,
             final PageReference pageRef) {
+
         super(id, pageRef);
 
         Form<?> form = new Form<>("objectTypeForm");
         addInnerObject(form);
 
-        PropertyModel<String> typeModel = new PropertyModel<>(item, "anyType");
+        PropertyModel<String> typeModel = new PropertyModel<>(resourceProvision, "anyType");
 
         form.add(new AjaxDropDownChoicePanel<>(
                 "type", "type", typeModel, false).
index 5d7c62b..40086f9 100644 (file)
@@ -64,19 +64,23 @@ public class ProvisionWizardBuilder extends AjaxWizardBuilder<ResourceProvision>
 
         private static final long serialVersionUID = -1657800545799468278L;
 
-        ObjectType(final ResourceProvision item) {
+        ObjectType(final ResourceProvision resourceProvision) {
             super(new ResourceModel("clazz.title", StringUtils.EMPTY),
-                    new ResourceModel("clazz.summary", StringUtils.EMPTY), new Model<>(item));
+                    new ResourceModel("clazz.summary", StringUtils.EMPTY), new Model<>(resourceProvision));
 
-            final WebMarkupContainer container = new WebMarkupContainer("container");
+            WebMarkupContainer container = new WebMarkupContainer("container");
             container.setOutputMarkupId(true);
             add(container);
 
             clazz = new AjaxTextFieldPanel(
-                    "clazz", "clazz", new PropertyModel<>(item, "objectClass"));
+                    "clazz", "clazz", new PropertyModel<>(resourceProvision, "objectClass"));
             clazz.setRequired(true);
             clazz.setChoices(connectorRestClient.getObjectClasses(resourceTO.getConnector()));
             container.add(clazz);
+
+            AjaxCheckBoxPanel ignoreCaseMatch = new AjaxCheckBoxPanel(
+                    "ignoreCaseMatch", "ignoreCaseMatch", new PropertyModel<>(resourceProvision, "ignoreCaseMatch"));
+            container.add(ignoreCaseMatch);
         }
     }
 
@@ -101,12 +105,12 @@ public class ProvisionWizardBuilder extends AjaxWizardBuilder<ResourceProvision>
 
         private final ResourceProvision provision;
 
-        AuxClasses(final ResourceProvision item) {
-            this.provision = item;
+        AuxClasses(final ResourceProvision resourceProvision) {
+            this.provision = resourceProvision;
 
             setTitleModel(new ResourceModel("auxClasses.title"));
-            setSummaryModel(new StringResourceModel("auxClasses.summary", this, new Model<>(item)));
-            add(new ProvisionAuxClassesPanel("auxClasses", item.getProvisionTO()));
+            setSummaryModel(new StringResourceModel("auxClasses.summary", this, new Model<>(resourceProvision)));
+            add(new ProvisionAuxClassesPanel("auxClasses", resourceProvision.getProvisionTO()));
         }
 
         @Override
@@ -122,7 +126,7 @@ public class ProvisionWizardBuilder extends AjaxWizardBuilder<ResourceProvision>
 
         private static final long serialVersionUID = 3454904947720856253L;
 
-        Mapping(final ResourceProvision item) {
+        Mapping(final ResourceProvision resourceProvision) {
             setTitleModel(Model.of("Mapping"));
             setSummaryModel(Model.of(StringUtils.EMPTY));
         }
@@ -135,16 +139,16 @@ public class ProvisionWizardBuilder extends AjaxWizardBuilder<ResourceProvision>
 
         private static final long serialVersionUID = 2359955465172450478L;
 
-        ConnObjectLink(final ResourceProvision item) {
+        ConnObjectLink(final ResourceProvision resourceProvision) {
             super(new ResourceModel("link.title", StringUtils.EMPTY),
                     new ResourceModel("link.summary", StringUtils.EMPTY));
 
-            final WebMarkupContainer connObjectLinkContainer = new WebMarkupContainer("connObjectLinkContainer");
+            WebMarkupContainer connObjectLinkContainer = new WebMarkupContainer("connObjectLinkContainer");
             connObjectLinkContainer.setOutputMarkupId(true);
             add(connObjectLinkContainer);
 
             boolean connObjectLinkEnabled = false;
-            if (StringUtils.isNotBlank(item.getConnObjectLink())) {
+            if (StringUtils.isNotBlank(resourceProvision.getConnObjectLink())) {
                 connObjectLinkEnabled = true;
             }
 
@@ -160,7 +164,7 @@ public class ProvisionWizardBuilder extends AjaxWizardBuilder<ResourceProvision>
             final AjaxTextFieldPanel connObjectLink = new AjaxTextFieldPanel(
                     "connObjectLink",
                     new ResourceModel("connObjectLink", "connObjectLink").getObject(),
-                    new PropertyModel<>(item, "connObjectLink"),
+                    new PropertyModel<>(resourceProvision, "connObjectLink"),
                     false);
             connObjectLink.enableJexlHelp();
             connObjectLink.setEnabled(connObjectLinkEnabled);
@@ -195,57 +199,57 @@ public class ProvisionWizardBuilder extends AjaxWizardBuilder<ResourceProvision>
     }
 
     @Override
-    protected WizardModel buildModelSteps(final ResourceProvision modelObject, final WizardModel wizardModel) {
-        wizardModel.add(new ObjectType(modelObject));
-        wizardModel.add(new AuxClasses(modelObject));
+    protected WizardModel buildModelSteps(final ResourceProvision resourceProvision, final WizardModel wizardModel) {
+        wizardModel.add(new ObjectType(resourceProvision));
+        wizardModel.add(new AuxClasses(resourceProvision));
 
-        Mapping mapping = new Mapping(modelObject);
+        Mapping mapping = new Mapping(resourceProvision);
         mapping.setOutputMarkupId(true);
 
         ItemTransformersTogglePanel itemTransformers = new ItemTransformersTogglePanel(mapping, pageRef);
         addOuterObject(itemTransformers);
         JEXLTransformersTogglePanel jexlTransformers = new JEXLTransformersTogglePanel(mapping, pageRef);
         addOuterObject(jexlTransformers);
-        if (modelObject.getProvisionTO() != null && modelObject.getProvisionTO().getMapping() == null) {
-            modelObject.getProvisionTO().setMapping(new MappingTO());
+        if (resourceProvision.getProvisionTO() != null && resourceProvision.getProvisionTO().getMapping() == null) {
+            resourceProvision.getProvisionTO().setMapping(new MappingTO());
         }
         mapping.add(new ResourceMappingPanel(
-                "mapping", resourceTO, adminRealm, modelObject, itemTransformers, jexlTransformers));
+                "mapping", resourceTO, adminRealm, resourceProvision, itemTransformers, jexlTransformers));
 
         wizardModel.add(mapping);
 
-        wizardModel.add(new ConnObjectLink(modelObject));
+        wizardModel.add(new ConnObjectLink(resourceProvision));
         return wizardModel;
     }
 
     @Override
-    protected Serializable onApplyInternal(final ResourceProvision modelObject) {
-        if (modelObject.getOrgUnitTO() != null) {
-            this.resourceTO.setOrgUnit(modelObject.getOrgUnitTO());
+    protected Serializable onApplyInternal(final ResourceProvision resourceProvision) {
+        if (resourceProvision.getOrgUnitTO() != null) {
+            this.resourceTO.setOrgUnit(resourceProvision.getOrgUnitTO());
 
             this.resourceTO.getOrgUnit().getItems().clear();
-            this.resourceTO.getOrgUnit().getItems().addAll(modelObject.getItems());
-        } else if (modelObject.getProvisionTO() != null) {
+            this.resourceTO.getOrgUnit().getItems().addAll(resourceProvision.getItems());
+        } else if (resourceProvision.getProvisionTO() != null) {
             final List<ProvisionTO> provisions;
-            if (modelObject.getKey() == null) {
+            if (resourceProvision.getKey() == null) {
                 provisions = this.resourceTO.getProvisions().stream().
-                        filter(object -> !modelObject.getAnyType().equals(object.getAnyType())).
+                        filter(object -> !resourceProvision.getAnyType().equals(object.getAnyType())).
                         collect(Collectors.toList());
             } else {
                 provisions = this.resourceTO.getProvisions().stream().
-                        filter(object -> !modelObject.getKey().equals(object.getKey())).
+                        filter(object -> !resourceProvision.getKey().equals(object.getKey())).
                         collect(Collectors.toList());
             }
 
-            ProvisionTO provisionTO = modelObject.getProvisionTO();
+            ProvisionTO provisionTO = resourceProvision.getProvisionTO();
             provisionTO.getMapping().getItems().clear();
-            provisionTO.getMapping().getItems().addAll(modelObject.getItems());
+            provisionTO.getMapping().getItems().addAll(resourceProvision.getItems());
             provisions.add(provisionTO);
 
             this.resourceTO.getProvisions().clear();
             this.resourceTO.getProvisions().addAll(provisions);
         }
 
-        return modelObject;
+        return resourceProvision;
     }
 }
index 6a36f62..270f32d 100644 (file)
@@ -142,13 +142,13 @@ public class ResourceMappingPanel extends AbstractMappingPanel {
                     LOG.error("Could not read AnyType classes for {}", anyType.getClasses(), e);
                 }
             }
-            for (String auxClass : provision.getAuxClasses()) {
+            provision.getAuxClasses().forEach(auxClass -> {
                 try {
                     anyTypeClassTOs.add(anyTypeClassRestClient.read(auxClass));
                 } catch (Exception e) {
                     LOG.error("Could not read AnyTypeClass for {}", auxClass, e);
                 }
-            }
+            });
 
             switch (provision.getAnyType()) {
                 case "USER":
@@ -163,14 +163,14 @@ public class ResourceMappingPanel extends AbstractMappingPanel {
                     choices.addAll(ANY_OBJECT_FIELD_NAMES);
             }
 
-            for (AnyTypeClassTO anyTypeClassTO : anyTypeClassTOs) {
+            anyTypeClassTOs.forEach(anyTypeClassTO -> {
                 choices.addAll(anyTypeClassTO.getPlainSchemas());
                 choices.addAll(anyTypeClassTO.getDerSchemas());
                 choices.addAll(anyTypeClassTO.getVirSchemas());
-            }
+            });
         }
 
-        final List<String> names = new ArrayList<>(choices);
+        List<String> names = new ArrayList<>(choices);
         Collections.sort(names);
         toBeUpdated.setChoices(names);
     }
index 796fb76..e4233b5 100644 (file)
@@ -128,6 +128,18 @@ public class ResourceProvision implements Serializable {
         return provisionTO == null ? Collections.<String>emptyList() : provisionTO.getAuxClasses();
     }
 
+    public boolean isIgnoreCaseMatch() {
+        return provisionTO == null ? orgUnitTO.isIgnoreCaseMatch() : provisionTO.isIgnoreCaseMatch();
+    }
+
+    public void setIgnoreCaseMatch(final boolean ignoreCaseMatch) {
+        if (provisionTO == null) {
+            orgUnitTO.setIgnoreCaseMatch(ignoreCaseMatch);
+        } else {
+            provisionTO.setIgnoreCaseMatch(ignoreCaseMatch);
+        }
+    }
+
     public String getConnObjectLink() {
         return provisionTO == null
                 ? orgUnitTO == null
index c3be6f2..7e5585a 100644 (file)
@@ -49,7 +49,7 @@ public class MappingTO extends AbstractBaseBean implements ItemContainerTO {
 
     @Override
     public ItemTO getConnObjectKeyItem() {
-        return getItems().stream().filter(item -> item.isConnObjectKey()).findFirst().get();
+        return getItems().stream().filter(ItemTO::isConnObjectKey).findFirst().get();
     }
 
     protected boolean addConnObjectKeyItem(final ItemTO connObjectItem) {
index ae4255f..f7e326b 100644 (file)
@@ -39,6 +39,8 @@ public class OrgUnitTO extends AbstractBaseBean implements EntityTO, ItemContain
 
     private String syncToken;
 
+    private boolean ignoreCaseMatch;
+
     private String connObjectLink;
 
     private final List<ItemTO> items = new ArrayList<>();
@@ -69,6 +71,14 @@ public class OrgUnitTO extends AbstractBaseBean implements EntityTO, ItemContain
         this.syncToken = syncToken;
     }
 
+    public boolean isIgnoreCaseMatch() {
+        return ignoreCaseMatch;
+    }
+
+    public void setIgnoreCaseMatch(final boolean ignoreCaseMatch) {
+        this.ignoreCaseMatch = ignoreCaseMatch;
+    }
+
     public String getConnObjectLink() {
         return connObjectLink;
     }
index b7dae9c..cf9293e 100644 (file)
@@ -43,6 +43,8 @@ public class ProvisionTO extends AbstractBaseBean implements EntityTO {
 
     private String syncToken;
 
+    private boolean ignoreCaseMatch;
+
     private MappingTO mapping;
 
     private final List<String> virSchemas = new ArrayList<>();
@@ -88,6 +90,14 @@ public class ProvisionTO extends AbstractBaseBean implements EntityTO {
         this.syncToken = syncToken;
     }
 
+    public boolean isIgnoreCaseMatch() {
+        return ignoreCaseMatch;
+    }
+
+    public void setIgnoreCaseMatch(final boolean ignoreCaseMatch) {
+        this.ignoreCaseMatch = ignoreCaseMatch;
+    }
+
     public MappingTO getMapping() {
         return mapping;
     }
index 88adeaa..34b1409 100644 (file)
@@ -150,6 +150,7 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<AbstractBase
         ConnectorObject connectorObject = connector.getObject(
                 provision.getObjectClass(),
                 AttributeBuilder.build(connObjectKeyItem.getExtAttrName(), connObjectKeyValue),
+                provision.isIgnoreCaseMatch(),
                 MappingUtils.buildOperationOptions(mapItems));
         if (connectorObject != null) {
             Set<Attribute> attributes = connectorObject.getAttributes();
index e90b245..bea59db 100644 (file)
@@ -331,6 +331,7 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
         ConnectorObject connectorObject = connector.getObject(
                 init.getRight().getObjectClass(),
                 AttributeBuilder.build(connObjectKeyItem.get().getExtAttrName(), connObjectKeyValue.get()),
+                init.getRight().isIgnoreCaseMatch(),
                 MappingUtils.buildOperationOptions(mapItems));
         if (connectorObject == null) {
             throw new NotFoundException(
index 658a32b..042a799 100644 (file)
@@ -41,9 +41,9 @@ public interface AnyDAO<A extends Any<?>> extends DAO<A> {
 
     A findByWorkflowId(String workflowId);
 
-    List<A> findByPlainAttrValue(String schemaName, PlainAttrValue attrValue);
+    List<A> findByPlainAttrValue(String schemaName, PlainAttrValue attrValue, boolean ignoreCaseMatch);
 
-    A findByPlainAttrUniqueValue(String schemaName, PlainAttrValue attrUniqueValue);
+    A findByPlainAttrUniqueValue(String schemaName, PlainAttrValue attrUniqueValue, boolean ignoreCaseMatch);
 
     /**
      * Find any objects by derived attribute value. This method could fail if one or more string literals contained
@@ -53,9 +53,10 @@ public interface AnyDAO<A extends Any<?>> extends DAO<A> {
      *
      * @param schemaName derived schema name
      * @param value derived attribute value
+     * @param ignoreCaseMatch whether comparison for string values should take case into account or not
      * @return list of any objects
      */
-    List<A> findByDerAttrValue(String schemaName, String value);
+    List<A> findByDerAttrValue(String schemaName, String value, boolean ignoreCaseMatch);
 
     List<A> findByResource(ExternalResource resource);
 
index 89bf153..52e209b 100644 (file)
@@ -40,6 +40,10 @@ public interface OrgUnit extends Entity {
 
     void setSyncToken(SyncToken syncToken);
 
+    boolean isIgnoreCaseMatch();
+
+    void setIgnoreCaseMatch(boolean ignoreCaseMatch);
+
     String getConnObjectLink();
 
     void setConnObjectLink(String connObjectLink);
index 0a89878..1fe6023 100644 (file)
@@ -49,6 +49,10 @@ public interface Provision extends Entity {
 
     void setSyncToken(SyncToken syncToken);
 
+    boolean isIgnoreCaseMatch();
+
+    void setIgnoreCaseMatch(boolean ignoreCaseMatch);
+
     Mapping getMapping();
 
     void setMapping(Mapping mapping);
index 53fb4a9..a59b33a 100644 (file)
@@ -195,19 +195,27 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
         return result;
     }
 
-    private Query findByPlainAttrValueQuery(final String entityName) {
-        return entityManager().createQuery("SELECT e FROM " + entityName + " e"
+    private Query findByPlainAttrValueQuery(final String entityName, final boolean ignoreCaseMatch) {
+        String query = "SELECT e FROM " + entityName + " e"
                 + " WHERE e.attribute.schema.id = :schemaKey AND (e.stringValue IS NOT NULL"
-                + " AND e.stringValue = :stringValue)"
+                + " AND "
+                + (ignoreCaseMatch ? "LOWER(" : "") + "e.stringValue" + (ignoreCaseMatch ? ")" : "")
+                + " = "
+                + (ignoreCaseMatch ? "LOWER(" : "") + ":stringValue" + (ignoreCaseMatch ? ")" : "") + ")"
                 + " OR (e.booleanValue IS NOT NULL AND e.booleanValue = :booleanValue)"
                 + " OR (e.dateValue IS NOT NULL AND e.dateValue = :dateValue)"
                 + " OR (e.longValue IS NOT NULL AND e.longValue = :longValue)"
-                + " OR (e.doubleValue IS NOT NULL AND e.doubleValue = :doubleValue)");
+                + " OR (e.doubleValue IS NOT NULL AND e.doubleValue = :doubleValue)";
+        return entityManager().createQuery(query);
     }
 
     @Override
     @SuppressWarnings("unchecked")
-    public List<A> findByPlainAttrValue(final String schemaKey, final PlainAttrValue attrValue) {
+    public List<A> findByPlainAttrValue(
+            final String schemaKey,
+            final PlainAttrValue attrValue,
+            final boolean ignoreCaseMatch) {
+
         PlainSchema schema = plainSchemaDAO().find(schemaKey);
         if (schema == null) {
             LOG.error("Invalid schema name '{}'", schemaKey);
@@ -217,7 +225,7 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
         String entityName = schema.isUniqueConstraint()
                 ? anyUtils().plainAttrUniqueValueClass().getName()
                 : anyUtils().plainAttrValueClass().getName();
-        Query query = findByPlainAttrValueQuery(entityName);
+        Query query = findByPlainAttrValueQuery(entityName, ignoreCaseMatch);
         query.setParameter("schemaKey", schemaKey);
         query.setParameter("stringValue", attrValue.getStringValue());
         query.setParameter("booleanValue", attrValue.getBooleanValue() == null
@@ -243,7 +251,11 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
     }
 
     @Override
-    public A findByPlainAttrUniqueValue(final String schemaKey, final PlainAttrValue attrUniqueValue) {
+    public A findByPlainAttrUniqueValue(
+            final String schemaKey,
+            final PlainAttrValue attrUniqueValue,
+            final boolean ignoreCaseMatch) {
+
         PlainSchema schema = plainSchemaDAO().find(schemaKey);
         if (schema == null) {
             LOG.error("Invalid schema name '{}'", schemaKey);
@@ -254,7 +266,7 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
             return null;
         }
 
-        List<A> result = findByPlainAttrValue(schemaKey, attrUniqueValue);
+        List<A> result = findByPlainAttrValue(schemaKey, attrUniqueValue, ignoreCaseMatch);
         return result.isEmpty()
                 ? null
                 : result.iterator().next();
@@ -283,15 +295,7 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
         return attrValues;
     }
 
-    /**
-     * Generate one where clause for each different attribute schema into the derived schema expression provided.
-     *
-     * @param expression derived schema expression
-     * @param value derived attribute value
-     * @param attrUtils USER / GROUP
-     * @return where clauses to use to build the query
-     */
-    private Set<String> getWhereClause(final String expression, final String value) {
+    private Set<String> getWhereClause(final String expression, final String value, final boolean ignoreCaseMatch) {
         Parser parser = new Parser(new StringReader(expression));
 
         // Schema names
@@ -386,10 +390,16 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
                             bld.append("v.dateValue = '").append(attrValues.get(i)).append("'");
                             break;
                         default:
-                            bld.append("v.stringValue = '").append(attrValues.get(i)).append("'");
+                            if (ignoreCaseMatch) {
+                                bld.append("LOWER(v.stringValue) = '").
+                                        append(attrValues.get(i).toLowerCase()).append("'");
+                            } else {
+                                bld.append("v.stringValue = '").
+                                        append(attrValues.get(i)).append("'");
+                            }
                     }
 
-                    bld.append(")");
+                    bld.append(')');
 
                     used.add(identifiers.get(i));
 
@@ -404,7 +414,7 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
     }
 
     @Override
-    public List<A> findByDerAttrValue(final String schemaKey, final String value) {
+    public List<A> findByDerAttrValue(final String schemaKey, final String value, final boolean ignoreCaseMatch) {
         DerSchema schema = derSchemaDAO().find(schemaKey);
         if (schema == null) {
             LOG.error("Invalid schema name '{}'", schemaKey);
@@ -415,7 +425,7 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
         StringBuilder querystring = new StringBuilder();
 
         boolean subquery = false;
-        for (String clause : getWhereClause(schema.getExpression(), value)) {
+        for (String clause : getWhereClause(schema.getExpression(), value, ignoreCaseMatch)) {
             if (querystring.length() > 0) {
                 subquery = true;
                 querystring.append(" AND a.owner_id IN ( ");
index 6a76358..89939fb 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.syncope.core.persistence.jpa.entity.resource;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
+import javax.persistence.Basic;
 import javax.persistence.Cacheable;
 import javax.persistence.CascadeType;
 import javax.persistence.Entity;
@@ -29,6 +30,8 @@ import javax.persistence.Lob;
 import javax.persistence.OneToMany;
 import javax.persistence.OneToOne;
 import javax.persistence.Table;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnitItem;
@@ -56,6 +59,11 @@ public class JPAOrgUnit extends AbstractGeneratedKeyEntity implements OrgUnit {
     @Lob
     private String serializedSyncToken;
 
+    @Basic
+    @Min(0)
+    @Max(1)
+    private Integer ignoreCaseMatch;
+
     @NotNull
     private String connObjectLink;
 
@@ -103,6 +111,16 @@ public class JPAOrgUnit extends AbstractGeneratedKeyEntity implements OrgUnit {
     }
 
     @Override
+    public boolean isIgnoreCaseMatch() {
+        return isBooleanAsInteger(ignoreCaseMatch);
+    }
+
+    @Override
+    public void setIgnoreCaseMatch(final boolean ignoreCaseMatch) {
+        this.ignoreCaseMatch = getBooleanAsInteger(ignoreCaseMatch);
+    }
+
+    @Override
     public String getConnObjectLink() {
         return connObjectLink;
     }
index 5c10f51..16e85c8 100644 (file)
@@ -20,6 +20,7 @@ package org.apache.syncope.core.persistence.jpa.entity.resource;
 
 import java.util.ArrayList;
 import java.util.List;
+import javax.persistence.Basic;
 import javax.persistence.CascadeType;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
@@ -31,6 +32,8 @@ import javax.persistence.ManyToOne;
 import javax.persistence.OneToOne;
 import javax.persistence.Table;
 import javax.persistence.UniqueConstraint;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
@@ -72,6 +75,11 @@ public class JPAProvision extends AbstractGeneratedKeyEntity implements Provisio
     @Lob
     private String serializedSyncToken;
 
+    @Basic
+    @Min(0)
+    @Max(1)
+    private Integer ignoreCaseMatch;
+
     @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "provision")
     private JPAMapping mapping;
 
@@ -138,6 +146,16 @@ public class JPAProvision extends AbstractGeneratedKeyEntity implements Provisio
     }
 
     @Override
+    public boolean isIgnoreCaseMatch() {
+        return isBooleanAsInteger(ignoreCaseMatch);
+    }
+
+    @Override
+    public void setIgnoreCaseMatch(final boolean ignoreCaseMatch) {
+        this.ignoreCaseMatch = getBooleanAsInteger(ignoreCaseMatch);
+    }
+
+    @Override
     public Mapping getMapping() {
         return mapping;
     }
index fa32394..47f47d8 100644 (file)
@@ -88,36 +88,50 @@ public class UserTest extends AbstractTest {
     }
 
     @Test
-    public void findByDerAttributeValue() {
-        final List<User> list = userDAO.findByDerAttrValue("cn", "Vivaldi, Antonio");
+    public void findByDerAttrValue() {
+        List<User> list = userDAO.findByDerAttrValue("cn", "Vivaldi, Antonio", false);
+        assertEquals(1, list.size());
+
+        list = userDAO.findByDerAttrValue("cn", "VIVALDI, ANTONIO", false);
+        assertEquals(0, list.size());
+
+        list = userDAO.findByDerAttrValue("cn", "VIVALDI, ANTONIO", true);
         assertEquals(1, list.size());
     }
 
     @Test
     public void findByInvalidDerAttrValue() {
-        assertTrue(userDAO.findByDerAttrValue("cn", "Antonio, Maria, Rossi").isEmpty());
+        assertTrue(userDAO.findByDerAttrValue("cn", "Antonio, Maria, Rossi", false).isEmpty());
     }
 
     @Test
     public void findByInvalidDerAttrExpression() {
-        assertTrue(userDAO.findByDerAttrValue("noschema", "Antonio, Maria").isEmpty());
+        assertTrue(userDAO.findByDerAttrValue("noschema", "Antonio, Maria", false).isEmpty());
     }
 
     @Test
-    public void findByAttributeValue() {
-        final UPlainAttrValue fullnameValue = entityFactory.newEntity(UPlainAttrValue.class);
+    public void findByPlainAttrValue() {
+        UPlainAttrValue fullnameValue = entityFactory.newEntity(UPlainAttrValue.class);
         fullnameValue.setStringValue("Gioacchino Rossini");
 
-        final List<User> list = userDAO.findByPlainAttrValue("fullname", fullnameValue);
+        List<User> list = userDAO.findByPlainAttrValue("fullname", fullnameValue, false);
+        assertEquals(1, list.size());
+
+        fullnameValue.setStringValue("Gioacchino ROSSINI");
+
+        list = userDAO.findByPlainAttrValue("fullname", fullnameValue, false);
+        assertEquals(0, list.size());
+
+        list = userDAO.findByPlainAttrValue("fullname", fullnameValue, true);
         assertEquals(1, list.size());
     }
 
     @Test
-    public void findByAttributeBooleanValue() {
+    public void findByPlainAttrBooleanValue() {
         final UPlainAttrValue coolValue = entityFactory.newEntity(UPlainAttrValue.class);
         coolValue.setBooleanValue(true);
 
-        final List<User> list = userDAO.findByPlainAttrValue("cool", coolValue);
+        final List<User> list = userDAO.findByPlainAttrValue("cool", coolValue, false);
         assertEquals(1, list.size());
     }
 
index eddafed..561e089 100644 (file)
@@ -241,11 +241,11 @@ public class UserTest extends AbstractTest {
         assertNotNull(firstname);
 
         // search by ksuffix derived attribute
-        List<User> list = userDAO.findByDerAttrValue("ksuffix", firstname + "k");
+        List<User> list = userDAO.findByDerAttrValue("ksuffix", firstname + "k", false);
         assertEquals(1, list.size());
 
         // search by kprefix derived attribute
-        list = userDAO.findByDerAttrValue("kprefix", "k" + firstname);
+        list = userDAO.findByDerAttrValue("kprefix", "k" + firstname, false);
         assertEquals(1, list.size());
     }
 
index a0b21cb..b699c87 100644 (file)
@@ -143,10 +143,15 @@ public interface Connector {
      *
      * @param objectClass ConnId's object class
      * @param connObjectKey ConnId's key attribute
+     * @param ignoreCaseMatch whether match should be performed regardless of the value case
      * @param options ConnId's OperationOptions
      * @return ConnId's connector object for given uid
      */
-    ConnectorObject getObject(ObjectClass objectClass, Attribute connObjectKey, OperationOptions options);
+    ConnectorObject getObject(
+            ObjectClass objectClass,
+            Attribute connObjectKey,
+            boolean ignoreCaseMatch,
+            OperationOptions options);
 
     /**
      * Search for remote objects.
index 79fbd23..22cc25d 100644 (file)
@@ -100,13 +100,17 @@ public class AsyncConnectorFacade {
             final ConnectorFacade connector,
             final ObjectClass objectClass,
             final Attribute connObjectKey,
+            final boolean ignoreCaseMatch,
             final OperationOptions options) {
 
         ConnectorObject[] objects = new ConnectorObject[1];
-        connector.search(objectClass, FilterBuilder.equalTo(connObjectKey), connectorObject -> {
-            objects[0] = connectorObject;
-            return false;
-        }, options);
+        connector.search(
+                objectClass,
+                ignoreCaseMatch ? FilterBuilder.equalsIgnoreCase(connObjectKey) : FilterBuilder.equalTo(connObjectKey),
+                connectorObject -> {
+                    objects[0] = connectorObject;
+                    return false;
+                }, options);
 
         return new AsyncResult<>(objects[0]);
     }
index 523ecc0..dfca75d 100644 (file)
@@ -392,12 +392,13 @@ public class ConnectorFacadeProxy implements Connector {
     public ConnectorObject getObject(
             final ObjectClass objectClass,
             final Attribute connObjectKey,
+            final boolean ignoreCaseMatch,
             final OperationOptions options) {
 
         Future<ConnectorObject> future = null;
 
         if (connInstance.getCapabilities().contains(ConnectorCapability.SEARCH)) {
-            future = asyncFacade.getObject(connector, objectClass, connObjectKey, options);
+            future = asyncFacade.getObject(connector, objectClass, connObjectKey, ignoreCaseMatch, options);
         } else {
             LOG.info("Search was attempted, although the connector only has these capabilities: {}. No action.",
                     connInstance.getCapabilities());
index 0977a60..b6c29ee 100644 (file)
@@ -74,7 +74,7 @@ public class VirAttrHandlerImpl implements VirAttrHandler {
 
         Map<Provision, Set<VirSchema>> toRead = new HashMap<>();
 
-        for (VirSchema schema : schemas) {
+        schemas.forEach(schema -> {
             if (ownedResources.contains(schema.getProvision().getResource())) {
                 VirAttrCacheValue virAttrCacheValue =
                         virAttrCache.get(any.getType().getKey(), any.getKey(), schema.getKey());
@@ -94,35 +94,36 @@ public class VirAttrHandlerImpl implements VirAttrHandler {
                 LOG.debug("Not considering {} since {} is not assigned to {}",
                         schema, any, schema.getProvision().getResource());
             }
-        }
+        });
 
-        for (Map.Entry<Provision, Set<VirSchema>> entry : toRead.entrySet()) {
-            LOG.debug("About to read from {}: {}", entry.getKey(), entry.getValue());
+        toRead.forEach((provision, schemasToRead) -> {
+            LOG.debug("About to read from {}: {}", provision, schemasToRead);
 
-            Optional<MappingItem> connObjectKeyItem = MappingUtils.getConnObjectKeyItem(entry.getKey());
+            Optional<MappingItem> connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision);
             String connObjectKeyValue = connObjectKeyItem.isPresent()
-                    ? mappingManager.getConnObjectKeyValue(any, entry.getKey()).orElse(null)
+                    ? mappingManager.getConnObjectKeyValue(any, provision).orElse(null)
                     : null;
             if (!connObjectKeyItem.isPresent() || connObjectKeyValue == null) {
-                LOG.error("No ConnObjectKey or value found for {}, ignoring...", entry.getKey());
+                LOG.error("No ConnObjectKey or value found for {}, ignoring...", provision);
             } else {
                 Set<MappingItem> linkingMappingItems = new HashSet<>();
                 linkingMappingItems.add(connObjectKeyItem.get());
-                linkingMappingItems.addAll(entry.getValue().stream().
+                linkingMappingItems.addAll(schemasToRead.stream().
                         map(schema -> schema.asLinkingMappingItem()).collect(Collectors.toSet()));
 
-                Connector connector = connFactory.getConnector(entry.getKey().getResource());
+                Connector connector = connFactory.getConnector(provision.getResource());
                 try {
                     ConnectorObject connectorObject = connector.getObject(
-                            entry.getKey().getObjectClass(),
+                            provision.getObjectClass(),
                             AttributeBuilder.build(connObjectKeyItem.get().getExtAttrName(), connObjectKeyValue),
+                            provision.isIgnoreCaseMatch(),
                             MappingUtils.buildOperationOptions(linkingMappingItems.iterator()));
 
                     if (connectorObject == null) {
                         LOG.debug("No read from {} with filter '{} == {}'",
-                                entry.getKey(), connObjectKeyItem.get().getExtAttrName(), connObjectKeyValue);
+                                provision, connObjectKeyItem.get().getExtAttrName(), connObjectKeyValue);
                     } else {
-                        entry.getValue().forEach(schema -> {
+                        schemasToRead.forEach(schema -> {
                             Attribute attr = connectorObject.getAttributeByName(schema.getExtAttrName());
                             if (attr != null) {
                                 VirAttrCacheValue virAttrCacheValue = new VirAttrCacheValue();
@@ -137,10 +138,10 @@ public class VirAttrHandlerImpl implements VirAttrHandler {
                         });
                     }
                 } catch (Exception e) {
-                    LOG.error("Error reading from {}", entry.getKey(), e);
+                    LOG.error("Error reading from {}", provision, e);
                 }
             }
-        }
+        });
 
         return result;
     }
index 6154658..122e153 100644 (file)
@@ -197,6 +197,8 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
                 provision.getAuxClasses().
                         removeIf(anyTypeClass -> !provisionTO.getAuxClasses().contains(anyTypeClass.getKey()));
 
+                provision.setIgnoreCaseMatch(provisionTO.isIgnoreCaseMatch());
+
                 if (provisionTO.getMapping() == null) {
                     provision.setMapping(null);
                 } else {
@@ -280,6 +282,8 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
             }
             orgUnit.setObjectClass(new ObjectClass(orgUnitTO.getObjectClass()));
 
+            orgUnit.setIgnoreCaseMatch(orgUnitTO.isIgnoreCaseMatch());
+
             if (orgUnitTO.getConnObjectLink() == null) {
                 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidOrgUnit);
                 sce.getElements().add("Null connObjectLink");
@@ -594,6 +598,7 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
             provisionTO.getAuxClasses().addAll(provision.getAuxClasses().stream().
                     map(cls -> cls.getKey()).collect(Collectors.toList()));
             provisionTO.setSyncToken(provision.getSerializedSyncToken());
+            provisionTO.setIgnoreCaseMatch(provision.isIgnoreCaseMatch());
 
             if (provision.getMapping() != null) {
                 MappingTO mappingTO = new MappingTO();
@@ -625,6 +630,7 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
             orgUnitTO.setKey(orgUnit.getKey());
             orgUnitTO.setObjectClass(orgUnit.getObjectClass().getObjectClassValue());
             orgUnitTO.setSyncToken(orgUnit.getSerializedSyncToken());
+            orgUnitTO.setIgnoreCaseMatch(orgUnit.isIgnoreCaseMatch());
             orgUnitTO.setConnObjectLink(orgUnit.getConnObjectLink());
             populateItems(orgUnit.getItems(), orgUnitTO);
 
index da358fd..63c6457 100644 (file)
@@ -296,6 +296,7 @@ public class ReconciliationReportlet extends AbstractReportlet {
                     ConnectorObject connectorObject = connector.getObject(
                             provision.getObjectClass(),
                             AttributeBuilder.build(connObjectKeyItem.get().getExtAttrName(), connObjectKeyValue),
+                            provision.isIgnoreCaseMatch(),
                             MappingUtils.buildOperationOptions(provision.getMapping().getItems().iterator()));
 
                     if (connectorObject == null) {
index 4a43019..a3e7132 100644 (file)
@@ -606,6 +606,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
                 obj = connector.getObject(
                         new ObjectClass(task.getObjectClassName()),
                         AttributeBuilder.build(connObjectKeyItem.get().getExtAttrName(), connObjectKey),
+                        provision.isIgnoreCaseMatch(),
                         MappingUtils.buildOperationOptions(new IteratorChain<>(
                                 MappingUtils.getPropagationItems(provision.getMapping().getItems()).iterator(),
                                 linkingMappingItems.iterator())));
@@ -656,6 +657,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
             try {
                 obj = connector.getObject(new ObjectClass(task.getObjectClassName()),
                         AttributeBuilder.build(connObjectKeyItem.get().getExtAttrName(), connObjectKey),
+                        orgUnit.isIgnoreCaseMatch(),
                         MappingUtils.buildOperationOptions(
                                 MappingUtils.getPropagationItems(orgUnit.getItems()).iterator()));
             } catch (TimeoutException toe) {
index 63f397b..2c10a12 100644 (file)
@@ -199,6 +199,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
             final ObjectClass objectClass,
             final String connObjectKey,
             final String connObjectKeyValue,
+            final boolean ignoreCaseMatch,
             final Iterator<? extends Item> iterator) {
 
         ConnectorObject obj = null;
@@ -206,6 +207,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
             obj = profile.getConnector().getObject(
                     objectClass,
                     AttributeBuilder.build(connObjectKey, connObjectKeyValue),
+                    ignoreCaseMatch,
                     MappingUtils.buildOperationOptions(iterator));
         } catch (TimeoutException toe) {
             LOG.debug("Request timeout", toe);
@@ -272,6 +274,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
                     provision.get().getObjectClass(),
                     connObjectKey.get().getExtAttrName(),
                     connObjecKeyValue.get(),
+                    provision.get().isIgnoreCaseMatch(),
                     provision.get().getMapping().getItems().iterator());
         } else {
             LOG.debug("ConnObjectKeyItem {} or its value {} are null", connObjectKey, connObjecKeyValue);
@@ -445,6 +448,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
                             provision.get().getObjectClass(),
                             connObjectKey.get().getExtAttrName(),
                             connObjecKeyValue.get(),
+                            provision.get().isIgnoreCaseMatch(),
                             provision.get().getMapping().getItems().iterator());
                 }
             } catch (IgnoreProvisionException e) {
index 434d275..2a4a510 100644 (file)
@@ -163,6 +163,7 @@ public class DefaultRealmPushResultHandler
             final ObjectClass objectClass,
             final String connObjectKey,
             final String connObjectKeyValue,
+            final boolean ignoreCaseMatch,
             final Iterator<? extends Item> iterator) {
 
         ConnectorObject obj = null;
@@ -170,6 +171,7 @@ public class DefaultRealmPushResultHandler
             obj = profile.getConnector().getObject(
                     objectClass,
                     AttributeBuilder.build(connObjectKey, connObjectKeyValue),
+                    ignoreCaseMatch,
                     MappingUtils.buildOperationOptions(iterator));
         } catch (TimeoutException toe) {
             LOG.debug("Request timeout", toe);
@@ -205,6 +207,7 @@ public class DefaultRealmPushResultHandler
                     orgUnit.getObjectClass(),
                     connObjectKey.get().getExtAttrName(),
                     connObjecKeyValue.get(),
+                    orgUnit.isIgnoreCaseMatch(),
                     orgUnit.getItems().iterator());
         } else {
             LOG.debug("OrgUnitItem {} or its value {} are null", connObjectKey, connObjecKeyValue);
@@ -383,6 +386,7 @@ public class DefaultRealmPushResultHandler
                             orgUnit.getObjectClass(),
                             connObjectKey.get().getExtAttrName(),
                             connObjecKeyValue.get(),
+                            orgUnit.isIgnoreCaseMatch(),
                             orgUnit.getItems().iterator());
                 }
             } catch (IgnoreProvisionException e) {
index fd66d1f..301e33f 100644 (file)
@@ -105,7 +105,7 @@ public class LDAPMembershipPullActions extends SchedulingPullActions {
         if (membAttr == null) {
             OperationOptionsBuilder oob = new OperationOptionsBuilder();
             oob.setAttributesToGet(groupMemberName);
-            ConnectorObject remoteObj = connector.getObject(ObjectClass.GROUP, delta.getUid(), oob.build());
+            ConnectorObject remoteObj = connector.getObject(ObjectClass.GROUP, delta.getUid(), false, oob.build());
             if (remoteObj == null) {
                 LOG.debug("Object for '{}' not found", delta.getUid().getUidValue());
             } else {
@@ -177,7 +177,8 @@ public class LDAPMembershipPullActions extends SchedulingPullActions {
                     anyTypeDAO.findUser(),
                     name,
                     profile.getTask().getResource(),
-                    profile.getConnector());
+                    profile.getConnector(),
+                    false);
             if (userKey.isPresent()) {
                 resolvedMemberships.put(userKey.get(), memb);
             } else {
index f8b73a2..95a4dc1 100644 (file)
@@ -30,6 +30,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.collections.IteratorChain;
 import org.apache.syncope.common.lib.types.ConflictResolutionAction;
 import org.apache.commons.lang3.tuple.MutablePair;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
@@ -123,7 +124,11 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
         return status.get();
     }
 
-    protected void setGroupOwners(final GroupPullResultHandler ghandler) {
+    protected void setGroupOwners(
+            final GroupPullResultHandler ghandler,
+            final boolean userIgnoreCaseMatch,
+            final boolean groupIgnoreCaseMatch) {
+
         ghandler.getGroupOwnerMap().entrySet().stream().map(entry -> {
             Group group = groupDAO.find(entry.getKey());
             if (group == null) {
@@ -137,7 +142,8 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
                         anyTypeDAO.findUser(),
                         entry.getValue(),
                         ghandler.getProfile().getTask().getResource(),
-                        ghandler.getProfile().getConnector());
+                        ghandler.getProfile().getConnector(),
+                        userIgnoreCaseMatch);
 
                 if (userKey.isPresent()) {
                     group.setUserOwner(userDAO.find(userKey.get()));
@@ -146,7 +152,8 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
                             anyTypeDAO.findGroup(),
                             entry.getValue(),
                             ghandler.getProfile().getTask().getResource(),
-                            ghandler.getProfile().getConnector());
+                            ghandler.getProfile().getConnector(),
+                            groupIgnoreCaseMatch);
 
                     if (groupKey.isPresent()) {
                         group.setGroupOwner(groupDAO.find(groupKey.get()));
@@ -272,8 +279,16 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
         // ...then provisions for any types
         SyncopePullResultHandler handler;
         GroupPullResultHandler ghandler = buildGroupHandler();
+        boolean userIgnoreCaseMatch = false;
+        boolean groupIgnoreCaseMatch = false;
         for (Provision provision : pullTask.getResource().getProvisions()) {
             if (provision.getMapping() != null) {
+                if (provision.getAnyType().getKind() == AnyTypeKind.USER) {
+                    userIgnoreCaseMatch = provision.isIgnoreCaseMatch();
+                } else if (provision.getAnyType().getKind() == AnyTypeKind.GROUP) {
+                    groupIgnoreCaseMatch = provision.isIgnoreCaseMatch();
+                }
+
                 status.set("Pulling " + provision.getObjectClass().getObjectClassValue());
 
                 switch (provision.getAnyType().getKind()) {
@@ -340,7 +355,7 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
             }
         }
         try {
-            setGroupOwners(ghandler);
+            setGroupOwners(ghandler, userIgnoreCaseMatch, groupIgnoreCaseMatch);
         } catch (Exception e) {
             LOG.error("While setting group owners", e);
         }
index 941e5e4..0d61f1b 100644 (file)
@@ -58,16 +58,19 @@ import org.identityconnectors.framework.common.objects.AttributeUtil;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 import org.identityconnectors.framework.common.objects.Name;
 import org.identityconnectors.framework.common.objects.OperationalAttributes;
-import org.identityconnectors.framework.common.objects.filter.EqualsFilter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule;
+import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
+import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
 import org.apache.syncope.core.provisioning.api.data.ItemTransformer;
 import org.apache.syncope.core.spring.ImplementationManager;
+import org.identityconnectors.framework.common.objects.filter.FilterBuilder;
 
 @Transactional(readOnly = true)
 @Component
@@ -118,7 +121,8 @@ public class PullUtils {
             final AnyType anyType,
             final String name,
             final ExternalResource resource,
-            final Connector connector) {
+            final Connector connector,
+            final boolean ignoreCaseMatch) {
 
         Optional<? extends Provision> provision = resource.getProvision(anyType);
         if (!provision.isPresent()) {
@@ -129,9 +133,11 @@ public class PullUtils {
 
         AnyUtils anyUtils = anyUtilsFactory.getInstance(anyType.getKind());
 
-        final List<ConnectorObject> found = new ArrayList<>();
+        List<ConnectorObject> found = new ArrayList<>();
+        Name nameAttr = new Name(name);
         connector.search(provision.get().getObjectClass(),
-                new EqualsFilter(new Name(name)), obj -> found.add(obj),
+                ignoreCaseMatch ? FilterBuilder.equalsIgnoreCase(nameAttr) : FilterBuilder.equalTo(nameAttr),
+                obj -> found.add(obj),
                 MappingUtils.buildOperationOptions(
                         MappingUtils.getPullItems(provision.get().getMapping().getItems()).iterator()));
 
@@ -211,20 +217,45 @@ public class PullUtils {
                     break;
 
                 case "username":
-                    User user = userDAO.findByUsername(connObjectKey);
-                    if (user != null) {
-                        result.add(user.getKey());
+                    if (provision.getAnyType().getKind() == AnyTypeKind.USER && provision.isIgnoreCaseMatch()) {
+                        AnyCond cond = new AnyCond(AttributeCond.Type.IEQ);
+                        cond.setSchema("username");
+                        cond.setExpression(connObjectKey);
+                        result.addAll(searchDAO.search(SearchCond.getLeafCond(cond), AnyTypeKind.USER).
+                                stream().map(Entity::getKey).collect(Collectors.toList()));
+                    } else {
+                        User user = userDAO.findByUsername(connObjectKey);
+                        if (user != null) {
+                            result.add(user.getKey());
+                        }
                     }
                     break;
 
                 case "name":
-                    Group group = groupDAO.findByName(connObjectKey);
-                    if (group != null) {
-                        result.add(group.getKey());
+                    if (provision.getAnyType().getKind() == AnyTypeKind.GROUP && provision.isIgnoreCaseMatch()) {
+                        AnyCond cond = new AnyCond(AttributeCond.Type.IEQ);
+                        cond.setSchema("name");
+                        cond.setExpression(connObjectKey);
+                        result.addAll(searchDAO.search(SearchCond.getLeafCond(cond), AnyTypeKind.GROUP).
+                                stream().map(Entity::getKey).collect(Collectors.toList()));
+                    } else {
+                        Group group = groupDAO.findByName(connObjectKey);
+                        if (group != null) {
+                            result.add(group.getKey());
+                        }
                     }
-                    AnyObject anyObject = anyObjectDAO.findByName(connObjectKey);
-                    if (anyObject != null) {
-                        result.add(anyObject.getKey());
+
+                    if (provision.getAnyType().getKind() == AnyTypeKind.ANY_OBJECT && provision.isIgnoreCaseMatch()) {
+                        AnyCond cond = new AnyCond(AttributeCond.Type.IEQ);
+                        cond.setSchema("name");
+                        cond.setExpression(connObjectKey);
+                        result.addAll(searchDAO.search(SearchCond.getLeafCond(cond), AnyTypeKind.ANY_OBJECT).
+                                stream().map(Entity::getKey).collect(Collectors.toList()));
+                    } else {
+                        AnyObject anyObject = anyObjectDAO.findByName(connObjectKey);
+                        if (anyObject != null) {
+                            result.add(anyObject.getKey());
+                        }
                     }
                     break;
 
@@ -247,15 +278,15 @@ public class PullUtils {
                         }
                     }
 
-                    result.addAll(anyUtils.dao().
-                            findByPlainAttrValue(intAttrName.getSchemaName(), value).stream().
-                            map(Entity::getKey).collect(Collectors.toList()));
+                    result.addAll(anyUtils.dao().findByPlainAttrValue(
+                            intAttrName.getSchemaName(), value, provision.isIgnoreCaseMatch()).
+                            stream().map(Entity::getKey).collect(Collectors.toList()));
                     break;
 
                 case DERIVED:
-                    result.addAll(anyUtils.dao().
-                            findByDerAttrValue(intAttrName.getSchemaName(), connObjectKey).stream().
-                            map(Entity::getKey).collect(Collectors.toList()));
+                    result.addAll(anyUtils.dao().findByDerAttrValue(
+                            intAttrName.getSchemaName(), connObjectKey, provision.isIgnoreCaseMatch()).
+                            stream().map(Entity::getKey).collect(Collectors.toList()));
                     break;
 
                 default:
@@ -325,7 +356,7 @@ public class PullUtils {
         String connObjectKey = null;
 
         Optional<? extends OrgUnitItem> connObjectKeyItem = orgUnit.getConnObjectKeyItem();
-        if (connObjectKeyItem != null) {
+        if (connObjectKeyItem.isPresent()) {
             Attribute connObjectKeyAttr = connObj.getAttributeByName(connObjectKeyItem.get().getExtAttrName());
             if (connObjectKeyAttr != null) {
                 connObjectKey = AttributeUtil.getStringValue(connObjectKeyAttr);
@@ -357,8 +388,15 @@ public class PullUtils {
                 break;
 
             case "name":
-                result.addAll(realmDAO.findByName(connObjectKey).stream().
-                        map(Entity::getKey).collect(Collectors.toList()));
+                if (orgUnit.isIgnoreCaseMatch()) {
+                    final String realmName = connObjectKey;
+                    result.addAll(realmDAO.findAll().stream().
+                            filter(r -> r.getName().equalsIgnoreCase(realmName)).
+                            map(Entity::getKey).collect(Collectors.toList()));
+                } else {
+                    result.addAll(realmDAO.findByName(connObjectKey).stream().
+                            map(Entity::getKey).collect(Collectors.toList()));
+                }
                 break;
 
             case "fullpath":
index 65151dc..d0d2b21 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.syncope.core.provisioning.java.pushpull;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 import org.apache.syncope.common.lib.collections.IteratorChain;
@@ -166,8 +167,16 @@ public class SinglePullJobDelegate extends PullJobDelegate implements SyncopeSin
                     handler,
                     options);
 
+            Optional<? extends Provision> userProvision = provision.getResource().getProvision(anyTypeDAO.findUser());
+            boolean userIgnoreCaseMatch = userProvision.isPresent()
+                    ? userProvision.get().isIgnoreCaseMatch()
+                    : false;
+            Optional<? extends Provision> groupProvision = provision.getResource().getProvision(anyTypeDAO.findGroup());
+            boolean groupIgnoreCaseMatch = groupProvision.isPresent()
+                    ? groupProvision.get().isIgnoreCaseMatch()
+                    : false;
             try {
-                setGroupOwners(ghandler);
+                setGroupOwners(ghandler, userIgnoreCaseMatch, groupIgnoreCaseMatch);
             } catch (Exception e) {
                 LOG.error("While setting group owners", e);
             }
index d3f763b..3393753 100644 (file)
@@ -155,13 +155,13 @@ public class SAML2UserManager {
                         }
                     }
 
-                    result.addAll(userDAO.findByPlainAttrValue(intAttrName.getSchemaName(), value).stream().
-                            map(user -> user.getUsername()).collect(Collectors.toList()));
+                    result.addAll(userDAO.findByPlainAttrValue(intAttrName.getSchemaName(), value, false).stream().
+                            map(User::getUsername).collect(Collectors.toList()));
                     break;
 
                 case DERIVED:
-                    result.addAll(userDAO.findByDerAttrValue(intAttrName.getSchemaName(), transformed).stream().
-                            map(user -> user.getUsername()).collect(Collectors.toList()));
+                    result.addAll(userDAO.findByDerAttrValue(intAttrName.getSchemaName(), transformed, false).stream().
+                            map(User::getUsername).collect(Collectors.toList()));
                     break;
 
                 default:
diff --git a/pom.xml b/pom.xml
index 32fe251..f8213d0 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -356,7 +356,7 @@ under the License.
   <properties>
     <syncope.version>${project.version}</syncope.version>
 
-    <connid.version>1.4.3.0</connid.version>
+    <connid.version>1.4.4.0-SNAPSHOT</connid.version>
     <connid.soap.version>1.4.2-SNAPSHOT</connid.soap.version>
     <connid.rest.version>1.0.2</connid.rest.version>
     <connid.database.version>2.2.5</connid.database.version>
index 754959e..1cd54ed 100644 (file)
@@ -155,6 +155,8 @@ Besides the items documented above, some more data needs to be specified for a c
 http://connid.tirasa.net/apidocs/1.4/org/identityconnectors/framework/common/objects/ObjectClass.html[object class^]
 shall be used during communication with the Identity Store; predefined are `\\__ACCOUNT__` for Users and 
 `\\__GROUP__` for Groups
+* whether matches between user / group / any object's attribute values and their counterparts on the Identity Store
+should be performed in a case-sensitive fashion or not
 * Object link - only required by some connector bundles as
 https://connid.atlassian.net/wiki/display/BASE/LDAP[LDAP^] and
 https://connid.atlassian.net/wiki/pages/viewpage.action?pageId=360482[Active Directory^], generally specifies the model
index b37f34d..2d88bf4 100644 (file)
@@ -25,8 +25,8 @@ Pull is the mechanism used to acquire identity data from Identity Stores; for ea
 Pull task execution involves querying the external resource and then processing each entity in an isolated transaction; 
 a retrieved entity can be:
 
-. _matching_ if a corresponding internal entity was found, according to the <<policies-pull,pull policy>> set for the
-enclosing external resource;
+. _matching_ if a corresponding internal entity was found, according to the <<mapping,mapping>> of - or the
+<<policies-pull,pull policy>> set for, if present - the enclosing external resource;
 . _unmatching_ otherwise.
 
 Once this has been assessed, entities are processed according to the matching / unmatching rules specified for the pull task: