keycloak-memoizeit

Changes

pom.xml 2(+1 -1)

Details

diff --git a/authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationProvider.java b/authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationProvider.java
index 542c994..f4ed95e 100644
--- a/authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationProvider.java
+++ b/authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationProvider.java
@@ -4,6 +4,7 @@ import java.util.List;
 import java.util.Map;
 
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
 import org.keycloak.provider.Provider;
 
 /**
@@ -36,11 +37,11 @@ public interface AuthenticationProvider extends Provider {
      *
      * @param realm
      * @param configuration
-     * @param username
+     * @param user Keycloak user, which will be registered on authentication provider side
      * @return ID of newly created user (For example ID from LDAP)
      * @throws AuthenticationProviderException if user creation couldn't happen
      */
-    String registerUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException;
+    String registerUser(RealmModel realm, Map<String, String> configuration, UserModel user) throws AuthenticationProviderException;
 
     /**
      * Standard Authentication flow
diff --git a/authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationProviderManager.java b/authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationProviderManager.java
index b0d485b..d36cb24 100755
--- a/authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationProviderManager.java
+++ b/authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationProviderManager.java
@@ -118,7 +118,7 @@ public class AuthenticationProviderManager {
                                         + authUser.getProviderName() + " but is not linked with model user");
                             }
                         } else {
-                            String userIdInProvider = delegate.registerUser(realm, providerModel.getConfig(), user.getLoginName());
+                            String userIdInProvider = delegate.registerUser(realm, providerModel.getConfig(), user);
                             authLink = new AuthenticationLinkModel(providerModel.getProviderName(), userIdInProvider);
                             user.setAuthenticationLink(authLink);
                             logger.infof("User '%s' registered in provider '%s' and linked", user.getLoginName(), providerModel.getProviderName());
@@ -143,7 +143,7 @@ public class AuthenticationProviderManager {
 
         String username = user.getLoginName();
 
-        // Update just those, which support password update
+        // Update just if password update is supported
         if (providerModel.isPasswordUpdateSupported()) {
             try {
                 AuthenticationProvider delegate = getProvider(providerName);
diff --git a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/AbstractModelAuthenticationProvider.java b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/AbstractModelAuthenticationProvider.java
index 730f3f4..ea20abc 100755
--- a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/AbstractModelAuthenticationProvider.java
+++ b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/AbstractModelAuthenticationProvider.java
@@ -30,11 +30,14 @@ public abstract class AbstractModelAuthenticationProvider implements Authenticat
     }
 
     @Override
-    public String registerUser(RealmModel currentRealm, Map<String, String> config, String username) throws AuthenticationProviderException {
+    public String registerUser(RealmModel currentRealm, Map<String, String> config, UserModel user) throws AuthenticationProviderException {
         RealmModel realm = getRealm(currentRealm, config);
-        UserModel user = currentRealm.addUser(username);
-        user.setEnabled(true);
-        return user.getId();
+        UserModel newUser = realm.addUser(user.getLoginName());
+        newUser.setFirstName(user.getFirstName());
+        newUser.setLastName(user.getLastName());
+        newUser.setEmail(user.getEmail());
+        newUser.setEnabled(true);
+        return newUser.getId();
     }
 
     @Override
diff --git a/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java b/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java
index b2310ed..772f6da 100755
--- a/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java
+++ b/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java
@@ -7,6 +7,7 @@ import org.keycloak.authentication.AuthUser;
 import org.keycloak.authentication.AuthenticationProvider;
 import org.keycloak.authentication.AuthenticationProviderException;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
 import org.keycloak.picketlink.IdentityManagerProvider;
 import org.picketlink.idm.IdentityManagementException;
 import org.picketlink.idm.IdentityManager;
@@ -67,16 +68,15 @@ public class PicketlinkAuthenticationProvider implements AuthenticationProvider 
     }
 
     @Override
-    public String registerUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException {
+    public String registerUser(RealmModel realm, Map<String, String> configuration, UserModel user) throws AuthenticationProviderException {
         IdentityManager identityManager = getIdentityManager(realm);
 
         try {
-            User picketlinkUser = new User(username);
+            User picketlinkUser = new User(user.getLoginName());
+            picketlinkUser.setFirstName(user.getFirstName());
+            picketlinkUser.setLastName(user.getLastName());
+            picketlinkUser.setEmail(user.getEmail());
             identityManager.add(picketlinkUser);
-
-            // Hack needed due to ActiveDirectory bug in Picketlink TODO: Remove once https://issues.jboss.org/browse/PLINK-485 fixed and updated in keycloak master
-            picketlinkUser = BasicModel.getUser(identityManager, picketlinkUser.getLoginName());
-
             return picketlinkUser.getId();
         } catch (IdentityManagementException ie) {
             throw convertIDMException(ie);
diff --git a/examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProvider.java b/examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProvider.java
index da10edd..9b83ec0 100644
--- a/examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProvider.java
+++ b/examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProvider.java
@@ -11,6 +11,7 @@ import org.keycloak.authentication.AuthUser;
 import org.keycloak.authentication.AuthenticationProvider;
 import org.keycloak.authentication.AuthenticationProviderException;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -45,9 +46,9 @@ public class PropertiesAuthenticationProvider implements AuthenticationProvider 
     }
 
     @Override
-    public String registerUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException {
+    public String registerUser(RealmModel realm, Map<String, String> configuration, UserModel user) throws AuthenticationProviderException {
         // Registration ignored
-        return username;
+        return user.getLoginName();
     }
 
     @Override
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
index 8053a5c..bf1f0fc 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
@@ -168,7 +168,7 @@ module.config([ '$routeProvider', function($routeProvider) {
                     return RealmLoader();
                 }
             },
-            controller : 'RealmLdapSettingsCtrl'
+            controller : 'RealmLDAPSettingsCtrl'
         })
         .when('/realms/:realm/audit', {
             templateUrl : 'partials/realm-audit.html',
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
index a18765f..392e726 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
@@ -896,8 +896,8 @@ module.controller('RealmSMTPSettingsCtrl', function($scope, Current, Realm, real
     }
 });
 
-module.controller('RealmLdapSettingsCtrl', function($scope, $location, Notifications, Realm, realm) {
-    console.log('RealmLdapSettingsCtrl');
+module.controller('RealmLDAPSettingsCtrl', function($scope, $location, Notifications, Realm, realm, RealmLDAPConnectionTester) {
+    console.log('RealmLDAPSettingsCtrl');
 
     $scope.ldapVendors = [
         { "id": "ad", "name": "Active Directory" },
@@ -905,15 +905,28 @@ module.controller('RealmLdapSettingsCtrl', function($scope, $location, Notificat
         { "id": "other", "name": "Other" }
     ];
 
+    $scope.usernameLDAPAttributes = [
+        "uid", "cn", "sAMAccountName"
+    ];
+
     $scope.realm = realm;
 
     var oldCopy = angular.copy($scope.realm);
     $scope.changed = false;
 
+    $scope.lastVendor = realm.ldapServer.vendor;
+
     $scope.$watch('realm', function() {
         if (!angular.equals($scope.realm, oldCopy)) {
             $scope.changed = true;
         }
+
+        if (!angular.equals($scope.realm.ldapServer.vendor, $scope.lastVendor)) {
+            console.log("LDAP vendor changed");
+            $scope.lastVendor = $scope.realm.ldapServer.vendor;
+
+            $scope.realm.ldapServer.usernameLDAPAttribute = ($scope.lastVendor === "ad") ? "cn" : "uid";
+        }
     }, true);
 
     $scope.save = function() {
@@ -928,7 +941,36 @@ module.controller('RealmLdapSettingsCtrl', function($scope, $location, Notificat
     $scope.reset = function() {
         $scope.realm = angular.copy(oldCopy);
         $scope.changed = false;
+        $scope.lastVendor = $scope.realm.ldapServer.vendor;
+    };
+
+    var initConnectionTest = function(testAction, ldapConfig) {
+        return {
+            action: testAction,
+            realm: $scope.realm.realm,
+            connectionUrl: ldapConfig.connectionUrl,
+            bindDn: ldapConfig.bindDn,
+            bindCredential: ldapConfig.bindCredential
+        };
     };
+
+    $scope.testConnection = function() {
+        console.log('RealmLDAPSettingsCtrl: testConnection');
+        RealmLDAPConnectionTester.get(initConnectionTest("testConnection", $scope.realm.ldapServer), function() {
+            Notifications.success("LDAP connection successful.");
+        }, function() {
+            Notifications.error("Error when trying to connect to LDAP. See server.log for details.");
+        });
+    }
+
+    $scope.testAuthentication = function() {
+        console.log('RealmLDAPSettingsCtrl: testAuthentication');
+        RealmLDAPConnectionTester.get(initConnectionTest("testAuthentication", $scope.realm.ldapServer), function() {
+            Notifications.success("LDAP authentication successful.");
+        }, function() {
+            Notifications.error("LDAP authentication failed. See server.log for details");
+        });
+    }
 });
 
 module.controller('RealmAuthSettingsCtrl', function($scope, realm) {
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
index b88c6b9..415734a 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
@@ -180,6 +180,10 @@ module.factory('RealmAuditEvents', function($resource) {
     });
 });
 
+module.factory('RealmLDAPConnectionTester', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/testLDAPConnection');
+});
+
 module.factory('ServerInfo', function($resource) {
     return $resource(authUrl + '/admin/serverinfo');
 });
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-ldap.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-ldap.html
index 6eb9fdb..6cbf269 100644
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-ldap.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-ldap.html
@@ -25,10 +25,24 @@
                     </div>
                 </div>
                 <div class="form-group clearfix">
+                    <label class="col-sm-2 control-label" for="usernameLDAPAttribute">Username LDAP attribute <span class="required">*</span></label>
+                    <div class="col-sm-4">
+                        <div class="select-kc">
+                            <select id="usernameLDAPAttribute"
+                                    ng-model="realm.ldapServer.usernameLDAPAttribute"
+                                    ng-options="usernameLDAPAttribute for usernameLDAPAttribute in usernameLDAPAttributes">
+                            </select>
+                        </div>
+                    </div>
+                </div>
+                <div class="form-group clearfix">
                     <label class="col-sm-2 control-label" for="ldapConnectionUrl">Connection URL <span class="required">*</span></label>
                     <div class="col-sm-4">
                         <input class="form-control" id="ldapConnectionUrl" type="text" ng-model="realm.ldapServer.connectionUrl" placeholder="LDAP connection URL" required>
                     </div>
+                    <div class="col-sm-4" data-ng-show="access.manageRealm">
+                        <a class="btn btn-primary" data-ng-click="testConnection()">Test connection</a>
+                    </div>
                 </div>
                 <div class="form-group clearfix">
                     <label class="col-sm-2 control-label" for="ldapBaseDn">Base DN <span class="required">*</span></label>
@@ -53,6 +67,9 @@
                     <div class="col-sm-4">
                         <input class="form-control" id="ldapBindCredential" type="text" ng-model="realm.ldapServer.bindCredential" placeholder="LDAP Bind Credentials" required>
                     </div>
+                    <div class="col-sm-4" data-ng-show="access.manageRealm">
+                        <a class="btn btn-primary" data-ng-click="testAuthentication()">Test authentication</a>
+                    </div>
                 </div>
             </fieldset>
 
diff --git a/model/tests/src/main/java/org/keycloak/model/test/LDAPEmbeddedServer.java b/model/tests/src/main/java/org/keycloak/model/test/LDAPEmbeddedServer.java
index 70c149f..354bc86 100644
--- a/model/tests/src/main/java/org/keycloak/model/test/LDAPEmbeddedServer.java
+++ b/model/tests/src/main/java/org/keycloak/model/test/LDAPEmbeddedServer.java
@@ -17,8 +17,8 @@ import javax.naming.NamingException;
 import javax.naming.directory.DirContext;
 import javax.naming.directory.InitialDirContext;
 
+import org.keycloak.models.LDAPConstants;
 import org.keycloak.models.RealmModel;
-import org.keycloak.picketlink.idm.LdapConstants;
 import org.picketbox.test.ldap.AbstractLDAPTest;
 
 /**
@@ -130,12 +130,12 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
 
     public void setupLdapInRealm(RealmModel realm) {
         Map<String,String> ldapConfig = new HashMap<String,String>();
-        ldapConfig.put(LdapConstants.CONNECTION_URL, getConnectionUrl());
-        ldapConfig.put(LdapConstants.BASE_DN, getBaseDn());
-        ldapConfig.put(LdapConstants.BIND_DN, getBindDn());
-        ldapConfig.put(LdapConstants.BIND_CREDENTIAL, getBindCredential());
-        ldapConfig.put(LdapConstants.USER_DN_SUFFIX, getUserDnSuffix());
-        ldapConfig.put(LdapConstants.VENDOR, getVendor());
+        ldapConfig.put(LDAPConstants.CONNECTION_URL, getConnectionUrl());
+        ldapConfig.put(LDAPConstants.BASE_DN, getBaseDn());
+        ldapConfig.put(LDAPConstants.BIND_DN, getBindDn());
+        ldapConfig.put(LDAPConstants.BIND_CREDENTIAL, getBindCredential());
+        ldapConfig.put(LDAPConstants.USER_DN_SUFFIX, getUserDnSuffix());
+        ldapConfig.put(LDAPConstants.VENDOR, getVendor());
         realm.setLdapServerConfig(ldapConfig);
     }
 
diff --git a/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersLDAPTest.java b/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersLDAPTest.java
index 155b235..a59a12a 100755
--- a/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersLDAPTest.java
+++ b/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersLDAPTest.java
@@ -5,8 +5,6 @@ import java.util.Collections;
 
 import javax.ws.rs.core.MultivaluedMap;
 
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
-import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.Before;
@@ -78,7 +76,7 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
         MultivaluedMap<String, String> formData = AuthProvidersExternalModelTest.createFormData("johnkeycloak", "password");
 
         // Set password of user in LDAP
-        LdapTestUtils.setLdapPassword(providerSession, realm, "johnkeycloak", "password");
+        LDAPTestUtils.setLdapPassword(providerSession, realm, "johnkeycloak", "password");
 
         // Verify that user doesn't exists in realm2 and can't authenticate here
         Assert.assertEquals(AuthenticationManager.AuthenticationStatus.INVALID_USER, am.authenticateForm(null, realm, formData));
@@ -142,7 +140,7 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
         // Add ldap
         setupAuthenticationProviders();
 
-        LdapTestUtils.setLdapPassword(providerSession, realm, "johnkeycloak", "password");
+        LDAPTestUtils.setLdapPassword(providerSession, realm, "johnkeycloak", "password");
 
         // First authenticate successfully to sync john into realm
         MultivaluedMap<String, String> formData = AuthProvidersExternalModelTest.createFormData("johnkeycloak", "password");
diff --git a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/KeycloakLDAPIdentityStore.java b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/KeycloakLDAPIdentityStore.java
new file mode 100644
index 0000000..124bb82
--- /dev/null
+++ b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/KeycloakLDAPIdentityStore.java
@@ -0,0 +1,92 @@
+package org.keycloak.picketlink.idm;
+
+import java.lang.reflect.Method;
+
+import javax.naming.directory.BasicAttributes;
+
+import org.keycloak.models.utils.reflection.Reflections;
+import org.picketlink.idm.config.LDAPMappingConfiguration;
+import org.picketlink.idm.ldap.internal.LDAPIdentityStore;
+import org.picketlink.idm.ldap.internal.LDAPOperationManager;
+import org.picketlink.idm.model.AttributedType;
+import org.picketlink.idm.model.basic.User;
+import org.picketlink.idm.query.IdentityQuery;
+import org.picketlink.idm.spi.IdentityContext;
+
+import static org.picketlink.common.constants.LDAPConstants.CN;
+import static org.picketlink.common.constants.LDAPConstants.COMMA;
+import static org.picketlink.common.constants.LDAPConstants.EQUAL;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class KeycloakLDAPIdentityStore extends LDAPIdentityStore {
+
+    public static Method GET_OPERATION_MANAGER_METHOD;
+    public static Method CREATE_SEARCH_FILTER_METHOD;
+    public static Method EXTRACT_ATTRIBUTES_METHOD;
+    public static Method GET_ENTRY_IDENTIFIER_METHOD;
+
+    public static final String SAM_ACCOUNT_NAME = "sAMAccountName";
+
+    static {
+        GET_OPERATION_MANAGER_METHOD = getMethodOnLDAPStore("getOperationManager");
+        CREATE_SEARCH_FILTER_METHOD = getMethodOnLDAPStore("createIdentityTypeSearchFilter", IdentityQuery.class, LDAPMappingConfiguration.class);
+        EXTRACT_ATTRIBUTES_METHOD = getMethodOnLDAPStore("extractAttributes", AttributedType.class, boolean.class);
+        GET_ENTRY_IDENTIFIER_METHOD = getMethodOnLDAPStore("getEntryIdentifier", AttributedType.class);
+    }
+
+    @Override
+    public void addAttributedType(IdentityContext context, AttributedType attributedType) {
+        LDAPMappingConfiguration userMappingConfig = getConfig().getMappingConfig(attributedType.getClass());
+        String ldapUsernameAttrName = userMappingConfig.getMappedProperties().get(userMappingConfig.getIdProperty().getName());
+
+        if (getConfig().isActiveDirectory() && SAM_ACCOUNT_NAME.equals(ldapUsernameAttrName)) {
+            // TODO: pain, but everything in picketlink is private... Improve if possible...
+            LDAPOperationManager operationManager = Reflections.invokeMethod(false, GET_OPERATION_MANAGER_METHOD, LDAPOperationManager.class, this);
+
+            String cn = getCommonName(attributedType);
+            String bindingDN = CN + EQUAL + cn + COMMA + userMappingConfig.getBaseDN();
+
+            BasicAttributes ldapAttributes = Reflections.invokeMethod(false, EXTRACT_ATTRIBUTES_METHOD, BasicAttributes.class, this, attributedType, true);
+            ldapAttributes.put(CN, cn);
+
+            operationManager.createSubContext(bindingDN, ldapAttributes);
+
+            String ldapId = Reflections.invokeMethod(false, GET_ENTRY_IDENTIFIER_METHOD, String.class, this, attributedType);
+            attributedType.setId(ldapId);
+        } else {
+            super.addAttributedType(context, attributedType);
+        }
+    }
+
+    // Hack as methods are protected on LDAPIdentityStore :/
+    public static Method getMethodOnLDAPStore(String methodName, Class... classes) {
+        try {
+            Method m = LDAPIdentityStore.class.getDeclaredMethod(methodName, classes);
+            m.setAccessible(true);
+            return m;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    protected String getCommonName(AttributedType aType) {
+        User user = (User)aType;
+        String fullName;
+        if (user.getFirstName() != null && user.getLastName() != null) {
+            fullName = user.getFirstName() + " " + user.getLastName();
+        } else if (user.getFirstName() != null && user.getFirstName().trim().length() > 0) {
+            fullName = user.getFirstName();
+        } else {
+            fullName = user.getLastName();
+        }
+
+        // Fallback to loginName
+        if (fullName == null || fullName.trim().length() == 0) {
+            fullName = user.getLoginName();
+        }
+
+        return fullName;
+    }
+}
diff --git a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java
index 62c64f3..6f13dac 100644
--- a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java
+++ b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java
@@ -1,21 +1,27 @@
 package org.keycloak.picketlink.idm;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
+import java.io.UnsupportedEncodingException;
 import java.util.Date;
+import java.util.List;
 
+import javax.naming.NamingException;
 import javax.naming.directory.BasicAttribute;
 import javax.naming.directory.DirContext;
 import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchResult;
 
+import org.keycloak.models.utils.reflection.Reflections;
 import org.picketlink.idm.IdentityManager;
+import org.picketlink.idm.config.LDAPMappingConfiguration;
+import org.picketlink.idm.credential.Credentials;
 import org.picketlink.idm.credential.Password;
+import org.picketlink.idm.credential.UsernamePasswordCredentials;
 import org.picketlink.idm.ldap.internal.LDAPIdentityStore;
 import org.picketlink.idm.ldap.internal.LDAPOperationManager;
 import org.picketlink.idm.ldap.internal.LDAPPlainTextPasswordCredentialHandler;
 import org.picketlink.idm.model.Account;
-import org.picketlink.idm.model.AttributedType;
 import org.picketlink.idm.model.basic.User;
+import org.picketlink.idm.query.IdentityQuery;
 import org.picketlink.idm.spi.IdentityContext;
 
 import static org.picketlink.idm.IDMLog.CREDENTIAL_LOGGER;
@@ -26,19 +32,12 @@ import static org.picketlink.idm.model.basic.BasicModel.getUser;
  */
 public class LDAPKeycloakCredentialHandler extends LDAPPlainTextPasswordCredentialHandler {
 
-    private static Method GET_BINDING_DN_METHOD;
-    private static Method GET_OPERATION_MANAGER_METHOD;
-
-    static {
-        GET_BINDING_DN_METHOD = getMethodOnLDAPStore("getBindingDN", AttributedType.class);
-        GET_OPERATION_MANAGER_METHOD = getMethodOnLDAPStore("getOperationManager");
-    }
-
     // Used just in ActiveDirectory to put account into "enabled" state (aka userAccountControl=512, see http://support.microsoft.com/kb/305144/en ) after password update. If value is -1, it's ignored
     private String userAccountControlAfterPasswordUpdate;
 
     @Override
     public void setup(LDAPIdentityStore store) {
+        // TODO: Don't setup it here once PLIDM-508 is fixed
         if (store.getConfig().isActiveDirectory() || Boolean.getBoolean("keycloak.ldap.ad.skipUserAccountControlAfterPasswordUpdate")) {
             String userAccountControlProp = System.getProperty("keycloak.ldap.ad.userAccountControlAfterPasswordUpdate");
             this.userAccountControlAfterPasswordUpdate = userAccountControlProp!=null ? userAccountControlProp : "512";
@@ -48,7 +47,7 @@ public class LDAPKeycloakCredentialHandler extends LDAPPlainTextPasswordCredenti
 
     // Overridden as in Keycloak, we don't have Agents
     @Override
-    protected Account getAccount(IdentityContext context, String loginName) {
+    protected User getAccount(IdentityContext context, String loginName) {
         IdentityManager identityManager = getIdentityManager(context);
 
         if (CREDENTIAL_LOGGER.isDebugEnabled()) {
@@ -60,34 +59,111 @@ public class LDAPKeycloakCredentialHandler extends LDAPPlainTextPasswordCredenti
 
     @Override
     public void update(IdentityContext context, Account account, Password password, LDAPIdentityStore store, Date effectiveDate, Date expiryDate) {
-        super.update(context, account, password, store, effectiveDate, expiryDate);
-
-        if (userAccountControlAfterPasswordUpdate != null) {
-            ModificationItem[] mods = new ModificationItem[1];
-            BasicAttribute mod0 = new BasicAttribute("userAccountControl", userAccountControlAfterPasswordUpdate);
-            mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, mod0);
-
-            try {
-                String bindingDN = (String) GET_BINDING_DN_METHOD.invoke(store, account);
-                LDAPOperationManager operationManager = (LDAPOperationManager) GET_OPERATION_MANAGER_METHOD.invoke(store);
-                operationManager.modifyAttribute(bindingDN, mod0);
-            } catch (IllegalAccessException iae) {
-                throw new RuntimeException(iae);
-            } catch (InvocationTargetException ite) {
-                Throwable cause = ite.getTargetException() != null ? ite.getTargetException() : ite;
-                throw new RuntimeException(cause);
+        if (!store.getConfig().isActiveDirectory()) {
+            super.update(context, account, password, store, effectiveDate, expiryDate);
+        } else {
+            User user = (User)account;
+            LDAPOperationManager operationManager = Reflections.invokeMethod(false, KeycloakLDAPIdentityStore.GET_OPERATION_MANAGER_METHOD, LDAPOperationManager.class, store);
+            IdentityManager identityManager = getIdentityManager(context);
+            String userDN = getDNOfUser(user, identityManager, store, operationManager);
+
+            updateADPassword(userDN, new String(password.getValue()), operationManager);
+
+            if (userAccountControlAfterPasswordUpdate != null) {
+                ModificationItem[] mods = new ModificationItem[1];
+                BasicAttribute mod0 = new BasicAttribute("userAccountControl", userAccountControlAfterPasswordUpdate);
+                mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, mod0);
+                operationManager.modifyAttribute(userDN, mod0);
             }
         }
     }
 
-    // Hack as methods are protected on LDAPIdentityStore :/
-    private static Method getMethodOnLDAPStore(String methodName, Class... classes) {
+    protected void updateADPassword(String userDN, String password, LDAPOperationManager operationManager) {
         try {
-            Method m = LDAPIdentityStore.class.getDeclaredMethod(methodName, classes);
-            m.setAccessible(true);
-            return m;
-        } catch (Exception e) {
+            // Replace the "unicdodePwd" attribute with a new value
+            // Password must be both Unicode and a quoted string
+            String newQuotedPassword = "\"" + password + "\"";
+            byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
+            BasicAttribute unicodePwd = new BasicAttribute("unicodePwd", newUnicodePassword);
+            operationManager.modifyAttribute(userDN, unicodePwd);
+        } catch (UnsupportedEncodingException e) {
             throw new RuntimeException(e);
         }
     }
+
+    @Override
+    public void validate(IdentityContext context, UsernamePasswordCredentials credentials,
+                         LDAPIdentityStore store) {
+        credentials.setStatus(Credentials.Status.INVALID);
+        credentials.setValidatedAccount(null);
+
+        if (CREDENTIAL_LOGGER.isDebugEnabled()) {
+            CREDENTIAL_LOGGER.debugf("Validating credentials [%s][%s] using identity store [%s] and credential handler [%s].", credentials.getClass(), credentials, store, this);
+        }
+
+        User account = getAccount(context, credentials.getUsername());
+
+        // If the user for the provided username cannot be found we fail validation
+        if (account != null) {
+            if (CREDENTIAL_LOGGER.isDebugEnabled()) {
+                CREDENTIAL_LOGGER.debugf("Found account [%s] from credentials [%s].", account, credentials);
+            }
+
+            if (account.isEnabled()) {
+                if (CREDENTIAL_LOGGER.isDebugEnabled()) {
+                    CREDENTIAL_LOGGER.debugf("Account [%s] is ENABLED.", account, credentials);
+                }
+
+                char[] password = credentials.getPassword().getValue();
+
+                // String bindingDN = store.getBindingDN(account);
+
+                LDAPOperationManager operationManager = Reflections.invokeMethod(false, KeycloakLDAPIdentityStore.GET_OPERATION_MANAGER_METHOD, LDAPOperationManager.class, store);
+                String bindingDN = getDNOfUser(account, getIdentityManager(context), store, operationManager);
+
+                if (operationManager.authenticate(bindingDN, new String(password))) {
+                    credentials.setValidatedAccount(account);
+                    credentials.setStatus(Credentials.Status.VALID);
+                }
+            } else {
+                if (CREDENTIAL_LOGGER.isDebugEnabled()) {
+                    CREDENTIAL_LOGGER.debugf("Account [%s] is DISABLED.", account, credentials);
+                }
+                credentials.setStatus(Credentials.Status.ACCOUNT_DISABLED);
+            }
+        } else {
+            if (CREDENTIAL_LOGGER.isDebugEnabled()) {
+                CREDENTIAL_LOGGER.debugf("Account NOT FOUND for credentials [%s][%s].", credentials.getClass(), credentials);
+            }
+        }
+
+        if (CREDENTIAL_LOGGER.isDebugEnabled()) {
+            CREDENTIAL_LOGGER.debugf("Credential [%s][%s] validated using identity store [%s] and credential handler [%s]. Status [%s]. Validated Account [%s]",
+                    credentials.getClass(), credentials, store, this, credentials.getStatus(), credentials.getValidatedAccount());
+        }
+    }
+
+    // TODO: remove later... It's needed just because LDAPIdentityStore.getBindingDN, which always uses idProperty as first part of DN, but in AD it doesn't work as we may have idProperty 'sAMAccountName'
+    // but DN like: cn=John Doe,OU=foo,DC=bar
+    protected String getDNOfUser(User user, IdentityManager identityManager, LDAPIdentityStore ldapStore, LDAPOperationManager operationManager) {
+
+        LDAPMappingConfiguration ldapEntryConfig = ldapStore.getConfig().getMappingConfig(User.class);
+        IdentityQuery<User> identityQuery = identityManager.createIdentityQuery(User.class)
+                .setParameter(User.LOGIN_NAME, user.getLoginName());
+        StringBuilder filter = Reflections.invokeMethod(false, KeycloakLDAPIdentityStore.CREATE_SEARCH_FILTER_METHOD, StringBuilder.class, ldapStore, identityQuery, ldapEntryConfig);
+
+        List<SearchResult> search = null;
+        try {
+            search = operationManager.search(ldapEntryConfig.getBaseDN(), filter.toString(), ldapEntryConfig);
+        } catch (NamingException ne) {
+            throw new RuntimeException(ne);
+        }
+
+        if (search.size() > 0) {
+            SearchResult sr1 = search.get(0);
+            return sr1.getNameInNamespace();
+        } else {
+            return null;
+        }
+    }
 }
diff --git a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/PartitionManagerRegistry.java b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/PartitionManagerRegistry.java
index 6204aa7..84a62c2 100644
--- a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/PartitionManagerRegistry.java
+++ b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/PartitionManagerRegistry.java
@@ -1,15 +1,20 @@
 package org.keycloak.picketlink.realm;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.jboss.logging.Logger;
+import org.keycloak.models.LDAPConstants;
 import org.keycloak.models.RealmModel;
+import org.keycloak.picketlink.idm.KeycloakLDAPIdentityStore;
 import org.keycloak.picketlink.idm.LDAPKeycloakCredentialHandler;
-import org.keycloak.picketlink.idm.LdapConstants;
 import org.picketlink.idm.PartitionManager;
+import org.picketlink.idm.config.AbstractIdentityStoreConfiguration;
+import org.picketlink.idm.config.IdentityConfiguration;
 import org.picketlink.idm.config.IdentityConfigurationBuilder;
+import org.picketlink.idm.config.IdentityStoreConfiguration;
 import org.picketlink.idm.config.LDAPIdentityStoreConfiguration;
 import org.picketlink.idm.internal.DefaultPartitionManager;
 import org.picketlink.idm.model.basic.User;
@@ -40,7 +45,7 @@ public class PartitionManagerRegistry {
         // Ldap config might have changed for the realm. In this case, we must re-initialize
         if (context == null || !ldapConfig.equals(context.config)) {
             logger.infof("Creating new partition manager for the realm: %s, LDAP Connection URL: %s, LDAP Base DN: %s, LDAP Vendor: %s", realm.getId(),
-                    ldapConfig.get(LdapConstants.CONNECTION_URL), ldapConfig.get(LdapConstants.BASE_DN), ldapConfig.get(LdapConstants.VENDOR));
+                    ldapConfig.get(LDAPConstants.CONNECTION_URL), ldapConfig.get(LDAPConstants.BASE_DN), ldapConfig.get(LDAPConstants.VENDOR));
             PartitionManager manager = createPartitionManager(ldapConfig);
             context = new PartitionManagerContext(ldapConfig, manager);
             partitionManagers.put(realm.getId(), context);
@@ -66,22 +71,27 @@ public class PartitionManagerRegistry {
         checkSystemProperty("com.sun.jndi.ldap.connect.pool.protocol", "plain");
         checkSystemProperty("com.sun.jndi.ldap.connect.pool.debug", "off");
 
-        String vendor = ldapConfig.get(LdapConstants.VENDOR);
+        String vendor = ldapConfig.get(LDAPConstants.VENDOR);
 
         // RHDS is using "nsuniqueid" as unique identifier instead of "entryUUID"
-        if (vendor != null && vendor.equals(LdapConstants.VENDOR_RHDS)) {
+        if (vendor != null && vendor.equals(LDAPConstants.VENDOR_RHDS)) {
             checkSystemProperty(LDAPIdentityStoreConfiguration.ENTRY_IDENTIFIER_ATTRIBUTE_NAME, "nsuniqueid");
         }
 
-        boolean activeDirectory = vendor != null && vendor.equals(LdapConstants.VENDOR_ACTIVE_DIRECTORY);
+        boolean activeDirectory = vendor != null && vendor.equals(LDAPConstants.VENDOR_ACTIVE_DIRECTORY);
+
+        String ldapLoginNameMapping = ldapConfig.get(LDAPConstants.USERNAME_LDAP_ATTRIBUTE);
+        if (ldapLoginNameMapping == null) {
+            ldapLoginNameMapping = activeDirectory ? CN : UID;
+        }
 
         // Try to compute properties based on LDAP server type, but still allow to override them through System properties TODO: Should allow better way than overriding from System properties. Perhaps init from XML?
-        String ldapLoginName = getNameOfLDAPAttribute("keycloak.ldap.idm.loginName", UID, CN, activeDirectory);
-        String ldapFirstName = getNameOfLDAPAttribute("keycloak.ldap.idm.firstName", CN, "givenName", activeDirectory);
-        String ldapLastName = getNameOfLDAPAttribute("keycloak.ldap.idm.lastName", SN, SN, activeDirectory);
-        String ldapEmail =  getNameOfLDAPAttribute("keycloak.ldap.idm.email", EMAIL, EMAIL, activeDirectory);
+        ldapLoginNameMapping = getNameOfLDAPAttribute("keycloak.ldap.idm.loginName", ldapLoginNameMapping, ldapLoginNameMapping, activeDirectory);
+        String ldapFirstNameMapping = getNameOfLDAPAttribute("keycloak.ldap.idm.firstName", CN, "givenName", activeDirectory);
+        String ldapLastNameMapping = getNameOfLDAPAttribute("keycloak.ldap.idm.lastName", SN, SN, activeDirectory);
+        String ldapEmailMapping =  getNameOfLDAPAttribute("keycloak.ldap.idm.email", EMAIL, EMAIL, activeDirectory);
 
-        logger.infof("LDAP Attributes mapping: loginName: %s, firstName: %s, lastName: %s, email: %s", ldapLoginName, ldapFirstName, ldapLastName, ldapEmail);
+        logger.infof("LDAP Attributes mapping: loginName: %s, firstName: %s, lastName: %s, email: %s", ldapLoginNameMapping, ldapFirstNameMapping, ldapLastNameMapping, ldapEmailMapping);
 
         // Use same mapping for User and Agent for now
         builder
@@ -90,21 +100,26 @@ public class PartitionManagerRegistry {
                     .ldap()
                         .connectionProperties(connectionProps)
                         .addCredentialHandler(LDAPKeycloakCredentialHandler.class)
-                        .baseDN(ldapConfig.get(LdapConstants.BASE_DN))
-                        .bindDN(ldapConfig.get(LdapConstants.BIND_DN))
-                        .bindCredential(ldapConfig.get(LdapConstants.BIND_CREDENTIAL))
-                        .url(ldapConfig.get(LdapConstants.CONNECTION_URL))
+                        .baseDN(ldapConfig.get(LDAPConstants.BASE_DN))
+                        .bindDN(ldapConfig.get(LDAPConstants.BIND_DN))
+                        .bindCredential(ldapConfig.get(LDAPConstants.BIND_CREDENTIAL))
+                        .url(ldapConfig.get(LDAPConstants.CONNECTION_URL))
                         .activeDirectory(activeDirectory)
                         .supportAllFeatures()
                         .mapping(User.class)
-                            .baseDN(ldapConfig.get(LdapConstants.USER_DN_SUFFIX))
+                            .baseDN(ldapConfig.get(LDAPConstants.USER_DN_SUFFIX))
                             .objectClasses("inetOrgPerson", "organizationalPerson")
-                            .attribute("loginName", ldapLoginName, true)
-                            .attribute("firstName", ldapFirstName)
-                            .attribute("lastName", ldapLastName)
-                            .attribute("email", ldapEmail);
+                            .attribute("loginName", ldapLoginNameMapping, true)
+                            .attribute("firstName", ldapFirstNameMapping)
+                            .attribute("lastName", ldapLastNameMapping)
+                            .attribute("email", ldapEmailMapping);
+
+        // Workaround to override the LDAPIdentityStore with our own :/
+        List<IdentityConfiguration> identityConfigs = builder.buildAll();
+        IdentityStoreConfiguration identityStoreConfig = identityConfigs.get(0).getStoreConfiguration().get(0);
+        ((AbstractIdentityStoreConfiguration)identityStoreConfig).setIdentityStoreType(KeycloakLDAPIdentityStore.class);
 
-        return new DefaultPartitionManager(builder.buildAll());
+        return new DefaultPartitionManager(identityConfigs);
     }
 
     private void checkSystemProperty(String name, String defaultValue) {

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index cd19d16..66927cb 100755
--- a/pom.xml
+++ b/pom.xml
@@ -19,7 +19,7 @@
         <resteasy.version.eap.6.3>2.3.7.Final</resteasy.version.eap.6.3>
         <resteasy.version.latest>3.0.8.Final</resteasy.version.latest>
         <undertow.version>1.0.15.Final</undertow.version>
-        <picketlink.version>2.6.0.CR2</picketlink.version>
+        <picketlink.version>2.6.0.CR5</picketlink.version>
         <picketbox.ldap.version>1.0.2.Final</picketbox.ldap.version>
         <mongo.driver.version>2.11.3</mongo.driver.version>
         <jboss.logging.version>3.1.1.GA</jboss.logging.version>
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 76b8876..dd4197e 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -276,7 +276,7 @@ public class AuthenticationManager {
                 user.setLastName(authUser.getLastName());
                 user.setEmail(authUser.getEmail());
                 user.setAuthenticationLink(new AuthenticationLinkModel(authUser.getProviderName(), authUser.getId()));
-                logger.info("User " + authUser.getUsername() + " created and linked with provider " + authUser.getProviderName());
+                logger.info("User " + authUser.getUsername() + " created in Keycloak and linked with provider " + authUser.getProviderName());
             } else {
                 logger.warn("User " + username + " not found");
                 return AuthenticationStatus.INVALID_USER;
diff --git a/services/src/main/java/org/keycloak/services/managers/LDAPConnectionTestManager.java b/services/src/main/java/org/keycloak/services/managers/LDAPConnectionTestManager.java
new file mode 100644
index 0000000..a11fd74
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/managers/LDAPConnectionTestManager.java
@@ -0,0 +1,64 @@
+package org.keycloak.services.managers;
+
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.ldap.InitialLdapContext;
+
+import org.jboss.logging.Logger;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.RealmModel;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class LDAPConnectionTestManager {
+
+    protected static final Logger logger = Logger.getLogger(LDAPConnectionTestManager.class);
+
+    public static final String TEST_CONNECTION = "testConnection";
+    public static final String TEST_AUTHENTICATION = "testAuthentication";
+
+    public boolean testLDAP(String action, String connectionUrl, String bindDn, String bindCredential) {
+        if (!TEST_CONNECTION.equals(action) && !TEST_AUTHENTICATION.equals(action)) {
+            logger.error("Unknown action: " + action);
+            return false;
+        }
+
+        Context ldapContext = null;
+        try {
+            Hashtable<String, Object> env = new Hashtable<String, Object>();
+            env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+            env.put(Context.SECURITY_AUTHENTICATION, "simple");
+
+            env.put(Context.PROVIDER_URL, connectionUrl);
+
+            if (TEST_AUTHENTICATION.equals(action)) {
+                env.put(Context.SECURITY_PRINCIPAL, bindDn);
+
+                char[] bindCredentialChar = null;
+                if (bindCredential != null) {
+                    bindCredentialChar = bindCredential.toCharArray();
+                }
+                env.put(Context.SECURITY_CREDENTIALS, bindCredentialChar);
+            }
+
+            ldapContext = new InitialLdapContext(env, null);
+            return true;
+        } catch (NamingException ne) {
+            String errorMessage = (TEST_AUTHENTICATION.equals(action)) ? "Error when authenticating to LDAP: " : "Error when connecting to LDAP: ";
+            logger.error(errorMessage + ne.getMessage(), ne);
+            return false;
+        } finally {
+            if (ldapContext != null) {
+                try {
+                    ldapContext.close();
+                } catch (NamingException ne) {
+                    logger.warn("Error when closing LDAP connection", ne);
+                }
+            }
+        }
+    }
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index cc2903f..0a6059e 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -17,6 +17,7 @@ import org.keycloak.provider.ProviderSession;
 import org.keycloak.representations.adapters.action.SessionStats;
 import org.keycloak.representations.idm.RealmAuditRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.LDAPConnectionTestManager;
 import org.keycloak.services.managers.ModelToRepresentation;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.ResourceAdminManager;
@@ -359,4 +360,14 @@ public class RealmAdminResource {
         AuditProvider audit = providers.getProvider(AuditProvider.class);
         audit.clear(realm.getId());
     }
+
+    @Path("testLDAPConnection")
+    @GET
+    public Response testLDAPConnection(@QueryParam("action") String action, @QueryParam("connectionUrl") String connectionUrl,
+                                       @QueryParam("bindDn") String bindDn, @QueryParam("bindCredential") String bindCredential) {
+        auth.init(RealmAuth.Resource.REALM).requireManage();
+
+        boolean result = new LDAPConnectionTestManager().testLDAP(action, connectionUrl, bindDn, bindCredential);
+        return result ? Response.noContent().build() : Flows.errors().error("LDAP test error", Response.Status.BAD_REQUEST);
+    }
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java
index 6f7469a..461b8a8 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java
@@ -9,7 +9,7 @@ import org.junit.rules.RuleChain;
 import org.junit.rules.TestRule;
 import org.junit.runners.MethodSorters;
 import org.keycloak.OAuth2Constants;
-import org.keycloak.model.test.LdapTestUtils;
+import org.keycloak.model.test.LDAPTestUtils;
 import org.keycloak.models.AuthenticationProviderModel;
 import org.keycloak.models.PasswordPolicy;
 import org.keycloak.models.RealmModel;
@@ -62,7 +62,7 @@ public class AuthProvidersIntegrationTest {
 
             // Configure LDAP
             ldapRule.getEmbeddedServer().setupLdapInRealm(appRealm);
-            LdapTestUtils.setLdapPassword(providerSession, appRealm, "johnkeycloak", "password");
+            LDAPTestUtils.setLdapPassword(providerSession, appRealm, "johnkeycloak", "password");
         }
     });