You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2022/12/17 18:19:00 UTC

[syncope] branch master updated: Replacing ApacheDS with UnboundID SDK as test LDAP server

This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/master by this push:
     new 7ad9a72d58 Replacing ApacheDS with UnboundID SDK as test LDAP server
7ad9a72d58 is described below

commit 7ad9a72d58d536cc6413a95a0e6eb335c7c0626a
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Sat Dec 17 19:18:49 2022 +0100

    Replacing ApacheDS with UnboundID SDK as test LDAP server
---
 .../src/test/resources/domains/MasterContent.xml   |  14 +-
 .../src/test/resources/domains/MasterContent.xml   |  14 +-
 .../LDAPPasswordPropagationActions.java            |  48 +-
 fit/build-tools/pom.xml                            |  16 +-
 .../normalization/NormalizationInterceptor.java    | 629 ---------------------
 .../fit/buildtools/ApacheDSRootDseServlet.java     | 100 ----
 .../fit/buildtools/ApacheDSStartStopListener.java  | 276 ---------
 .../fit/buildtools/LDAPStartStopListener.java      |  89 +++
 .../fit/buildtools/LdifInputStreamLoader.java      | 116 ----
 .../buildtools/SyncopeBuildToolsApplication.java   |  14 +-
 .../buildtools/cxf/DateParamConverterProvider.java |   6 +-
 .../src/main/resources/application.properties      |   3 +
 fit/build-tools/src/main/resources/content.ldif    |  16 +-
 .../org/apache/syncope/fit/core/SearchITCase.java  |   2 +-
 pom.xml                                            |  41 +-
 src/main/asciidoc/getting-started/obtain.adoc      |   2 +-
 standalone/pom.xml                                 |   2 +-
 17 files changed, 156 insertions(+), 1232 deletions(-)

diff --git a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
index bd3532be8a..70326e7b38 100644
--- a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
@@ -63,7 +63,7 @@ under the License.
       
   <!-- Authentication modules -->
   <AuthModule id="DefaultLDAPAuthModule" authModuleState="ACTIVE"
-              description="LDAP auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.LDAPAuthModuleConf","userIdAttribute":"cn","bindDn": "uid=admin,ou=system", "bindCredential":"secret","ldapUrl":"ldap://localhost:1389","searchFilter":"cn={user}","baseDn":"ou=People,o=isp","subtreeSearch":true,"principalAttributeList":["sn","givenName","mail","cn"]}'
+              description="LDAP auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.LDAPAuthModuleConf","userIdAttribute":"cn","bindDn": "${testds.bindDn}", "bindCredential":"${testds.password}","ldapUrl":"ldap://localhost:${testds.port}","searchFilter":"cn={user}","baseDn":"ou=People,${testds.rootDn}","subtreeSearch":true,"principalAttributeList":["sn","givenName","mail","cn"]}'
               items='[{"intAttrName":"mail","extAttrName":"mail","connObjectKey":false,"password":false,"mandatoryCondition":"false","purpose":"NONE","propagationJEXLTransformer":null,"pullJEXLTransformer":null,"transformers":[]},{"intAttrName":"givenName","extAttrName":"givenName","connObjectKey":false,"password":false,"mandatoryCondition":"false","purpose":"NONE","propagationJEXLTransformer":null,"pullJEXLTransformer":null,"transformers":[]},{"intAttrName":"sn","extAttrName":"sn","conn [...]
   <AuthModule id="DefaultJDBCAuthModule" authModuleState="ACTIVE"
               description="JDBC auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.JDBCAuthModuleConf","sql":"SELECT * FROM users_table WHERE name=?", "fieldPassword": "password"}'/>
@@ -89,7 +89,7 @@ under the License.
 
   <!-- Attribute repositories -->
   <AttrRepo id="DefaultLDAPAttrRepo" attrRepoState="ACTIVE"
-            description="LDAP attr repo" jsonConf='{"_class":"org.apache.syncope.common.lib.attr.LDAPAttrRepoConf","searchFilter":"cn={user}","subtreeSearch":true,"ldapUrl":"ldap://localhost:1389","bindDn":"uid=admin,ou=system","bindCredential":"secret","baseDn":"ou=People,o=isp","attributes":{},"useAllQueryAttributes":true,"queryAttributes":{}}'/>
+            description="LDAP attr repo" jsonConf='{"_class":"org.apache.syncope.common.lib.attr.LDAPAttrRepoConf","searchFilter":"cn={user}","subtreeSearch":true,"ldapUrl":"ldap://localhost:${testds.port}","bindDn":"${testds.bindDn}","bindCredential":"${testds.password}","baseDn":"ou=People,${testds.rootDn}","attributes":{},"useAllQueryAttributes":true,"queryAttributes":{}}'/>
   <AttrRepo id="DefaultJDBCAttrRepo" attrRepoState="ACTIVE"
             description="JDBC attr repo" jsonConf='{"_class":"org.apache.syncope.common.lib.attr.JDBCAttrRepoConf","sql":"SELECT * FROM table WHERE name=?","dialect":"org.hibernate.dialect.H2Dialect","driverClass":"org.h2.Driver","url":"jdbc:h2:mem:syncopedb;DB_CLOSE_DELAY=-1","user":"username","password":"password","singleRow":true,"requireAllAttributes":true,"caseCanonicalization":"NONE","queryType":"AND","columnMappings":{},"username":[],"attributes":{},"caseInsensitiveQueryAttributes [...]
   <AttrRepo id="DefaultStubAttrRepo" attrRepoState="ACTIVE"
@@ -505,12 +505,12 @@ under the License.
                 capabilities='["CREATE","UPDATE","DELETE","SEARCH","SYNC"]'/>
     
   <ConnInstance id="74141a3b-0762-4720-a4aa-fc3e374ef3ef"
-                bundleName="net.tirasa.connid.bundles.ldap" displayName="ApacheDS"
+                bundleName="net.tirasa.connid.bundles.ldap" displayName="TestLDAP"
                 adminRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
                 location="${connid.location}"
                 connectorName="net.tirasa.connid.bundles.ldap.LdapConnector"
                 version="${connid.ldap.version}" 
-                jsonConf='[{"schema":{"name":"host","type":"java.lang.String","required":true,"order":1,"confidential":false,"defaultValues":[]},"values":["localhost"],"overridable":false},{"schema":{"name":"port","type":"int","required":false,"order":2,"confidential":false,"defaultValues":[389]},"values":[1389],"overridable":false},{"schema":{"name":"ssl","type":"boolean","required":false,"order":3,"confidential":false,"defaultValues":[false]},"values":["false"],"overridable":false},{"s [...]
+                jsonConf='[{"schema":{"name":"host","type":"java.lang.String","required":true,"order":1,"confidential":false,"defaultValues":[]},"values":["localhost"],"overridable":false},{"schema":{"name":"port","type":"int","required":false,"order":2,"confidential":false,"defaultValues":[389]},"values":[${testds.port}],"overridable":false},{"schema":{"name":"ssl","type":"boolean","required":false,"order":3,"confidential":false,"defaultValues":[false]},"values":["false"],"overridable": [...]
                 capabilities='["CREATE","UPDATE","UPDATE_DELTA","DELETE","SEARCH"]'/>
   
   <ConnInstance id="a28abd9b-9f4a-4ef6-a7a8-d19ad2a8f29d" displayName="H2-test2"
@@ -596,7 +596,7 @@ under the License.
                     randomPwdIfNotProvided="1" enforceMandatoryCondition="1" overrideCapabilities="0"
                     propagationPriority="1"
                     createTraceLevel="ALL" deleteTraceLevel="ALL" updateTraceLevel="ALL" provisioningTraceLevel="ALL"
-                    provisions='[{"anyType":"USER","objectClass":"__ACCOUNT__","auxClasses":["generic membership","minimal group"],"syncToken":null,"ignoreCaseMatch":false,"uidOnCreate":null,"mapping":{"connObjectLink":"&#39;uid=&#39; + username + &#39;,ou=people,o=isp&#39;","items":[{"intAttrName":"username","extAttrName":"cn","connObjectKey":true,"password":false,"mandatoryCondition":"true","purpose":"BOTH","propagationJEXLTransformer":null,"pullJEXLTransformer":null,"transformers":[]} [...]
+                    provisions='[{"anyType":"USER","objectClass":"__ACCOUNT__","auxClasses":["generic membership","minimal group"],"syncToken":null,"ignoreCaseMatch":false,"uidOnCreate":null,"mapping":{"connObjectLink":"&#39;uid=&#39; + username + &#39;,ou=people,${testds.rootDn}&#39;","items":[{"intAttrName":"username","extAttrName":"cn","connObjectKey":true,"password":false,"mandatoryCondition":"true","purpose":"BOTH","propagationJEXLTransformer":null,"pullJEXLTransformer":null,"transf [...]
   <Implementation id="LDAPMembershipPropagationActions" type="PROPAGATION_ACTIONS" engine="JAVA"
                   body="org.apache.syncope.core.provisioning.java.propagation.LDAPMembershipPropagationActions"/>
   <ExternalResourcePropAction resource_id="resource-ldap" implementation_id="LDAPMembershipPropagationActions"/>
@@ -604,8 +604,8 @@ under the License.
                     randomPwdIfNotProvided="1" enforceMandatoryCondition="1" overrideCapabilities="0"
                     propagationPriority="1"
                     createTraceLevel="ALL" deleteTraceLevel="ALL" updateTraceLevel="ALL" provisioningTraceLevel="ALL"
-                    jsonConf='[{"schema":{"name":"uidAttribute","displayName":"Uid Attribute","helpMessage":"The name of the LDAP attribute which is mapped to the Uid attribute. Default is \"entryUUID\".","type":"java.lang.String","required":false,"order":21,"confidential":false,"defaultValues":["entryUUID"]},"overridable":true,"values":["l"]},{"schema":{"name":"baseContexts","displayName":"Base Contexts","helpMessage":"One or more starting points in the LDAP tree that will be used when  [...]
-                    orgUnit='{"objectClass":"organizationalUnit","syncToken":null,"ignoreCaseMatch":false,"connObjectLink":"syncope:fullPath2Dn(fullPath, &#39;ou&#39;) + &#39;,o=isp&#39;","items":[{"intAttrName":"fullpath","extAttrName":"l","connObjectKey":true,"password":false,"mandatoryCondition":"true","purpose":"BOTH","propagationJEXLTransformer":null,"pullJEXLTransformer":null,"transformers":[]},{"intAttrName":"name","extAttrName":"ou","connObjectKey":false,"password":false,"mandato [...]
+                    jsonConf='[{"schema":{"name":"uidAttribute","displayName":"Uid Attribute","helpMessage":"The name of the LDAP attribute which is mapped to the Uid attribute. Default is \"entryUUID\".","type":"java.lang.String","required":false,"order":21,"confidential":false,"defaultValues":["entryUUID"]},"overridable":true,"values":["l"]},{"schema":{"name":"baseContexts","displayName":"Base Contexts","helpMessage":"One or more starting points in the LDAP tree that will be used when  [...]
+                    orgUnit='{"objectClass":"organizationalUnit","syncToken":null,"ignoreCaseMatch":false,"connObjectLink":"syncope:fullPath2Dn(fullPath, &#39;ou&#39;) + &#39;,${testds.rootDn}&#39;","items":[{"intAttrName":"fullpath","extAttrName":"l","connObjectKey":true,"password":false,"mandatoryCondition":"true","purpose":"BOTH","propagationJEXLTransformer":null,"pullJEXLTransformer":null,"transformers":[]},{"intAttrName":"name","extAttrName":"ou","connObjectKey":false,"password":fal [...]
   
   <ExternalResource id="ws-target-resource-nopropagation" connector_id="fcf9f2b0-f7d6-42c9-84a6-61b28255a42b"
                     randomPwdIfNotProvided="0" enforceMandatoryCondition="1" overrideCapabilities="0"
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 2068736e3c..9a0e075325 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -63,7 +63,7 @@ under the License.
 
   <!-- Authentication modules -->
   <AuthModule id="DefaultLDAPAuthModule" authModuleState="ACTIVE"
-              description="LDAP auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.LDAPAuthModuleConf","userIdAttribute":"cn","bindDn": "uid=admin,ou=system", "bindCredential":"secret","ldapUrl":"ldap://localhost:1389","searchFilter":"cn={user}","baseDn":"ou=People,o=isp","subtreeSearch":true,"principalAttributeList":["sn","givenName","mail","cn"]}'
+              description="LDAP auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.LDAPAuthModuleConf","userIdAttribute":"cn","bindDn": "${testds.bindDn}", "bindCredential":"${testds.password}","ldapUrl":"ldap://localhost:${testds.port}","searchFilter":"cn={user}","baseDn":"ou=People,${testds.rootDn}","subtreeSearch":true,"principalAttributeList":["sn","givenName","mail","cn"]}'
               items='[{"intAttrName":"mail","extAttrName":"mail","connObjectKey":false,"password":false,"mandatoryCondition":"false","purpose":"NONE","propagationJEXLTransformer":null,"pullJEXLTransformer":null,"transformers":[]},{"intAttrName":"givenName","extAttrName":"givenName","connObjectKey":false,"password":false,"mandatoryCondition":"false","purpose":"NONE","propagationJEXLTransformer":null,"pullJEXLTransformer":null,"transformers":[]},{"intAttrName":"sn","extAttrName":"sn","conn [...]
   <AuthModule id="DefaultJDBCAuthModule" authModuleState="ACTIVE"
               description="JDBC auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.JDBCAuthModuleConf","sql":"SELECT * FROM users_table WHERE name=?", "fieldPassword": "password"}'/>
@@ -89,7 +89,7 @@ under the License.
 
   <!-- Attribute repositories -->
   <AttrRepo id="DefaultLDAPAttrRepo" attrRepoState="ACTIVE"
-            description="LDAP attr repo" jsonConf='{"_class":"org.apache.syncope.common.lib.attr.LDAPAttrRepoConf","searchFilter":"cn={user}","subtreeSearch":true,"ldapUrl":"ldap://localhost:1389","bindDn":"uid=admin,ou=system","bindCredential":"secret","baseDn":"ou=People,o=isp","attributes":{},"useAllQueryAttributes":true,"queryAttributes":{}}'/>
+            description="LDAP attr repo" jsonConf='{"_class":"org.apache.syncope.common.lib.attr.LDAPAttrRepoConf","searchFilter":"cn={user}","subtreeSearch":true,"ldapUrl":"ldap://localhost:${testds.port}","bindDn":"${testds.bindDn}","bindCredential":"${testds.password}","baseDn":"ou=People,${testds.rootDn}","attributes":{},"useAllQueryAttributes":true,"queryAttributes":{}}'/>
   <AttrRepo id="DefaultJDBCAttrRepo" attrRepoState="ACTIVE"
             description="JDBC attr repo" jsonConf='{"_class":"org.apache.syncope.common.lib.attr.JDBCAttrRepoConf","sql":"SELECT * FROM table WHERE name=?","dialect":"org.hibernate.dialect.H2Dialect","driverClass":"org.h2.Driver","url":"jdbc:h2:mem:syncopedb;DB_CLOSE_DELAY=-1","user":"username","password":"password","singleRow":true,"requireAllAttributes":true,"caseCanonicalization":"NONE","queryType":"AND","columnMappings":{},"username":[],"attributes":{},"caseInsensitiveQueryAttributes [...]
   <AttrRepo id="DefaultStubAttrRepo" attrRepoState="ACTIVE"
@@ -591,12 +591,12 @@ under the License.
                 capabilities='["CREATE","UPDATE","DELETE","SEARCH","SYNC"]'/>
     
   <ConnInstance id="74141a3b-0762-4720-a4aa-fc3e374ef3ef"
-                bundleName="net.tirasa.connid.bundles.ldap" displayName="ApacheDS"
+                bundleName="net.tirasa.connid.bundles.ldap" displayName="TestLDAP"
                 adminRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
                 location="${connid.location}"
                 connectorName="net.tirasa.connid.bundles.ldap.LdapConnector"
                 version="${connid.ldap.version}" 
-                jsonConf='[{"schema":{"name":"host","type":"java.lang.String","required":true,"order":1,"confidential":false,"defaultValues":[]},"values":["localhost"],"overridable":false},{"schema":{"name":"port","type":"int","required":false,"order":2,"confidential":false,"defaultValues":[389]},"values":[1389],"overridable":false},{"schema":{"name":"ssl","type":"boolean","required":false,"order":3,"confidential":false,"defaultValues":[false]},"values":["false"],"overridable":false},{"s [...]
+                jsonConf='[{"schema":{"name":"host","type":"java.lang.String","required":true,"order":1,"confidential":false,"defaultValues":[]},"values":["localhost"],"overridable":false},{"schema":{"name":"port","type":"int","required":false,"order":2,"confidential":false,"defaultValues":[389]},"values":[${testds.port}],"overridable":false},{"schema":{"name":"ssl","type":"boolean","required":false,"order":3,"confidential":false,"defaultValues":[false]},"values":["false"],"overridable": [...]
                 capabilities='["CREATE","UPDATE","UPDATE_DELTA","DELETE","SEARCH"]'/>
   
   <ConnInstance id="a28abd9b-9f4a-4ef6-a7a8-d19ad2a8f29d" displayName="H2-test2"
@@ -682,7 +682,7 @@ under the License.
                     randomPwdIfNotProvided="1" enforceMandatoryCondition="1" overrideCapabilities="0"
                     propagationPriority="1"
                     createTraceLevel="ALL" deleteTraceLevel="ALL" updateTraceLevel="ALL" provisioningTraceLevel="ALL"
-                    provisions='[{"anyType":"USER","objectClass":"__ACCOUNT__","auxClasses":["generic membership","minimal group"],"syncToken":null,"ignoreCaseMatch":false,"uidOnCreate":null,"mapping":{"connObjectLink":"&#39;uid=&#39; + username + &#39;,ou=people,o=isp&#39;","items":[{"intAttrName":"username","extAttrName":"cn","connObjectKey":true,"password":false,"mandatoryCondition":"true","purpose":"BOTH","propagationJEXLTransformer":null,"pullJEXLTransformer":null,"transformers":[]} [...]
+                    provisions='[{"anyType":"USER","objectClass":"__ACCOUNT__","auxClasses":["generic membership","minimal group"],"syncToken":null,"ignoreCaseMatch":false,"uidOnCreate":null,"mapping":{"connObjectLink":"&#39;uid=&#39; + username + &#39;,ou=people,${testds.rootDn}&#39;","items":[{"intAttrName":"username","extAttrName":"cn","connObjectKey":true,"password":false,"mandatoryCondition":"true","purpose":"BOTH","propagationJEXLTransformer":null,"pullJEXLTransformer":null,"transf [...]
   <Implementation id="LDAPMembershipPropagationActions" type="PROPAGATION_ACTIONS" engine="JAVA"
                   body="org.apache.syncope.core.provisioning.java.propagation.LDAPMembershipPropagationActions"/>
   <ExternalResourcePropAction resource_id="resource-ldap" implementation_id="LDAPMembershipPropagationActions"/>
@@ -690,8 +690,8 @@ under the License.
                     randomPwdIfNotProvided="1" enforceMandatoryCondition="1" overrideCapabilities="0"
                     propagationPriority="1"
                     createTraceLevel="ALL" deleteTraceLevel="ALL" updateTraceLevel="ALL" provisioningTraceLevel="ALL"
-                    jsonConf='[{"schema":{"name":"uidAttribute","displayName":"Uid Attribute","helpMessage":"The name of the LDAP attribute which is mapped to the Uid attribute. Default is \"entryUUID\".","type":"java.lang.String","required":false,"order":21,"confidential":false,"defaultValues":["entryUUID"]},"overridable":true,"values":["l"]},{"schema":{"name":"baseContexts","displayName":"Base Contexts","helpMessage":"One or more starting points in the LDAP tree that will be used when  [...]
-                    orgUnit='{"objectClass":"organizationalUnit","syncToken":null,"ignoreCaseMatch":false,"connObjectLink":"syncope:fullPath2Dn(fullPath, &#39;ou&#39;) + &#39;,o=isp&#39;","items":[{"intAttrName":"fullpath","extAttrName":"l","connObjectKey":true,"password":false,"mandatoryCondition":"true","purpose":"BOTH","propagationJEXLTransformer":null,"pullJEXLTransformer":null,"transformers":[]},{"intAttrName":"name","extAttrName":"ou","connObjectKey":false,"password":false,"mandato [...]
+                    jsonConf='[{"schema":{"name":"uidAttribute","displayName":"Uid Attribute","helpMessage":"The name of the LDAP attribute which is mapped to the Uid attribute. Default is \"entryUUID\".","type":"java.lang.String","required":false,"order":21,"confidential":false,"defaultValues":["entryUUID"]},"overridable":true,"values":["l"]},{"schema":{"name":"baseContexts","displayName":"Base Contexts","helpMessage":"One or more starting points in the LDAP tree that will be used when  [...]
+                    orgUnit='{"objectClass":"organizationalUnit","syncToken":null,"ignoreCaseMatch":false,"connObjectLink":"syncope:fullPath2Dn(fullPath, &#39;ou&#39;) + &#39;,${testds.rootDn}&#39;","items":[{"intAttrName":"fullpath","extAttrName":"l","connObjectKey":true,"password":false,"mandatoryCondition":"true","purpose":"BOTH","propagationJEXLTransformer":null,"pullJEXLTransformer":null,"transformers":[]},{"intAttrName":"name","extAttrName":"ou","connObjectKey":false,"password":fal [...]
   
   <ExternalResource id="ws-target-resource-nopropagation" connector_id="fcf9f2b0-f7d6-42c9-84a6-61b28255a42b"
                     randomPwdIfNotProvided="0" enforceMandatoryCondition="1" overrideCapabilities="0"
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPPasswordPropagationActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPPasswordPropagationActions.java
index eb3cee8806..879a014b0a 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPPasswordPropagationActions.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPPasswordPropagationActions.java
@@ -19,13 +19,13 @@
 package org.apache.syncope.core.provisioning.java.propagation;
 
 import java.util.Base64;
+import java.util.Optional;
 import java.util.Set;
 import javax.xml.bind.DatatypeConverter;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.ConnInstance;
-import org.apache.syncope.core.persistence.api.entity.task.PropagationData;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
@@ -39,6 +39,7 @@ import org.identityconnectors.framework.common.objects.AttributeUtil;
 import org.identityconnectors.framework.common.objects.OperationalAttributes;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
 
 /**
  * Propagate a non-cleartext password out to a resource, if the PropagationManager has not already
@@ -48,50 +49,45 @@ import org.springframework.transaction.annotation.Transactional;
 @SyncopeImplementation(scope = InstanceScope.PER_CONTEXT)
 public class LDAPPasswordPropagationActions implements PropagationActions {
 
-    private static final String CLEARTEXT = "CLEARTEXT";
+    protected static final String CLEARTEXT = "CLEARTEXT";
 
     @Autowired
-    private UserDAO userDAO;
+    protected UserDAO userDAO;
 
     @Transactional(readOnly = true)
     @Override
     public void before(final PropagationTaskInfo taskInfo) {
         if (AnyTypeKind.USER == taskInfo.getAnyTypeKind()) {
             User user = userDAO.find(taskInfo.getEntityKey());
+            if (user == null || user.getPassword() == null) {
+                return;
+            }
 
-            PropagationData data = taskInfo.getPropagationData();
-            if (user != null && user.getPassword() != null && data.getAttributes() != null) {
-                Set<Attribute> attrs = data.getAttributes();
-
-                Attribute missing = AttributeUtil.find(PropagationManager.MANDATORY_MISSING_ATTR_NAME, attrs);
-
-                ConnInstance connInstance = taskInfo.getResource().getConnector();
-                String cipherAlgorithm = getCipherAlgorithm(connInstance);
-                if (missing != null && missing.getValue() != null && missing.getValue().size() == 1
-                        && missing.getValue().get(0).equals(OperationalAttributes.PASSWORD_NAME)
-                        && cipherAlgorithmMatches(getCipherAlgorithm(connInstance), user.getCipherAlgorithm())) {
+            Set<Attribute> attrs = taskInfo.getPropagationData().getAttributes();
 
-                    String password = user.getPassword().toLowerCase();
-                    byte[] decodedPassword = DatatypeConverter.parseHexBinary(password);
-                    String base64EncodedPassword = Base64.getEncoder().encodeToString(decodedPassword);
+            String cipherAlgorithm = getCipherAlgorithm(taskInfo.getResource().getConnector());
+            Optional.ofNullable(AttributeUtil.find(PropagationManager.MANDATORY_MISSING_ATTR_NAME, attrs)).
+                    filter(missing -> !CollectionUtils.isEmpty(missing.getValue())
+                    && OperationalAttributes.PASSWORD_NAME.equals(missing.getValue().get(0))
+                    && cipherAlgorithmMatches(cipherAlgorithm, user.getCipherAlgorithm())).
+                    ifPresent(missing -> {
+                        attrs.remove(missing);
 
-                    String cipherPlusPassword = ('{' + cipherAlgorithm.toLowerCase() + '}' + base64EncodedPassword);
+                        byte[] decodedPassword = DatatypeConverter.parseHexBinary(user.getPassword().toLowerCase());
+                        String base64EncodedPassword = Base64.getEncoder().encodeToString(decodedPassword);
 
-                    Attribute passwordAttribute = AttributeBuilder.buildPassword(
-                            new GuardedString(cipherPlusPassword.toCharArray()));
+                        String cipherPlusPassword = '{' + cipherAlgorithm + '}' + base64EncodedPassword;
 
-                    attrs.add(passwordAttribute);
-                    attrs.remove(missing);
-                }
-            }
+                        attrs.add(AttributeBuilder.buildPassword(new GuardedString(cipherPlusPassword.toCharArray())));
+                    });
         }
     }
 
     protected String getCipherAlgorithm(final ConnInstance connInstance) {
         return connInstance.getConf().stream().
                 filter(property -> "passwordHashAlgorithm".equals(property.getSchema().getName())
-                && property.getValues() != null && !property.getValues().isEmpty()).findFirst().
-                map(cipherAlgorithm -> (String) cipherAlgorithm.getValues().get(0)).
+                && !property.getValues().isEmpty()).findFirst().
+                map(cipherAlgorithm -> cipherAlgorithm.getValues().get(0).toString()).
                 orElse(CLEARTEXT);
     }
 
diff --git a/fit/build-tools/pom.xml b/fit/build-tools/pom.xml
index 6c2bb1276a..d99c1b4a76 100644
--- a/fit/build-tools/pom.xml
+++ b/fit/build-tools/pom.xml
@@ -70,20 +70,8 @@ under the License.
     </dependency>
 
     <dependency>
-      <groupId>org.apache.directory.server</groupId>
-      <artifactId>apacheds-core-api</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.directory.server</groupId>
-      <artifactId>apacheds-core-annotations</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.directory.server</groupId>
-      <artifactId>apacheds-service-builder</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.directory.api</groupId>
-      <artifactId>api-ldap-codec-standalone</artifactId>
+      <groupId>com.unboundid</groupId>
+      <artifactId>unboundid-ldapsdk</artifactId>
     </dependency>
 
     <dependency>
diff --git a/fit/build-tools/src/main/java/org/apache/directory/server/core/normalization/NormalizationInterceptor.java b/fit/build-tools/src/main/java/org/apache/directory/server/core/normalization/NormalizationInterceptor.java
deleted file mode 100644
index 3811dde6f8..0000000000
--- a/fit/build-tools/src/main/java/org/apache/directory/server/core/normalization/NormalizationInterceptor.java
+++ /dev/null
@@ -1,629 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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 org.apache.directory.server.core.normalization;
-
-// Remove this class as soon as upgrade to ApacheDS 2.0.0.AM27 is available
-
-// CHECKSTYLE:OFF
-
-import org.apache.directory.api.ldap.model.constants.SchemaConstants;
-import org.apache.directory.api.ldap.model.cursor.EmptyCursor;
-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.Value;
-import org.apache.directory.api.ldap.model.exception.LdapException;
-import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeTypeException;
-import org.apache.directory.api.ldap.model.filter.AndNode;
-import org.apache.directory.api.ldap.model.filter.BranchNode;
-import org.apache.directory.api.ldap.model.filter.EqualityNode;
-import org.apache.directory.api.ldap.model.filter.ExprNode;
-import org.apache.directory.api.ldap.model.filter.LeafNode;
-import org.apache.directory.api.ldap.model.filter.NotNode;
-import org.apache.directory.api.ldap.model.filter.ObjectClassNode;
-import org.apache.directory.api.ldap.model.filter.OrNode;
-import org.apache.directory.api.ldap.model.filter.PresenceNode;
-import org.apache.directory.api.ldap.model.filter.UndefinedNode;
-import org.apache.directory.api.ldap.model.name.Ava;
-import org.apache.directory.api.ldap.model.name.Dn;
-import org.apache.directory.api.ldap.model.name.Rdn;
-import org.apache.directory.api.ldap.model.schema.AttributeType;
-import org.apache.directory.api.ldap.model.schema.normalizers.ConcreteNameComponentNormalizer;
-import org.apache.directory.api.ldap.model.schema.normalizers.NameComponentNormalizer;
-import org.apache.directory.server.core.api.DirectoryService;
-import org.apache.directory.server.core.api.InterceptorEnum;
-import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
-import org.apache.directory.server.core.api.filtering.EntryFilteringCursorImpl;
-import org.apache.directory.server.core.api.interceptor.BaseInterceptor;
-import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
-import org.apache.directory.server.core.api.interceptor.context.CompareOperationContext;
-import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext;
-import org.apache.directory.server.core.api.interceptor.context.HasEntryOperationContext;
-import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext;
-import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
-import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
-import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
-import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
-import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
-import org.apache.directory.server.core.api.normalization.FilterNormalizingVisitor;
-import org.apache.directory.server.i18n.I18n;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-
-/**
- * A name normalization service.  This service makes sure all relative and distinguished
- * names are normalized before calls are made against the respective interface methods
- * on DefaultPartitionNexus.
- *
- * The Filters are also normalized.
- *
- * If the Rdn AttributeTypes are not present in the entry for an Add request,
- * they will be added.
- *
- * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
- */
-public class NormalizationInterceptor extends BaseInterceptor
-{
-    /** logger used by this class */
-    private static final Logger LOG = LoggerFactory.getLogger( NormalizationInterceptor.class );
-
-    /** a filter node value normalizer and undefined node remover */
-    private FilterNormalizingVisitor normVisitor;
-
-
-    /**
-     * Creates a new instance of a NormalizationInterceptor.
-     */
-    public NormalizationInterceptor()
-    {
-        super( InterceptorEnum.NORMALIZATION_INTERCEPTOR );
-    }
-
-
-    /**
-     * Initialize the registries, normalizers.
-     */
-    @Override
-    public void init( DirectoryService directoryService ) throws LdapException
-    {
-        LOG.debug( "Initialiazing the NormalizationInterceptor" );
-
-        super.init( directoryService );
-
-        NameComponentNormalizer ncn = new ConcreteNameComponentNormalizer( schemaManager );
-        normVisitor = new FilterNormalizingVisitor( ncn, schemaManager );
-    }
-
-
-    /**
-     * The destroy method does nothing
-     */
-    @Override
-    public void destroy()
-    {
-    }
-
-
-    // ------------------------------------------------------------------------
-    // Normalize all Name based arguments for ContextPartition interface operations
-    // ------------------------------------------------------------------------
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void add( AddOperationContext addContext ) throws LdapException
-    {
-        Dn addDn = addContext.getDn();
-        
-        if ( !addDn.isSchemaAware() )
-        {
-            addContext.setDn( new Dn( schemaManager, addDn ) );
-        }
-        
-        Dn entryDn = addContext.getEntry().getDn();
-        
-        if ( !entryDn.isSchemaAware() )
-        {
-            addContext.getEntry().setDn( new Dn( schemaManager, entryDn ) );
-        }
-        
-        addRdnAttributesToEntry( addContext.getDn(), addContext.getEntry() );
-        
-        next( addContext );
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean compare( CompareOperationContext compareContext ) throws LdapException
-    {
-        Dn dn = compareContext.getDn();
-        
-        if ( !dn.isSchemaAware() )
-        {
-            compareContext.setDn( new Dn( schemaManager, dn ) );
-        }
-
-        // Get the attributeType from the OID
-        try
-        {
-            AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( compareContext.getOid() );
-
-            // Translate the value from binary to String if the AT is HR
-            if ( attributeType.getSyntax().isHumanReadable() && ( !compareContext.getValue().isHumanReadable() ) )
-            {
-                compareContext.setValue( compareContext.getValue() );
-            }
-
-            compareContext.setAttributeType( attributeType );
-        }
-        catch ( LdapException le )
-        {
-            throw new LdapInvalidAttributeTypeException( I18n.err( I18n.ERR_266, compareContext.getOid() ) );
-        }
-
-        return next( compareContext );
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void delete( DeleteOperationContext deleteContext ) throws LdapException
-    {
-        Dn dn = deleteContext.getDn();
-        
-        if ( !dn.isSchemaAware() )
-        {
-            deleteContext.setDn( new Dn( schemaManager, dn ) );
-        }
-
-        next( deleteContext );
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean hasEntry( HasEntryOperationContext hasEntryContext ) throws LdapException
-    {
-        Dn dn = hasEntryContext.getDn();
-        
-        if ( !dn.isSchemaAware() )
-        {
-            hasEntryContext.setDn( new Dn( schemaManager, dn ) );
-        }
-
-        return next( hasEntryContext );
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public Entry lookup( LookupOperationContext lookupContext ) throws LdapException
-    {
-        Dn dn = lookupContext.getDn();
-        
-        if ( !dn.isSchemaAware() )
-        {
-            lookupContext.setDn( new Dn( schemaManager, dn ) );
-        }
-
-        return next( lookupContext );
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void modify( ModifyOperationContext modifyContext ) throws LdapException
-    {
-        Dn dn = modifyContext.getDn();
-        
-        if ( !dn.isSchemaAware() )
-        {
-            modifyContext.setDn( new Dn( schemaManager, dn ) );
-        }
-
-        if ( modifyContext.getModItems() != null )
-        {
-            for ( Modification modification : modifyContext.getModItems() )
-            {
-                AttributeType attributeType = schemaManager.getAttributeType( modification.getAttribute().getId() );
-                modification.apply( attributeType );
-            }
-        }
-
-        next( modifyContext );
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void move( MoveOperationContext moveContext ) throws LdapException
-    {
-        Dn moveDn = moveContext.getDn();
-        
-        if ( !moveDn.isSchemaAware() )
-        {
-            moveContext.setDn( new Dn( schemaManager, moveDn ) );
-        }
-
-        Dn oldSuperiorDn = moveContext.getOldSuperior();
-        
-        if ( !oldSuperiorDn.isSchemaAware() )
-        {
-            moveContext.setOldSuperior( new Dn( schemaManager, oldSuperiorDn ) );
-        }
-
-        Dn newSuperiorDn = moveContext.getNewSuperior();
-        
-        if ( !newSuperiorDn.isSchemaAware() )
-        {
-            moveContext.setNewSuperior( new Dn( schemaManager, newSuperiorDn ) );
-        }
-        
-        Dn newDn = moveContext.getNewDn();
-        
-        if ( !newDn.isSchemaAware() )
-        {
-            moveContext.setNewDn( new Dn( schemaManager, newDn ) );
-        }
-
-        Rdn rdn = moveContext.getRdn();
-        
-        if ( !rdn.isSchemaAware() )
-        {
-            moveContext.setRdn( new Rdn( schemaManager, rdn ) );
-        }
-
-        next( moveContext );
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void moveAndRename( MoveAndRenameOperationContext moveAndRenameContext ) throws LdapException
-    {
-        Rdn newRdn = moveAndRenameContext.getNewRdn();
-        
-        if ( !newRdn.isSchemaAware() )
-        {
-            moveAndRenameContext.setNewRdn( new Rdn( schemaManager, newRdn ) );
-        }
-        
-        Dn dn = moveAndRenameContext.getDn();
-        
-        if ( !dn.isSchemaAware() )
-        {
-            moveAndRenameContext.setDn( new Dn( schemaManager, dn ) );
-        }
-        
-        Dn newDn = moveAndRenameContext.getNewDn();
-        
-        if ( !newDn.isSchemaAware() )
-        {
-            moveAndRenameContext.setNewDn( new Dn( schemaManager, newDn ) );
-        }
-        
-        Dn newSuperiorDn = moveAndRenameContext.getNewSuperiorDn();
-        
-        if ( !newSuperiorDn.isSchemaAware() )
-        {
-            moveAndRenameContext.setNewSuperiorDn( new Dn( schemaManager, newSuperiorDn ) );
-        }
-
-        next( moveAndRenameContext );
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void rename( RenameOperationContext renameContext ) throws LdapException
-    {
-        // Normalize the new Rdn and the Dn if needed
-        Dn dn = renameContext.getDn();
-        
-        if ( !dn.isSchemaAware() )
-        {
-            renameContext.setDn( new Dn( schemaManager, dn ) );
-        }
-        
-        Rdn newRdn = renameContext.getNewRdn();
-        
-        if ( !newRdn.isSchemaAware() )
-        {
-            renameContext.setNewRdn( new Rdn( schemaManager, newRdn ) );
-        }
-        
-        Dn newDn = renameContext.getNewDn();
-        
-        if ( !newDn.isSchemaAware() )
-        {
-            renameContext.setNewDn( new Dn( schemaManager, newDn ) );
-        }
-
-        // Push to the next interceptor
-        next( renameContext );
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public EntryFilteringCursor search( SearchOperationContext searchContext ) throws LdapException
-    {
-        Dn dn = searchContext.getDn();
-        
-        if ( !dn.isSchemaAware() )
-        {
-            searchContext.setDn( new Dn( schemaManager, dn ) );
-        }
-
-        ExprNode filter = searchContext.getFilter();
-
-        if ( filter == null )
-        {
-            LOG.warn( "undefined filter based on undefined attributeType not evaluted at all.  Returning empty enumeration." );
-            return new EntryFilteringCursorImpl(new EmptyCursor<>(), searchContext, schemaManager );
-        }
-
-        // Normalize the filter
-        filter = ( ExprNode ) filter.accept( normVisitor );
-
-        if ( filter == null )
-        {
-            LOG.warn( "undefined filter based on undefined attributeType not evaluted at all.  Returning empty enumeration." );
-            return new EntryFilteringCursorImpl(new EmptyCursor<>(), searchContext, schemaManager );
-        }
-
-        // We now have to remove the (ObjectClass=*) filter if it's present, and to add the scope filter
-        ExprNode modifiedFilter = removeObjectClass( filter );
-
-        searchContext.setFilter( modifiedFilter );
-
-        // TODO Normalize the returned Attributes, storing the UP attributes to format the returned values.
-        return next( searchContext );
-    }
-
-
-    /**
-     * Remove the (ObjectClass=*) node from an AndNode, if we have one.
-     */
-    private ExprNode handleAndNode( ExprNode node )
-    {
-        int nbNodes = 0;
-        AndNode newAndNode = new AndNode();
-
-        for ( ExprNode child : ( ( BranchNode ) node ).getChildren() )
-        {
-            ExprNode modifiedNode = removeObjectClass( child );
-
-            if ( !( modifiedNode instanceof ObjectClassNode ) )
-            {
-                newAndNode.addNode( modifiedNode );
-                nbNodes++;
-            }
-
-            if ( modifiedNode instanceof UndefinedNode )
-            {
-                // We can just return an Undefined node as nothing will get selected
-                return UndefinedNode.UNDEFINED_NODE;
-            }
-        }
-
-        switch ( nbNodes )
-        {
-            case 0:
-                // Unlikely... But (&(ObjectClass=*)) or (|(ObjectClass=*)) are still an option
-                return ObjectClassNode.OBJECT_CLASS_NODE;
-
-            case 1:
-                // We can safely remove the AND/OR node and replace it with its first child
-                return newAndNode.getFirstChild();
-
-            default:
-                return newAndNode;
-        }
-    }
-
-
-    /**
-     * Remove the (ObjectClass=*) node from a NotNode, if we have one.
-     */
-    private ExprNode handleNotNode( ExprNode node )
-    {
-        NotNode newNotNode = new NotNode();
-
-        for ( ExprNode child : ( ( BranchNode ) node ).getChildren() )
-        {
-            ExprNode modifiedNode = removeObjectClass( child );
-
-            if ( modifiedNode instanceof ObjectClassNode )
-            {
-                // We don't want any entry which has an ObjectClass, return an undefined node
-                return UndefinedNode.UNDEFINED_NODE;
-            }
-
-            if ( modifiedNode instanceof UndefinedNode )
-            {
-                // Here, we will select everything
-                return ObjectClassNode.OBJECT_CLASS_NODE;
-            }
-            
-            newNotNode.addNode( modifiedNode );
-
-        }
-
-        return newNotNode;
-    }
-
-
-    /**
-     * Remove the (ObjectClass=*) node from an OrNode, if we have one.
-     */
-    private ExprNode handleOrNode( ExprNode node )
-    {
-        OrNode newOrNode = new OrNode();
-
-        for ( ExprNode child : ( ( BranchNode ) node ).getChildren() )
-        {
-            ExprNode modifiedNode = removeObjectClass( child );
-
-            if ( modifiedNode instanceof ObjectClassNode )
-            {
-                // We can return immediately with an ObjectClass node
-                return ObjectClassNode.OBJECT_CLASS_NODE;
-            }
-            
-            newOrNode.addNode( modifiedNode );
-        }
-
-        return newOrNode;
-    }
-
-
-    /**
-     * Remove the (ObjectClass=*) and ( ObjectClass=top) nodes from the filter, if we have one.
-     */
-    private ExprNode removeObjectClass( ExprNode node )
-    {
-        if ( node instanceof LeafNode )
-        {
-            LeafNode leafNode = ( LeafNode ) node;
-
-            if ( leafNode.getAttributeType() == directoryService.getAtProvider().getObjectClass() )
-            {
-                if ( leafNode instanceof PresenceNode )
-                {
-                    // We can safely remove the node and return an undefined node
-                    return ObjectClassNode.OBJECT_CLASS_NODE;
-                }
-                else if ( leafNode instanceof EqualityNode )
-                {
-                    @SuppressWarnings("unchecked")
-                    Value value = ( ( EqualityNode<String> ) leafNode ).getValue();
-
-                    if ( value.equals( SchemaConstants.TOP_OC ) )
-                    {
-                        // Here too we can safely remove the node and return an undefined node
-                        return ObjectClassNode.OBJECT_CLASS_NODE;
-                    }
-                }
-            }
-        }
-
-        // --------------------------------------------------------------------
-        //                 H A N D L E   B R A N C H   N O D E S
-        // --------------------------------------------------------------------
-
-        if ( node instanceof AndNode )
-        {
-            return handleAndNode( node );
-        }
-        else if ( node instanceof OrNode )
-        {
-            return handleOrNode( node );
-        }
-        else if ( node instanceof NotNode )
-        {
-            return handleNotNode( node );
-        }
-        else
-        {
-            // Failover : we return the initial node as is
-            return node;
-        }
-    }
-
-
-    // ------------------------------------------------------------------------
-    // Normalize all Name based arguments for other interface operations
-    // ------------------------------------------------------------------------
-    /**
-     * Adds missing Rdn's attributes and values to the entry.
-     *
-     * @param dn the Dn
-     * @param entry the entry
-     */
-    private void addRdnAttributesToEntry( Dn dn, Entry entry ) throws LdapException
-    {
-        if ( dn == null || entry == null )
-        {
-            return;
-        }
-
-        Rdn rdn = dn.getRdn();
-
-        // Loop on all the AVAs
-        for ( Ava ava : rdn )
-        {
-            Value value = ava.getValue();
-            String upValue = ava.getValue().getString();
-            String upId = ava.getType();
-
-            // Check that the entry contains this Ava
-            if ( !entry.contains( upId, value ) )
-            {
-                String message = "The Rdn '" + upId + "=" + upValue + "' is not present in the entry";
-                LOG.warn( message );
-
-                // We don't have this attribute : add it.
-                // Two cases :
-                // 1) The attribute does not exist
-                if ( !entry.containsAttribute( upId ) )
-                {
-                    entry.add( upId, upValue );
-                }
-                // 2) The attribute exists
-                else
-                {
-                    AttributeType at = schemaManager.lookupAttributeTypeRegistry( upId );
-
-                    // 2.1 if the attribute is single valued, replace the value
-                    if ( at.isSingleValued() )
-                    {
-                        entry.removeAttributes( upId );
-                        entry.add( upId, upValue );
-                    }
-                    // 2.2 the attribute is multi-valued : add the missing value
-                    else
-                    {
-                        entry.add( upId, upValue );
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/ApacheDSRootDseServlet.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/ApacheDSRootDseServlet.java
deleted file mode 100644
index 0be4ef3a7d..0000000000
--- a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/ApacheDSRootDseServlet.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 org.apache.syncope.fit.buildtools;
-
-import java.io.PrintWriter;
-import java.util.Properties;
-import javax.naming.Context;
-import javax.naming.NamingEnumeration;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.DirContext;
-import javax.naming.directory.InitialDirContext;
-import javax.naming.directory.SearchControls;
-import javax.naming.directory.SearchResult;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.annotation.WebServlet;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.apache.directory.server.core.api.DirectoryService;
-import org.apache.directory.server.core.jndi.CoreContextFactory;
-
-@WebServlet(urlPatterns = "/apacheDS")
-public class ApacheDSRootDseServlet extends HttpServlet {
-
-    private static final long serialVersionUID = 1514567335969002735L;
-
-    @Override
-    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException {
-        try {
-            resp.setContentType("text/plain");
-            PrintWriter out = resp.getWriter();
-
-            out.println("*** ApacheDS RootDSE ***\n");
-
-            DirContext ctx = new InitialDirContext(this.createEnv());
-
-            SearchControls ctls = new SearchControls();
-            ctls.setReturningAttributes(new String[] { "*", "+" });
-            ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
-
-            NamingEnumeration<SearchResult> result = ctx.search("", "(objectClass=*)", ctls);
-            if (result.hasMore()) {
-                SearchResult entry = result.next();
-                Attributes as = entry.getAttributes();
-
-                NamingEnumeration<String> ids = as.getIDs();
-                while (ids.hasMore()) {
-                    String id = ids.next();
-                    Attribute attr = as.get(id);
-                    for (int i = 0; i < attr.size(); ++i) {
-                        out.println(id + ": " + attr.get(i));
-                    }
-                }
-            }
-            ctx.close();
-
-            out.flush();
-        } catch (Exception e) {
-            throw new ServletException(e);
-        }
-    }
-
-    /**
-     * Creates an environment configuration for JNDI access.
-     */
-    private Properties createEnv() {
-        // Fetch directory service from servlet context
-        ServletContext servletContext = this.getServletContext();
-        DirectoryService directoryService = (DirectoryService) servletContext.getAttribute(DirectoryService.JNDI_KEY);
-
-        Properties env = new Properties();
-        env.put(DirectoryService.JNDI_KEY, directoryService);
-        env.put(Context.PROVIDER_URL, "");
-        env.put(Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName());
-
-        env.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
-        env.put(Context.SECURITY_CREDENTIALS, "secret");
-        env.put(Context.SECURITY_AUTHENTICATION, "simple");
-
-        return env;
-    }
-}
diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/ApacheDSStartStopListener.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/ApacheDSStartStopListener.java
deleted file mode 100644
index 6a3322fdbe..0000000000
--- a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/ApacheDSStartStopListener.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 org.apache.syncope.fit.buildtools;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletContextEvent;
-import javax.servlet.ServletContextListener;
-import javax.servlet.annotation.WebListener;
-import org.apache.directory.api.ldap.model.constants.SchemaConstants;
-import org.apache.directory.api.ldap.model.entry.Entry;
-import org.apache.directory.api.ldap.model.name.Dn;
-import org.apache.directory.api.ldap.model.schema.LdapComparator;
-import org.apache.directory.api.ldap.model.schema.SchemaManager;
-import org.apache.directory.api.ldap.model.schema.comparators.NormalizingComparator;
-import org.apache.directory.api.ldap.model.schema.registries.ComparatorRegistry;
-import org.apache.directory.api.ldap.model.schema.registries.SchemaLoader;
-import org.apache.directory.api.ldap.schema.extractor.SchemaLdifExtractor;
-import org.apache.directory.api.ldap.schema.extractor.impl.DefaultSchemaLdifExtractor;
-import org.apache.directory.api.ldap.schema.loader.LdifSchemaLoader;
-import org.apache.directory.api.ldap.schema.manager.impl.DefaultSchemaManager;
-import org.apache.directory.api.util.exception.Exceptions;
-import org.apache.directory.server.constants.ServerDNConstants;
-import org.apache.directory.server.core.DefaultDirectoryService;
-import org.apache.directory.server.core.api.DirectoryService;
-import org.apache.directory.server.core.api.DnFactory;
-import org.apache.directory.server.core.api.InstanceLayout;
-import org.apache.directory.server.core.api.partition.Partition;
-import org.apache.directory.server.core.api.schema.SchemaPartition;
-import org.apache.directory.server.core.factory.JdbmPartitionFactory;
-import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
-import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
-import org.apache.directory.server.core.partition.ldif.LdifPartition;
-import org.apache.directory.server.i18n.I18n;
-import org.apache.directory.server.ldap.LdapServer;
-import org.apache.directory.server.protocol.shared.transport.TcpTransport;
-import org.apache.directory.server.xdbm.Index;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.context.ApplicationContext;
-import org.springframework.core.io.Resource;
-import org.springframework.web.context.support.WebApplicationContextUtils;
-
-/**
- * Start and stop an embedded ApacheDS instance alongside with Servlet Context.
- */
-@WebListener
-public class ApacheDSStartStopListener implements ServletContextListener {
-
-    private static final Logger LOG = LoggerFactory.getLogger(ApacheDSStartStopListener.class);
-
-    private DirectoryService service;
-
-    private LdapServer server;
-
-    /**
-     * Add a new partition to the server.
-     *
-     * @param partitionId The partition Id
-     * @param partitionDn The partition DN
-     * @param dnFactory the DN factory
-     * @return The newly added partition
-     * @throws Exception If the partition can't be added
-     */
-    private void addPartition(final String partitionId, final String partitionDn, final DnFactory dnFactory)
-            throws Exception {
-
-        // Create a new partition with the given partition id
-        JdbmPartition partition = new JdbmPartition(service.getSchemaManager(), dnFactory);
-        partition.setId(partitionId);
-        partition.setPartitionPath(new File(service.getInstanceLayout().getPartitionsDirectory(), partitionId).toURI());
-        partition.setSuffixDn(new Dn(partitionDn));
-        service.addPartition(partition);
-
-        Set<Index<?, String>> indexedAttributes = Stream.of(
-                SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.OU_AT,
-                SchemaConstants.UID_AT, SchemaConstants.CN_AT).
-                map(attr -> new JdbmIndex<String>(attr, false)).collect(Collectors.toSet());
-        partition.setIndexedAttributes(indexedAttributes);
-    }
-
-    /**
-     * Initialize the schema manager and add the schema partition to directory service.
-     *
-     * @throws Exception if the schema LDIF files are not found on the classpath
-     */
-    private void initSchemaPartition() throws Exception {
-        File workingDirectory = service.getInstanceLayout().getPartitionsDirectory();
-
-        // Extract the schema on disk (a brand new one) and load the registries
-        File schemaRepository = new File(workingDirectory, "schema");
-        SchemaLdifExtractor extractor = new DefaultSchemaLdifExtractor(workingDirectory);
-        try {
-            extractor.extractOrCopy();
-        } catch (IOException ioe) {
-            // The schema has already been extracted, bypass
-        }
-
-        SchemaLoader loader = new LdifSchemaLoader(schemaRepository);
-        SchemaManager schemaManager = new DefaultSchemaManager(loader);
-
-        // We have to load the schema now, otherwise we won't be able
-        // to initialize the Partitions, as we won't be able to parse 
-        // and normalize their suffix Dn
-        schemaManager.loadAllEnabled();
-
-        // Tell all the normalizer comparators that they should not normalize anything
-        ComparatorRegistry comparatorRegistry = schemaManager.getComparatorRegistry();
-        for (LdapComparator<?> comparator : comparatorRegistry) {
-            if (comparator instanceof NormalizingComparator) {
-                ((NormalizingComparator) comparator).setOnServer();
-            }
-        }
-
-        service.setSchemaManager(schemaManager);
-
-        // Init the LdifPartition
-        LdifPartition ldifPartition = new LdifPartition(schemaManager, service.getDnFactory());
-        ldifPartition.setPartitionPath(new File(workingDirectory, "schema").toURI());
-        SchemaPartition schemaPartition = new SchemaPartition(schemaManager);
-        schemaPartition.setWrappedPartition(ldifPartition);
-        service.setSchemaPartition(schemaPartition);
-
-        List<Throwable> errors = schemaManager.getErrors();
-        if (!errors.isEmpty()) {
-            throw new IllegalStateException(I18n.err(I18n.ERR_317, Exceptions.printErrors(errors)));
-        }
-    }
-
-    private void initSystemPartition() throws Exception {
-        JdbmPartitionFactory partitionFactory = new JdbmPartitionFactory();
-
-        Partition systemPartition = partitionFactory.createPartition(
-                service.getSchemaManager(),
-                service.getDnFactory(),
-                "system",
-                ServerDNConstants.SYSTEM_DN,
-                500,
-                new File(service.getInstanceLayout().getPartitionsDirectory(), "system"));
-        systemPartition.setSchemaManager(service.getSchemaManager());
-
-        partitionFactory.addIndex(systemPartition, SchemaConstants.OBJECT_CLASS_AT, 100);
-
-        service.setSystemPartition(systemPartition);
-    }
-
-    /**
-     * Initialize the server. It creates the partition, adds the index, and injects the context entries for the created
-     * partitions.
-     *
-     * @param workDir the directory to be used for storing the data
-     * @param loadDefaultContent if default content should be loaded
-     * @throws Exception if there were some problems while initializing
-     */
-    private void initDirectoryService(final ServletContext servletContext, final File workDir,
-            final boolean loadDefaultContent) throws Exception {
-
-        // Initialize the LDAP service
-        service = new DefaultDirectoryService();
-        service.setInstanceLayout(new InstanceLayout(workDir));
-
-        // first load the schema
-        initSchemaPartition();
-
-        // then the system partition
-        initSystemPartition();
-
-        // Disable the ChangeLog system
-        service.getChangeLog().setEnabled(false);
-        service.setDenormalizeOpAttrsEnabled(true);
-
-        // Now we can create as many partitions as we need
-        addPartition("isp", "o=isp", service.getDnFactory());
-
-        // And start the service
-        service.startup();
-
-        if (loadDefaultContent) {
-            Resource contentLdif = Objects.requireNonNull(
-                WebApplicationContextUtils.getWebApplicationContext(servletContext))
-                .getResource("classpath:/content.ldif");
-            LdifInputStreamLoader contentLoader = new LdifInputStreamLoader(service.getAdminSession(),
-                contentLdif.getInputStream());
-            int numEntries = contentLoader.execute();
-            LOG.info("Successfully created {} entries", numEntries);
-        }
-    }
-
-    /**
-     * Startup ApacheDS embedded.
-     *
-     * @param sce ServletContext event
-     */
-    @Override
-    public void contextInitialized(final ServletContextEvent sce) {
-        File workDir = (File) sce.getServletContext().getAttribute("javax.servlet.context.tempdir");
-        workDir = new File(workDir, "server-work");
-
-        final boolean loadDefaultContent = !workDir.exists();
-
-        if (loadDefaultContent && !workDir.mkdirs()) {
-            throw new RuntimeException("Could not create " + workDir.getAbsolutePath());
-        }
-
-        Entry result;
-        try {
-            initDirectoryService(sce.getServletContext(), workDir, loadDefaultContent);
-
-            ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(
-                sce.getServletContext());
-            server = new LdapServer();
-            server.setTransports(new TcpTransport(Integer.parseInt(
-                Objects.requireNonNull(
-                    Objects.requireNonNull(applicationContext).getEnvironment().getProperty("testds.port")))));
-            server.setDirectoryService(service);
-
-            server.start();
-
-            // store directoryService in context to provide it to servlets etc.
-            sce.getServletContext().setAttribute(DirectoryService.JNDI_KEY, service);
-
-            result = service.getAdminSession().lookup(new Dn("o=isp"));
-        } catch (Exception e) {
-            LOG.error("Fatal error in context init", e);
-            throw new RuntimeException(e);
-        }
-
-        if (result == null) {
-            throw new RuntimeException("Base DN not found");
-        } else {
-            LOG.info("ApacheDS startup completed successfully");
-        }
-    }
-
-    /**
-     * Shutdown ApacheDS embedded.
-     *
-     * @param sce ServletContext event
-     */
-    @Override
-    public void contextDestroyed(final ServletContextEvent sce) {
-        try {
-            if (server != null) {
-                server.stop();
-            }
-            if (service != null) {
-                service.shutdown();
-            }
-        } catch (Exception e) {
-            LOG.error("Fatal error in context shutdown", e);
-            throw new RuntimeException(e);
-        }
-    }
-}
diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/LDAPStartStopListener.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/LDAPStartStopListener.java
new file mode 100644
index 0000000000..1987db58e4
--- /dev/null
+++ b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/LDAPStartStopListener.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.syncope.fit.buildtools;
+
+import com.unboundid.ldap.listener.Base64PasswordEncoderOutputFormatter;
+import com.unboundid.ldap.listener.InMemoryDirectoryServer;
+import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
+import com.unboundid.ldap.listener.InMemoryListenerConfig;
+import com.unboundid.ldap.listener.UnsaltedMessageDigestInMemoryPasswordEncoder;
+import com.unboundid.ldap.sdk.schema.Schema;
+import java.net.InetAddress;
+import java.security.MessageDigest;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.annotation.WebListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+/**
+ * Start and stop an in-memory LDAP server instance alongside with Servlet Context.
+ */
+@WebListener
+public class LDAPStartStopListener implements ServletContextListener {
+
+    private static final Logger LOG = LoggerFactory.getLogger(LDAPStartStopListener.class);
+
+    private InMemoryDirectoryServer ldapServer;
+
+    @Override
+    public void contextInitialized(final ServletContextEvent sce) {
+        try {
+            ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
+
+            InMemoryDirectoryServerConfig config =
+                    new InMemoryDirectoryServerConfig(ctx.getEnvironment().getProperty("testds.rootDn"));
+
+            config.addAdditionalBindCredentials(
+                    ctx.getEnvironment().getProperty("testds.bindDn"),
+                    ctx.getEnvironment().getProperty("testds.password"));
+
+            InMemoryListenerConfig listenerConfig = InMemoryListenerConfig.createLDAPConfig(
+                    "test-listener",
+                    InetAddress.getLoopbackAddress(),
+                    Integer.parseInt(ctx.getEnvironment().getProperty("testds.port")),
+                    null);
+            config.setListenerConfigs(listenerConfig);
+
+            config.setSchema(Schema.getDefaultStandardSchema());
+
+            config.setPasswordEncoders(
+                    new UnsaltedMessageDigestInMemoryPasswordEncoder(
+                            "{SHA}",
+                            Base64PasswordEncoderOutputFormatter.getInstance(),
+                            MessageDigest.getInstance("SHA-1")));
+
+            ldapServer = new InMemoryDirectoryServer(config);
+            ldapServer.importFromLDIF(false, ctx.getResource("classpath:/content.ldif").getFile());
+            ldapServer.startListening();
+        } catch (Exception e) {
+            LOG.error("Fatal error in context init", e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void contextDestroyed(final ServletContextEvent sce) {
+        if (ldapServer != null) {
+            ldapServer.shutDown(true);
+        }
+    }
+}
diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/LdifInputStreamLoader.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/LdifInputStreamLoader.java
deleted file mode 100644
index fe1116e572..0000000000
--- a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/LdifInputStreamLoader.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 org.apache.syncope.fit.buildtools;
-
-import java.io.InputStream;
-import java.util.List;
-import org.apache.directory.api.ldap.model.entry.DefaultEntry;
-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.exception.LdapException;
-import org.apache.directory.api.ldap.model.ldif.LdifEntry;
-import org.apache.directory.api.ldap.model.ldif.LdifReader;
-import org.apache.directory.api.ldap.model.name.Dn;
-import org.apache.directory.server.core.api.CoreSession;
-import org.apache.directory.server.i18n.I18n;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class LdifInputStreamLoader {
-
-    /**
-     * The log for this class.
-     */
-    private static final Logger LOG = LoggerFactory.getLogger(LdifInputStreamLoader.class);
-
-    /**
-     * A handle on the top core session.
-     */
-    protected CoreSession coreSession;
-
-    /**
-     * The LDIF input stream file containing LDIFs to load.
-     */
-    protected InputStream ldif;
-
-    /**
-     * the total count of entries loaded
-     */
-    private int count;
-
-    /**
-     * Creates a new instance of LdifFileLoader.
-     *
-     * @param coreSession the context to load the entries into.
-     * @param ldif the file of LDIF entries to load.
-     */
-    public LdifInputStreamLoader(final CoreSession coreSession, final InputStream ldif) {
-        this.coreSession = coreSession;
-        this.ldif = ldif;
-    }
-
-    /**
-     * Opens the LDIF file and loads the entries into the context.
-     *
-     * @return The count of entries created.
-     */
-    public int execute() {
-        try {
-            try {
-                for (LdifEntry ldifEntry : new LdifReader(ldif)) {
-                    Dn dn = ldifEntry.getDn();
-
-                    if (ldifEntry.isEntry()) {
-                        Entry entry = ldifEntry.getEntry();
-
-                        try {
-                            coreSession.lookup(dn);
-                            LOG.debug("Found {}, will not create.", dn);
-                        } catch (Exception e) {
-                            try {
-                                coreSession.add(
-                                        new DefaultEntry(coreSession.getDirectoryService().getSchemaManager(), entry));
-                                count++;
-                                LOG.debug("Created {}.", dn);
-                            } catch (LdapException e1) {
-                                LOG.error("Could not create entry " + entry, e1);
-                            }
-                        }
-                    } else {
-                        //modify
-                        List<Modification> items = ldifEntry.getModifications();
-
-                        try {
-                            coreSession.modify(dn, items);
-                            LOG.debug("Modified: " + dn + " with modificationItems: " + items);
-                        } catch (LdapException e) {
-                            LOG.debug("Could not modify: " + dn + " with modificationItems: " + items, e);
-                        }
-                    }
-                }
-            } finally {
-                ldif.close();
-            }
-        } catch (Exception ioe) {
-            LOG.error(I18n.err(I18n.ERR_174), ioe);
-        }
-
-        return count;
-    }
-}
diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/SyncopeBuildToolsApplication.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/SyncopeBuildToolsApplication.java
index ec1ea7249b..772dc05183 100644
--- a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/SyncopeBuildToolsApplication.java
+++ b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/SyncopeBuildToolsApplication.java
@@ -82,7 +82,7 @@ public class SyncopeBuildToolsApplication extends SpringBootServletInitializer {
 
     @Bean
     public Endpoint soapProvisioning(final Provisioning provisioning,
-                                     final Bus bus) {
+            final Bus bus) {
         EndpointImpl soapProvisioning = new EndpointImpl(provisioning);
         soapProvisioning.setBus(bus);
         soapProvisioning.publish("/soap");
@@ -100,8 +100,12 @@ public class SyncopeBuildToolsApplication extends SpringBootServletInitializer {
     }
 
     @Bean
-    public Server restProvisioning(final GreenMailService greenMailService, final UserService userService,
-                                   final ApplicationContext ctx, final Bus bus) {
+    public Server restProvisioning(
+            final GreenMailService greenMailService,
+            final UserService userService,
+            final ApplicationContext ctx,
+            final Bus bus) {
+
         SpringJAXRSServerFactoryBean restProvisioning = new SpringJAXRSServerFactoryBean();
         restProvisioning.setApplicationContext(ctx);
         restProvisioning.setBus(bus);
@@ -115,12 +119,10 @@ public class SyncopeBuildToolsApplication extends SpringBootServletInitializer {
     @Override
     public void onStartup(final ServletContext sc) throws ServletException {
         sc.addListener(new ConnectorServerStartStopListener());
-        sc.addListener(new ApacheDSStartStopListener());
+        sc.addListener(new LDAPStartStopListener());
         sc.addListener(new H2StartStopListener());
         sc.addListener(new GreenMailStartStopListener());
 
-        ServletRegistration.Dynamic apacheDS = sc.addServlet("ApacheDSRootDseServlet", ApacheDSRootDseServlet.class);
-        apacheDS.addMapping("/apacheDS");
         ServletRegistration.Dynamic sts = sc.addServlet("ServiceTimeoutServlet", ServiceTimeoutServlet.class);
         sts.addMapping("/services/*");
 
diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/DateParamConverterProvider.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/DateParamConverterProvider.java
index 123bde8161..80b1b9947a 100644
--- a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/DateParamConverterProvider.java
+++ b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/DateParamConverterProvider.java
@@ -23,7 +23,7 @@ import java.lang.reflect.Type;
 import java.util.Date;
 import javax.ws.rs.ext.ParamConverter;
 import javax.ws.rs.ext.ParamConverterProvider;
-import org.apache.commons.lang3.StringUtils;
+import org.springframework.util.StringUtils;
 
 public class DateParamConverterProvider implements ParamConverterProvider {
 
@@ -31,11 +31,11 @@ public class DateParamConverterProvider implements ParamConverterProvider {
 
         @Override
         public Date fromString(final String value) {
-            if (StringUtils.isBlank(value)) {
+            if (!StringUtils.hasText(value)) {
                 return null;
             }
             try {
-                return new Date(Long.valueOf(value));
+                return new Date(Long.parseLong(value));
             } catch (final NumberFormatException e) {
                 throw new IllegalArgumentException("Unparsable date: " + value, e);
             }
diff --git a/fit/build-tools/src/main/resources/application.properties b/fit/build-tools/src/main/resources/application.properties
index 6ee9cb47c8..4dd3aff457 100644
--- a/fit/build-tools/src/main/resources/application.properties
+++ b/fit/build-tools/src/main/resources/application.properties
@@ -32,6 +32,9 @@ testdb.username=${testdb.username}
 testdb.password=${testdb.password}
 testdb.webport=${testdb.webport}
 
+testds.rootDn=${testds.rootDn}
+testds.bindDn=${testds.bindDn}
+testds.password=${testds.password}
 testds.port=${testds.port}
 
 testconnectorserver.port=${testconnectorserver.port}
diff --git a/fit/build-tools/src/main/resources/content.ldif b/fit/build-tools/src/main/resources/content.ldif
index d37c13e2dd..6b05f91826 100644
--- a/fit/build-tools/src/main/resources/content.ldif
+++ b/fit/build-tools/src/main/resources/content.ldif
@@ -14,25 +14,25 @@ objectClass: organization
 objectClass: top
 o: isp
 
-DN: ou=People,o=isp
+DN: ou=people,o=isp
 objectClass: organizationalUnit
 objectClass: top
-ou: People
+ou: people
 
-DN: ou=Groups,o=isp
+DN: ou=groups,o=isp
 objectClass: organizationalUnit
 objectClass: top
-ou: Groups
+ou: groups
 
-DN: cn=testLDAPGroup,ou=Groups,o=isp
+DN: cn=testLDAPGroup,ou=groups,o=isp
 objectClass: groupOfUniqueNames
 objectClass: top
 cn: testLDAPGroup
 uniqueMember: uid=admin,ou=system
-uniqueMember: uid=pullFromLDAP,ou=People,o=isp
-owner: uid=pullFromLDAP,ou=People,o=isp
+uniqueMember: uid=pullFromLDAP,ou=people,o=isp
+owner: uid=pullFromLDAP,ou=people,o=isp
 
-DN: uid=pullFromLDAP,ou=People,o=isp
+DN: uid=pullFromLDAP,ou=people,o=isp
 objectClass: organizationalPerson
 objectClass: person
 objectClass: inetOrgPerson
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
index d28f681cfe..933bd95fe9 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
@@ -528,7 +528,7 @@ public class SearchITCase extends AbstractITCase {
                 matches = null;
 
                 boolean succeeded = false;
-                // needed because ApacheDS seems to randomly fail when searching with cookie
+                // needed because embedded LDAP server seems to randomly fail when searching with cookie
                 for (int i = 0; i < 5 && !succeeded; i++) {
                     try {
                         matches = RESOURCE_SERVICE.searchConnObjects(
diff --git a/pom.xml b/pom.xml
index 10c81a106e..86cb32ac82 100644
--- a/pom.xml
+++ b/pom.xml
@@ -434,9 +434,6 @@ under the License.
 
     <elasticsearch.version>8.5.2</elasticsearch.version>
 
-    <apacheds.version>2.0.0.AM26</apacheds.version>
-    <apachedirapi.version>2.0.0</apachedirapi.version>
-
     <log4j2.version>2.19.0</log4j2.version>
     <disruptor.version>3.4.4</disruptor.version>
     
@@ -480,13 +477,16 @@ under the License.
     <curator.version>5.4.0</curator.version>
     <zookeeper.version>3.8.0</zookeeper.version>
 
+    <testds.rootDn>o=isp</testds.rootDn>
+    <testds.bindDn>uid=admin,ou=system</testds.bindDn>
+    <testds.password>secret</testds.password>
     <testds.port>1389</testds.port>
-    <testdb.webport>9082</testdb.webport>
 
     <testdb.driver>org.h2.Driver</testdb.driver>
     <testdb.url>jdbc:h2:tcp://localhost:9092/mem:testdb;DB_CLOSE_DELAY=-1</testdb.url>
     <testdb.username>sa</testdb.username>
     <testdb.password>sa</testdb.password>
+    <testdb.webport>9082</testdb.webport>
 
     <testconnectorserver.port>4554</testconnectorserver.port>
     <testconnectorserver.key>testconnectorserver</testconnectorserver.key>
@@ -1144,39 +1144,6 @@ under the License.
         <version>${h2.version}</version>
       </dependency>
 
-      <dependency>
-        <groupId>org.apache.directory.server</groupId>
-        <artifactId>apacheds-core-api</artifactId>
-        <version>${apacheds.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.apache.directory.server</groupId>
-        <artifactId>apacheds-core-annotations</artifactId>
-        <version>${apacheds.version}</version>
-        <exclusions>
-          <exclusion>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-          </exclusion>
-        </exclusions>
-      </dependency>
-      <dependency>
-        <groupId>org.apache.directory.server</groupId>
-        <artifactId>apacheds-service-builder</artifactId>
-        <version>${apacheds.version}</version>
-        <exclusions>
-          <exclusion>
-            <groupId>org.apache.directory.server</groupId>
-            <artifactId>apacheds-http-integration</artifactId>
-          </exclusion>
-        </exclusions>
-      </dependency>
-      <dependency>
-        <groupId>org.apache.directory.api</groupId>
-        <artifactId>api-ldap-codec-standalone</artifactId>
-        <version>${apachedirapi.version}</version>
-      </dependency>
-
       <dependency>
         <groupId>com.icegreen</groupId>
         <artifactId>greenmail</artifactId>
diff --git a/src/main/asciidoc/getting-started/obtain.adoc b/src/main/asciidoc/getting-started/obtain.adoc
index 457ffd117a..fcbd1ecc91 100644
--- a/src/main/asciidoc/getting-started/obtain.adoc
+++ b/src/main/asciidoc/getting-started/obtain.adoc
@@ -678,7 +678,7 @@ Credentials: `admin` / `password`
  Click 'Connect' button
 
 | External resource: LDAP
-| An http://directory.apache.org/apacheds/[Apache DS^] instance is available. +
+| An embedded instance is available. +
 You can configure any LDAP client (such as http://jxplorer.org/[JXplorer^], for example) with the following information: +
  +
  host: `localhost` +
diff --git a/standalone/pom.xml b/standalone/pom.xml
index 5779ca2281..b1d3bd8803 100644
--- a/standalone/pom.xml
+++ b/standalone/pom.xml
@@ -171,7 +171,7 @@ under the License.
                 <mkdir dir="${work.dir}/apache-tomcat-${tomcat.version}/${test.csvdir.path}"/>
                 <copy file="../fit/core-reference/src/test/resources/test.csv" todir="${work.dir}/apache-tomcat-${tomcat.version}/${test.csvdir.path}"/>
 
-                <!-- Syncope build tools (provide H2, Apache DS and REST / SOAP resources + ConnId connector server) -->
+                <!-- Syncope build tools (provide H2, LDAP and REST / SOAP resources + ConnId connector server) -->
                 <copy todir="${work.dir}/apache-tomcat-${tomcat.version}/webapps/syncope-fit-build-tools">
                   <fileset dir="../fit/build-tools/target/syncope-fit-build-tools-${project.version}" includes="**/*"/>
                 </copy>