diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java
index d75812f506..e6f52d3b79 100755
--- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java
+++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java
@@ -101,7 +101,6 @@ import org.keycloak.userprofile.UserProfileMetadata;
import org.keycloak.userprofile.UserProfileUtil;
import org.keycloak.utils.StreamsUtil;
-
/**
* @author Marek Posolda
* @author Bill Burke
diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java
index e49be2eb80..1d7b32a29e 100755
--- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java
@@ -54,6 +54,7 @@ import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapper;
import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapperFactory;
import org.keycloak.storage.ldap.mappers.HardcodedLDAPAttributeMapper;
import org.keycloak.storage.ldap.mappers.HardcodedLDAPAttributeMapperFactory;
+import org.keycloak.storage.ldap.mappers.KerberosPrincipalAttributeMapperFactory;
import org.keycloak.storage.ldap.mappers.LDAPConfigDecorator;
import org.keycloak.storage.ldap.mappers.LDAPMappersComparator;
import org.keycloak.storage.ldap.mappers.LDAPStorageMapper;
@@ -451,6 +452,11 @@ public class LDAPStorageProviderFactory implements UserStorageProviderFactory {
+ LDAPTestContext ctx = LDAPTestContext.init(session);
+ RealmModel testRealm = ctx.getRealm();
+
+ ctx.getLdapModel().getConfig().putSingle(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.WRITABLE.toString());
+ UserStorageSyncManager usersSyncManager = new UserStorageSyncManager();
+
+ renameUserInLDAP(ctx, testRealm, "hnelson", "hnelson2", "hnelson2@keycloak.org", "hnelson2@KEYCLOAK.ORG", "secret2");
+
+ // Assert still old users in local provider
+ LDAPTestAsserts.assertUserImported(UserStoragePrivateUtil.userLocalStorage(session), testRealm, "hnelson", "Horatio", "Nelson", "hnelson@keycloak.org", null);
+
+ // Trigger sync
+ KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
+ SynchronizationResult syncResult = usersSyncManager.syncAllUsers(sessionFactory, testRealm.getId(), ctx.getLdapModel());
+ Assert.assertEquals(0, syncResult.getFailed());
+ });
+
+ testingClient.server().run(session -> {
+ LDAPTestContext ctx = LDAPTestContext.init(session);
+ RealmModel testRealm = ctx.getRealm();
+ UserProvider userProvider = UserStoragePrivateUtil.userLocalStorage(session);
+ // Assert users updated in local provider
+ LDAPTestAsserts.assertUserImported(session.users(), testRealm, "hnelson2", "Horatio", "Nelson", "hnelson2@keycloak.org", null);
+ UserModel updatedLocalUser = userProvider.getUserByUsername(testRealm, "hnelson2");
+ LDAPObject ldapUser = ctx.getLdapProvider().loadLDAPUserByUsername(testRealm, "hnelson2");
+ Assert.assertNull(userProvider.getUserByUsername(testRealm, "hnelson"));
+ // Assert UUID didn't change
+ Assert.assertEquals(updatedLocalUser.getAttributeStream(LDAPConstants.LDAP_ID).findFirst().get(), ldapUser.getUuid());
+ // Assert Kerberos principal was changed in Keycloak
+ Assert.assertEquals(updatedLocalUser.getAttributeStream(KERBEROS_PRINCIPAL).findFirst().get(), ldapUser.getAttributeAsString(ctx.getLdapProvider().getKerberosConfig().getKerberosPrincipalAttribute()));
+ });
+
+ // login not possible with old user
+ loginPage.open();
+ loginPage.login("hnelson", "secret2");
+ Assert.assertEquals("Invalid username or password.", loginPage.getInputError());
+
+ // login after update successful
+ assertSuccessfulSpnegoLogin("hnelson2", "hnelson2", "secret2");
+ } finally {
+ // revert changes in LDAP
+ testingClient.server().run(session -> {
+ LDAPTestContext ctx = LDAPTestContext.init(session);
+ RealmModel testRealm = ctx.getRealm();
+
+ renameUserInLDAP(ctx, testRealm, "hnelson2", "hnelson", "hnelson@keycloak.org", "hnelson@KEYCLOAK.ORG", "secret");
+ });
+ }
+ }
+
+ private static void renameUserInLDAP(LDAPTestContext ctx, RealmModel testRealm, String username, String newUsername, String newEmail, String newKr5Principal, String secret) {
+ // Update user in LDAP, change username, email, krb5Principal
+ LDAPObject ldapUser = ctx.getLdapProvider().loadLDAPUserByUsername(testRealm, username);
+
+ if (ldapUser != null) {
+ ldapUser.removeReadOnlyAttributeName("uid");
+ ldapUser.removeReadOnlyAttributeName("mail");
+ ldapUser.removeReadOnlyAttributeName(ctx.getLdapProvider().getKerberosConfig().getKerberosPrincipalAttribute());
+ String userNameLdapAttributeName = ctx.getLdapProvider().getLdapIdentityStore().getConfig().getUsernameLdapAttribute();
+ ldapUser.setSingleAttribute(userNameLdapAttributeName, newUsername);
+ ldapUser.setSingleAttribute(LDAPConstants.EMAIL, newEmail);
+ ldapUser.setSingleAttribute(ctx.getLdapProvider().getKerberosConfig().getKerberosPrincipalAttribute(), newKr5Principal);
+ ctx.getLdapProvider().getLdapIdentityStore().update(ldapUser);
+
+ // update also password in LDAP to force propagation into KDC
+ LDAPTestUtils.updateLDAPPassword(ctx.getLdapProvider(), ldapUser, secret);
+ }
+ }
+
@Test
public void validatePasswordPolicyTest() throws Exception{
updateProviderEditMode(UserStorageProvider.EditMode.WRITABLE);