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 4d1f6ccb15..f975da54b7 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 @@ -790,6 +790,10 @@ public class LDAPStorageProvider implements UserStorageProvider, } else { // Use Naming LDAP API LDAPObject ldapUser = loadAndValidateUser(realm, user); + if (ldapUser == null) { + // user was removed from ldap - password verification must fail. + return false; + } try { ldapIdentityStore.validatePassword(ldapUser, password); @@ -821,6 +825,10 @@ public class LDAPStorageProvider implements UserStorageProvider, LDAPIdentityStore ldapIdentityStore = getLdapIdentityStore(); String password = input.getChallengeResponse(); LDAPObject ldapUser = loadAndValidateUser(realm, user); + if (ldapUser == null) { + logger.warnf("User '%s' can't be updated in LDAP as it doesn't exist there", user.getUsername()); + return false; + } if (ldapIdentityStore.getConfig().isValidatePasswordPolicy()) { PolicyError error = session.getProvider(PasswordPolicyManagerProvider.class).validate(realm, user, password); if (error != null) throw new ModelException(error.getMessage(), error.getParameters()); @@ -967,7 +975,8 @@ public class LDAPStorageProvider implements UserStorageProvider, return null; } else { LDAPObject ldapObject = loadAndValidateUser(realm, user); - if (kerberosPrincipalAttrName != null && !kerberosPrincipal.toString().equalsIgnoreCase(ldapObject.getAttributeAsString(kerberosPrincipalAttrName))) { + if (kerberosPrincipalAttrName != null && ldapObject != null && + !kerberosPrincipal.toString().equalsIgnoreCase(ldapObject.getAttributeAsString(kerberosPrincipalAttrName))) { logger.warnf("User with username [%s] aready exists and is linked to provider [%s] but is not valid. Authenticated kerberos principal is [%s], but LDAP user has different kerberos principal [%s]", user.getUsername(), model.getName(), kerberosPrincipal, ldapObject.getAttributeAsString(kerberosPrincipalAttrName)); ldapObject = null; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPUserLoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPUserLoginTest.java index 0dff5afdd1..29b6518aca 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPUserLoginTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPUserLoginTest.java @@ -324,4 +324,31 @@ public class LDAPUserLoginTest extends AbstractLDAPTest { verifyConnectionUrlProtocolPrefix("ldap://"); runLDAPLoginTest(); } + + // Check that login fails as expected when an LDAP user that has already authenticated is removed from LDAP and attempts to authenticate again. + // See https://github.com/keycloak/keycloak/issues/28523 + @Test + @LDAPConnectionParameters(bindType=LDAPConnectionParameters.BindType.SIMPLE, encryption=LDAPConnectionParameters.Encryption.NONE) + public void loginLDAPUserAuthenticationSimpleDeleteLDAPUser() { + // create another user for this test. + getTestingClient().server().run(session -> { + LDAPTestContext ctx = LDAPTestContext.init(session); + RealmModel appRealm = ctx.getRealm(); + LDAPObject jane = LDAPTestUtils.addLDAPUser(ctx.getLdapProvider(), appRealm, "janedoe", "Jane", + "Doe", "janedoe@keycloak.org", "2nd Avenue", "09283"); + LDAPTestUtils.updateLDAPPassword(ctx.getLdapProvider(), jane, DEFAULT_TEST_USERS.get("VALID_USER_PASSWORD")); + }); + // login with the new user, then logout - user is now cached in Keycloak. + this.verifyLoginSucceededAndLogout("janedoe", DEFAULT_TEST_USERS.get("VALID_USER_PASSWORD")); + + // now remove the user directly in LDAP. + getTestingClient().server().run(session -> { + LDAPTestContext ctx = LDAPTestContext.init(session); + RealmModel appRealm = ctx.getRealm(); + LDAPTestUtils.removeLDAPUserByUsername(ctx.getLdapProvider(), appRealm, ctx.getLdapProvider().getLdapIdentityStore().getConfig(), "janedoe"); + }); + + // attempt to login again with the deleted user should fail with the proper message. + this.verifyLoginFailed("janedoe", DEFAULT_TEST_USERS.get("VALID_USER_PASSWORD")); + } }