keycloak-memoizeit

Changes

Details

diff --git a/examples/kerberos/README.md b/examples/kerberos/README.md
index c39786b..c65316c 100644
--- a/examples/kerberos/README.md
+++ b/examples/kerberos/README.md
@@ -19,7 +19,7 @@ cp http.keytab /tmp/http.keytab
 ```
 
 Alternative is to configure different location for `keyTab` property in `kerberosrealm.json` configuration file (On Windows this will be needed).
-WARNING: In production, keytab file should be in secured location accessible just to the user under which is Keycloak server running.
+**WARNING**: In production, keytab file should be in secured location accessible just to the user under which is Keycloak server running.
 
 
 **3)** Run Keycloak server and import `kerberosrealm.json` into it through admin console. This will import realm with sample application
@@ -47,6 +47,8 @@ See [this file](https://github.com/keycloak/keycloak/blob/master/testsuite/integ
 with these commands (assuming you're in `kerberos` directory with this example)
 
 ```
+cd ../ldap
+mvn clean install
 cd ..
 java -jar ldap/embedded-ldap/target/embedded-ldap.jar kerberos
 ```
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java
index 141ae38..43057d1 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java
@@ -10,6 +10,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
+import java.util.TreeSet;
 
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
@@ -130,7 +131,7 @@ public class LDAPIdentityStore implements IdentityStore {
                                 .lookupById(baseDN, equalCondition.getValue().toString(), identityQuery.getReturningLdapAttributes());
 
                         if (search != null) {
-                            results.add(populateAttributedType(search, identityQuery.getReturningReadOnlyLdapAttributes()));
+                            results.add(populateAttributedType(search, identityQuery));
                         }
                     }
 
@@ -150,7 +151,7 @@ public class LDAPIdentityStore implements IdentityStore {
 
             for (SearchResult result : search) {
                 if (!result.getNameInNamespace().equalsIgnoreCase(baseDN)) {
-                    results.add(populateAttributedType(result, identityQuery.getReturningReadOnlyLdapAttributes()));
+                    results.add(populateAttributedType(result, identityQuery));
                 }
             }
         } catch (Exception e) {
@@ -371,7 +372,13 @@ public class LDAPIdentityStore implements IdentityStore {
     }
 
 
-    private LDAPObject populateAttributedType(SearchResult searchResult, Collection<String> readOnlyAttrNames) {
+    private LDAPObject populateAttributedType(SearchResult searchResult, LDAPQuery ldapQuery) {
+        Set<String> readOnlyAttrNames = ldapQuery.getReturningReadOnlyLdapAttributes();
+        Set<String> lowerCasedAttrNames = new TreeSet<>();
+        for (String attrName : ldapQuery.getReturningLdapAttributes()) {
+            lowerCasedAttrNames.add(attrName.toLowerCase());
+        }
+
         try {
             String entryDN = searchResult.getNameInNamespace();
             Attributes attributes = searchResult.getAttributes();
@@ -397,7 +404,10 @@ public class LDAPIdentityStore implements IdentityStore {
                 if (ldapAttributeName.equalsIgnoreCase(getConfig().getUuidLDAPAttributeName())) {
                     Object uuidValue = ldapAttribute.get();
                     ldapObject.setUuid(this.operationManager.decodeEntryUUID(uuidValue));
-                } else {
+                }
+
+                // Note: UUID is normally not populated here. It's populated just in case that it's used for name of other attribute as well
+                if (!ldapAttributeName.equalsIgnoreCase(getConfig().getUuidLDAPAttributeName()) || (lowerCasedAttrNames.contains(ldapAttributeName.toLowerCase()))) {
                     Set<String> attrValues = new LinkedHashSet<>();
                     NamingEnumeration<?> enumm = ldapAttribute.getAll();
                     while (enumm.hasMoreElements()) {
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java
index 18b8a86..3e28dfe 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java
@@ -240,7 +240,7 @@ public class LDAPOperationManager {
 
                 filter = "(&(objectClass=*)(" + getUuidAttributeName() + LDAPConstants.EQUAL + LDAPUtil.convertObjectGUIToByteString(objectGUID) + "))";
             } catch (NamingException ne) {
-                return filter;
+                filter = null;
             }
         }
 
@@ -435,7 +435,7 @@ public class LDAPOperationManager {
     public String decodeEntryUUID(final Object entryUUID) {
         String id;
 
-        if (this.config.isActiveDirectory()) {
+        if (this.config.isActiveDirectory() && entryUUID instanceof byte[]) {
             id = LDAPUtil.decodeObjectGUID((byte[]) entryUUID);
         } else {
             id = entryUUID.toString();
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
index 597f9fe..1fa601d 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
@@ -431,13 +431,6 @@ public class LDAPFederationProvider implements UserFederationProvider {
             return null;
         }
 
-        // KEYCLOAK-808: Should we allow case-sensitivity to be configurable?
-        String ldapUsername = LDAPUtils.getUsername(ldapUser, ldapIdentityStore.getConfig());
-        if (!username.equals(ldapUsername)) {
-            logger.warnf("User found in LDAP but with different username. LDAP username: %s, Searched username: %s", username, ldapUsername);
-            return null;
-        }
-
         return ldapUser;
     }
 
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java
index 438c57d..536753f 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java
@@ -23,6 +23,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserFederationMapperModel;
 import org.keycloak.models.UserFederationProvider;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.UserModelDelegate;
 import org.keycloak.models.utils.reflection.Property;
 import org.keycloak.models.utils.reflection.PropertyCriteria;
@@ -138,6 +139,9 @@ public class UserAttributeLDAPFederationMapper extends AbstractLDAPFederationMap
     // throw ModelDuplicateException if there is different user in model with same email
     protected void checkDuplicateEmail(String userModelAttrName, String email, RealmModel realm, KeycloakSession session, UserModel user) {
         if (UserModel.EMAIL.equalsIgnoreCase(userModelAttrName)) {
+            // lowercase before search
+            email = KeycloakModelUtils.toLowerCaseSafe(email);
+
             UserModel that = session.userStorage().getUserByEmail(email, realm);
             if (that != null && !that.getId().equals(user.getId())) {
                 session.getTransaction().setRollbackOnly();
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
index 51644ba..e885f7e 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
@@ -69,6 +69,8 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, cl
                 $scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.client.id});
                 $scope.selectedClientRoles = [];
                 $scope.selectedClientMappings = [];
+                $scope.realmComposite = CompositeRealmRoleMapping.query({realm : realm.realm, userId : user.id});
+                $scope.realmRoles = AvailableRealmRoleMapping.query({realm : realm.realm, userId : user.id});
                 Notifications.success("Role mappings updated.");
             });
     };
@@ -81,6 +83,8 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, cl
                 $scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.id, client : $scope.client.id});
                 $scope.selectedClientRoles = [];
                 $scope.selectedClientMappings = [];
+                $scope.realmComposite = CompositeRealmRoleMapping.query({realm : realm.realm, userId : user.id});
+                $scope.realmRoles = AvailableRealmRoleMapping.query({realm : realm.realm, userId : user.id});
                 Notifications.success("Role mappings updated.");
             });
     };
diff --git a/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java b/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java
index 29f3d4a..e2ef2f6 100755
--- a/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java
+++ b/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java
@@ -1,16 +1,14 @@
 package org.keycloak.migration.migrators;
 
 import org.keycloak.migration.ModelVersion;
-import org.keycloak.models.AuthenticationExecutionModel;
-import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.ImpersonationConstants;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
-import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.DefaultAuthenticationFlows;
 import org.keycloak.models.utils.DefaultRequiredActions;
+import org.keycloak.models.utils.KeycloakModelUtils;
 
-import java.util.HashSet;
 import java.util.List;
 
 /**
@@ -28,7 +26,19 @@ public class MigrateTo1_4_0 {
                 DefaultRequiredActions.addActions(realm);
             }
             ImpersonationConstants.setupImpersonationService(session, realm);
+            migrateUsers(session, realm);
         }
 
     }
+
+    public void migrateUsers(KeycloakSession session, RealmModel realm) {
+        List<UserModel> users = session.userStorage().getUsers(realm);
+        for (UserModel user : users) {
+            String email = user.getEmail();
+            email = KeycloakModelUtils.toLowerCaseSafe(email);
+            if (email != null && !email.equals(user.getEmail())) {
+                user.setEmail(email);
+            }
+        }
+    }
 }
diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index f26b4e9..c98a114 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -350,4 +350,7 @@ public final class KeycloakModelUtils {
         return mapperModel;
     }
 
+    public static String toLowerCaseSafe(String str) {
+        return str==null ? str : str.toLowerCase();
+    }
 }
diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java
index f037f01..5db8f93 100755
--- a/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java
+++ b/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java
@@ -86,6 +86,8 @@ public class UserAdapter implements UserModel, Comparable {
 
     @Override
     public void setUsername(String username) {
+        username = KeycloakModelUtils.toLowerCaseSafe(username);
+
         if (getUsername() == null) {
             user.setUsername(username);
             return;
@@ -145,6 +147,8 @@ public class UserAdapter implements UserModel, Comparable {
 
     @Override
     public void setEmail(String email) {
+        email = KeycloakModelUtils.toLowerCaseSafe(email);
+
         if (email == null) {
             user.setEmail(email);
             return;
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java
index 46130a9..b075ea1 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java
@@ -10,6 +10,7 @@ import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserCredentialValueModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.cache.entities.CachedUser;
+import org.keycloak.models.utils.KeycloakModelUtils;
 
 import java.util.Collections;
 import java.util.HashSet;
@@ -57,6 +58,7 @@ public class UserAdapter implements UserModel {
     @Override
     public void setUsername(String username) {
         getDelegateForUpdate();
+        username = KeycloakModelUtils.toLowerCaseSafe(username);
         updated.setUsername(username);
     }
 
@@ -189,6 +191,7 @@ public class UserAdapter implements UserModel {
     @Override
     public void setEmail(String email) {
         getDelegateForUpdate();
+        email = KeycloakModelUtils.toLowerCaseSafe(email);
         updated.setEmail(email);
     }
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
index 0e10110..ca0c284 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
@@ -74,6 +74,7 @@ public class UserAdapter implements UserModel {
 
     @Override
     public void setUsername(String username) {
+        username = KeycloakModelUtils.toLowerCaseSafe(username);
         user.setUsername(username);
     }
 
@@ -266,6 +267,7 @@ public class UserAdapter implements UserModel {
 
     @Override
     public void setEmail(String email) {
+        email = KeycloakModelUtils.toLowerCaseSafe(email);
         user.setEmail(email);
     }
 
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
index f63494b..a840813 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
@@ -67,6 +67,8 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
 
     @Override
     public void setUsername(String username) {
+        username = KeycloakModelUtils.toLowerCaseSafe(username);
+
         user.setUsername(username);
         updateUser();
     }
@@ -121,6 +123,8 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
 
     @Override
     public void setEmail(String email) {
+        email = KeycloakModelUtils.toLowerCaseSafe(email);
+
         user.setEmail(email);
         updateUser();
     }
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index 9f223d2..a6888a9 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -144,7 +144,6 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
             Response response = identityProvider.performLogin(createAuthenticationRequest(providerId, clientSessionCode));
 
             if (response != null) {
-                this.event.success();
                 if (isDebugEnabled()) {
                     LOGGER.debugf("Identity provider [%s] is going to send a request [%s].", identityProvider, response);
                 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java
index ab644cd..aa5c392 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java
@@ -27,9 +27,13 @@ import static org.junit.Assert.fail;
 public class UserTest extends AbstractClientTest {
 
     public String createUser() {
+        return createUser("user1", "user1@localhost");
+    }
+
+    public String createUser(String username, String email) {
         UserRepresentation user = new UserRepresentation();
-        user.setUsername("user1");
-        user.setEmail("user1@localhost");
+        user.setUsername(username);
+        user.setEmail(email);
 
         Response response = realm.users().create(user);
         String createdId = ApiUtil.getCreatedId(response);
@@ -115,6 +119,19 @@ public class UserTest extends AbstractClientTest {
         assertEquals(409, response.getStatus());
         response.close();
     }
+
+    @Test
+    public void createDuplicatedUser7() {
+        createUser("user1", "USer1@Localhost");
+
+        UserRepresentation user = new UserRepresentation();
+        user.setUsername("user2");
+        user.setEmail("user1@localhost");
+        Response response = realm.users().create(user);
+        assertEquals(409, response.getStatus());
+        response.close();
+
+    }
     
     private void createUsers() {
         for (int i = 1; i < 10; i++) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java
index 708da5b..b91ee08 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java
@@ -39,6 +39,7 @@ import org.keycloak.testsuite.rule.WebResource;
 import org.keycloak.testsuite.rule.WebRule;
 import org.openqa.selenium.WebDriver;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -113,12 +114,75 @@ public class FederationProvidersIntegrationTest {
 
 
     @Test
-    public void caseSensitiveSearch() {
+    public void caseInSensitiveImport() {
+        KeycloakSession session = keycloakRule.startSession();
+        try {
+            RealmManager manager = new RealmManager(session);
+            RealmModel appRealm = manager.getRealm("test");
+            LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
+            LDAPObject jbrown2 = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "JBrown2", "John", "Brown2", "jbrown2@email.org", null, "1234");
+            ldapFedProvider.getLdapIdentityStore().updatePassword(jbrown2, "password");
+            LDAPObject jbrown3 = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "jbrown3", "John", "Brown3", "JBrown3@email.org", null, "1234");
+            ldapFedProvider.getLdapIdentityStore().updatePassword(jbrown3, "password");
+        } finally {
+            keycloakRule.stopSession(session, true);
+        }
+
+        loginSuccessAndLogout("jbrown2", "password");
+        loginSuccessAndLogout("JBrown2", "password");
+        loginSuccessAndLogout("jbrown2@email.org", "password");
+        loginSuccessAndLogout("JBrown2@email.org", "password");
+
+        loginSuccessAndLogout("jbrown3", "password");
+        loginSuccessAndLogout("JBrown3", "password");
+        loginSuccessAndLogout("jbrown3@email.org", "password");
+        loginSuccessAndLogout("JBrown3@email.org", "password");
+    }
+
+    private void loginSuccessAndLogout(String username, String password) {
         loginPage.open();
+        loginPage.login(username, password);
+        Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+        Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
+        oauth.openLogout();
+    }
 
-        // This should fail for now due to case-sensitivity
-        loginPage.login("johnKeycloak", "Password1");
-        Assert.assertEquals("Invalid username or password.", loginPage.getError());
+    @Test
+    public void caseInsensitiveSearch() {
+        KeycloakSession session = keycloakRule.startSession();
+        try {
+            RealmManager manager = new RealmManager(session);
+            RealmModel appRealm = manager.getRealm("test");
+            LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
+            LDAPObject jbrown2 = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "JBrown4", "John", "Brown4", "jbrown4@email.org", null, "1234");
+            ldapFedProvider.getLdapIdentityStore().updatePassword(jbrown2, "password");
+            LDAPObject jbrown3 = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "jbrown5", "John", "Brown5", "JBrown5@Email.org", null, "1234");
+            ldapFedProvider.getLdapIdentityStore().updatePassword(jbrown3, "password");
+        } finally {
+            keycloakRule.stopSession(session, true);
+        }
+
+        session = keycloakRule.startSession();
+        try {
+            RealmManager manager = new RealmManager(session);
+            RealmModel appRealm = manager.getRealm("test");
+
+            // search by username
+            List<UserModel> users = session.users().searchForUser("JBROwn4", appRealm);
+            Assert.assertEquals(1, users.size());
+            UserModel user4 = users.get(0);
+            Assert.assertEquals("jbrown4", user4.getUsername());
+            Assert.assertEquals("jbrown4@email.org", user4.getEmail());
+
+            // search by email
+            users = session.users().searchForUser("JBROwn5@eMAil.org", appRealm);
+            Assert.assertEquals(1, users.size());
+            UserModel user5 = users.get(0);
+            Assert.assertEquals("jbrown5", user5.getUsername());
+            Assert.assertEquals("jbrown5@email.org", user5.getEmail());
+        } finally {
+            keycloakRule.stopSession(session, true);
+        }
     }
 
     @Test
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/SyncProvidersTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/SyncProvidersTest.java
index c13c9bc..e50caf8 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/SyncProvidersTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/SyncProvidersTest.java
@@ -7,6 +7,7 @@ import org.junit.Test;
 import org.junit.rules.RuleChain;
 import org.junit.rules.TestRule;
 import org.junit.runners.MethodSorters;
+import org.keycloak.federation.ldap.LDAPConfig;
 import org.keycloak.federation.ldap.LDAPFederationProvider;
 import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
 import org.keycloak.federation.ldap.idm.model.LDAPObject;
@@ -17,9 +18,12 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserFederationProvider;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserFederationSyncResult;
+import org.keycloak.models.UserModel;
 import org.keycloak.models.UserProvider;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.UsersSyncManager;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
 import org.keycloak.testsuite.rule.KeycloakRule;
 import org.keycloak.testsuite.rule.LDAPRule;
 import org.keycloak.testsuite.DummyUserFederationProviderFactory;
@@ -147,7 +151,7 @@ public class SyncProvidersTest {
             RealmModel testRealm = session.realms().getRealm("test");
             UserProvider userProvider = session.userStorage();
             // Assert users updated in local provider
-            FederationTestUtils.assertUserImported(userProvider, testRealm, "user5", "User5FN", "User5LN", "user5Updated@email.org", "521");
+            FederationTestUtils.assertUserImported(userProvider, testRealm, "user5", "User5FN", "User5LN", "user5updated@email.org", "521");
             FederationTestUtils.assertUserImported(userProvider, testRealm, "user6", "User6FN", "User6LN", "user6@email.org", "126");
         } finally {
             keycloakRule.stopSession(session, false);
@@ -213,6 +217,68 @@ public class SyncProvidersTest {
         }
     }
 
+    // KEYCLOAK-1571
+    @Test
+    public void test03SameUUIDAndUsernameSync() {
+        KeycloakSession session = keycloakRule.startSession();
+        String origUuidAttrName;
+
+        try {
+            RealmModel testRealm = session.realms().getRealm("test");
+
+            // Remove all users from model
+            for (UserModel user : session.userStorage().getUsers(testRealm)) {
+                session.userStorage().removeUser(testRealm, user);
+            }
+
+            UserFederationProviderModel providerModel = KeycloakModelUtils.findUserFederationProviderByDisplayName(ldapModel.getDisplayName(), testRealm);
+
+            // Change name of UUID attribute to same like usernameAttribute
+            LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
+            String uidAttrName = ldapFedProvider.getLdapIdentityStore().getConfig().getUsernameLdapAttribute();
+            origUuidAttrName = providerModel.getConfig().get(LDAPConstants.UUID_LDAP_ATTRIBUTE);
+            providerModel.getConfig().put(LDAPConstants.UUID_LDAP_ATTRIBUTE, uidAttrName);
+
+            // Need to change this due to ApacheDS pagination bug (For other LDAP servers, pagination works fine) TODO: Remove once ApacheDS upgraded and pagination is fixed
+            providerModel.getConfig().put(LDAPConstants.BATCH_SIZE_FOR_SYNC, "10");
+            testRealm.updateUserFederationProvider(providerModel);
+
+        } finally {
+            keycloakRule.stopSession(session, true);
+        }
+
+        session = keycloakRule.startSession();
+        try {
+            RealmModel testRealm = session.realms().getRealm("test");
+            UserFederationProviderModel providerModel = KeycloakModelUtils.findUserFederationProviderByDisplayName(ldapModel.getDisplayName(), testRealm);
+
+            KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
+            UserFederationSyncResult syncResult = new UsersSyncManager().syncAllUsers(sessionFactory, "test", providerModel);
+            Assert.assertEquals(0, syncResult.getFailed());
+
+        } finally {
+            keycloakRule.stopSession(session, false);
+        }
+
+        session = keycloakRule.startSession();
+        try {
+            RealmModel testRealm = session.realms().getRealm("test");
+
+            // Assert users imported with correct LDAP_ID
+            FederationTestUtils.assertUserImported(session.users(), testRealm, "user1", "User1FN", "User1LN", "user1@email.org", "121");
+            FederationTestUtils.assertUserImported(session.users(), testRealm, "user2", "User2FN", "User2LN", "user2@email.org", "122");
+            UserModel user1 = session.users().getUserByUsername("user1", testRealm);
+            Assert.assertEquals("user1",  user1.getFirstAttribute(LDAPConstants.LDAP_ID));
+
+            // Revert config changes
+            UserFederationProviderModel providerModel = KeycloakModelUtils.findUserFederationProviderByDisplayName(ldapModel.getDisplayName(), testRealm);
+            providerModel.getConfig().put(LDAPConstants.UUID_LDAP_ATTRIBUTE, origUuidAttrName);
+            testRealm.updateUserFederationProvider(providerModel);
+        } finally {
+            keycloakRule.stopSession(session, true);
+        }
+    }
+
     @Test
     public void testPeriodicSync() {
         KeycloakSession session = keycloakRule.startSession();