VYPR
Low severity2.7NVD Advisory· Published Jun 18, 2024· Updated Apr 15, 2026

CVE-2024-5967

CVE-2024-5967

Description

A vulnerability was found in Keycloak. The LDAP testing endpoint allows changing the Connection URL  independently without re-entering the currently configured LDAP bind credentials. This flaw allows an attacker with admin access (permission manage-realm) to change the LDAP host URL ("Connection URL") to a machine they control. The Keycloak server will connect to the attacker's host and try to authenticate with the configured credentials, thus leaking them to the attacker. As a consequence, an attacker who has compromised the admin console or compromised a user with sufficient privileges can leak domain credentials and attack the domain.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.keycloak:keycloak-ldap-federationMaven
>= 25.0.0, < 25.0.125.0.1
org.keycloak:keycloak-ldap-federationMaven
< 22.0.1222.0.12
org.keycloak:keycloak-ldap-federationMaven
>= 23.0.0, < 24.0.624.0.6

Patches

3
0d0530046b9c

Improvements for ldap test authentication

https://github.com/keycloak/keycloakrmartincJun 14, 2024via ghsa
3 files changed · +72 3
  • core/src/main/java/org/keycloak/representations/idm/TestLdapConnectionRepresentation.java+7 1 modified
    @@ -16,10 +16,15 @@ public TestLdapConnectionRepresentation() {
         }
     
         public TestLdapConnectionRepresentation(String action, String connectionUrl, String bindDn, String bindCredential, String useTruststoreSpi, String connectionTimeout) {
    -        this(action, connectionUrl, bindDn, bindCredential, useTruststoreSpi, connectionTimeout, null, null);
    +        this(action, connectionUrl, bindDn, bindCredential, useTruststoreSpi, connectionTimeout, null, null, null);
         }
     
         public TestLdapConnectionRepresentation(String action, String connectionUrl, String bindDn, String bindCredential, String useTruststoreSpi, String connectionTimeout, String startTls, String authType) {
    +        this(action, connectionUrl, bindDn, bindCredential, useTruststoreSpi, connectionTimeout, startTls, authType, null);
    +    }
    +
    +    public TestLdapConnectionRepresentation(String action, String connectionUrl, String bindDn, String bindCredential,
    +            String useTruststoreSpi, String connectionTimeout, String startTls, String authType, String componentId) {
             this.action = action;
             this.connectionUrl = connectionUrl;
             this.bindDn = bindDn;
    @@ -28,6 +33,7 @@ public TestLdapConnectionRepresentation(String action, String connectionUrl, Str
             this.connectionTimeout = connectionTimeout;
             this.startTls = startTls;
             this.authType = authType;
    +        this.componentId = componentId;
         }
     
         public String getAction() {
    
  • federation/ldap/src/main/java/org/keycloak/services/managers/LDAPServerCapabilitiesManager.java+15 2 modified
    @@ -16,12 +16,15 @@
      */
     package org.keycloak.services.managers;
     
    +import java.net.URI;
     import java.util.Collections;
    +import java.util.Objects;
     import java.util.Set;
     import javax.naming.ldap.LdapContext;
     
     import org.jboss.logging.Logger;
     import org.keycloak.common.util.MultivaluedHashMap;
    +import org.keycloak.component.ComponentModel;
     import org.keycloak.models.KeycloakSession;
     import org.keycloak.models.LDAPConstants;
     import org.keycloak.models.RealmModel;
    @@ -30,6 +33,7 @@
     import org.keycloak.services.ServicesLogger;
     import org.keycloak.storage.ldap.LDAPConfig;
     import org.keycloak.representations.idm.LDAPCapabilityRepresentation;
    +import org.keycloak.storage.ldap.idm.model.LDAPDn;
     import org.keycloak.storage.ldap.idm.store.ldap.LDAPContextManager;
     import org.keycloak.storage.ldap.idm.store.ldap.LDAPIdentityStore;
     import org.keycloak.utils.StringUtil;
    @@ -62,8 +66,17 @@ private static int parseConnectionTimeout(String connectionTimeout) {
     
         public static LDAPConfig buildLDAPConfig(TestLdapConnectionRepresentation config, RealmModel realm) {
             String bindCredential = config.getBindCredential();
    -        if (config.getComponentId() != null && ComponentRepresentation.SECRET_VALUE.equals(bindCredential)) {
    -            bindCredential = realm.getComponent(config.getComponentId()).getConfig().getFirst(LDAPConstants.BIND_CREDENTIAL);
    +        if (config.getComponentId() != null && !LDAPConstants.AUTH_TYPE.equals(LDAPConstants.AUTH_TYPE_NONE)
    +                && ComponentRepresentation.SECRET_VALUE.equals(bindCredential)) {
    +            // check the connection URL and the bind DN are the same to allow using the same configured password
    +            ComponentModel component = realm.getComponent(config.getComponentId());
    +            if (component != null) {
    +                LDAPConfig ldapConfig = new LDAPConfig(component.getConfig());
    +                if (Objects.equals(URI.create(config.getConnectionUrl()), URI.create(ldapConfig.getConnectionUrl()))
    +                        && Objects.equals(LDAPDn.fromString(config.getBindDn()), LDAPDn.fromString(ldapConfig.getBindDN()))) {
    +                    bindCredential = ldapConfig.getBindCredential();
    +                }
    +            }
             }
             MultivaluedHashMap<String, String> configMap = new MultivaluedHashMap<>();
             configMap.putSingle(LDAPConstants.AUTH_TYPE, config.getAuthType());
    
  • testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationLdapConnectionTest.java+50 0 modified
    @@ -18,11 +18,13 @@
     package org.keycloak.testsuite.admin;
     
     import java.util.List;
    +import java.util.Map;
     
     import org.hamcrest.Matchers;
     import org.junit.ClassRule;
     import org.junit.Test;
     import org.keycloak.models.LDAPConstants;
    +import org.keycloak.representations.idm.ComponentRepresentation;
     import org.keycloak.representations.idm.LDAPCapabilityRepresentation;
     import org.keycloak.representations.idm.TestLdapConnectionRepresentation;
     import org.keycloak.services.managers.LDAPServerCapabilitiesManager;
    @@ -147,6 +149,54 @@ public void testLdapConnectionMoreServers() {
     
         }
     
    +    @Test
    +    public void testLdapConnectionComponentAlreadyCreated() {
    +        // create ldap componnet model using ldaps
    +        Map<String, String> cfg = ldapRule.getConfig();
    +        cfg.put(LDAPConstants.CONNECTION_URL, "ldaps://localhost:10636");
    +        cfg.put(LDAPConstants.START_TLS, "false");
    +        cfg.put(LDAPConstants.USE_TRUSTSTORE_SPI, "true");
    +        String ldapModelId = testingClient.testing().ldap(REALM_NAME).createLDAPProvider(cfg, false);
    +        try {
    +            // test passing everything with password included
    +            Response response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION,
    +                    cfg.get(LDAPConstants.CONNECTION_URL), cfg.get(LDAPConstants.BIND_DN), cfg.get(LDAPConstants.BIND_CREDENTIAL),
    +                    cfg.get(LDAPConstants.USE_TRUSTSTORE_SPI), cfg.get(LDAPConstants.CONNECTION_TIMEOUT),
    +                    cfg.get(LDAPConstants.START_TLS), cfg.get(LDAPConstants.AUTH_TYPE), ldapModelId));
    +            assertStatus(response, 204);
    +
    +            // test passing the secret but not changing anything
    +            response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION,
    +                    cfg.get(LDAPConstants.CONNECTION_URL), cfg.get(LDAPConstants.BIND_DN), ComponentRepresentation.SECRET_VALUE,
    +                    cfg.get(LDAPConstants.USE_TRUSTSTORE_SPI), cfg.get(LDAPConstants.CONNECTION_TIMEOUT),
    +                    cfg.get(LDAPConstants.START_TLS), cfg.get(LDAPConstants.AUTH_TYPE), ldapModelId));
    +            assertStatus(response, 204);
    +
    +            // test passing the secret and changing the connection timeout which is allowed
    +            response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION,
    +                    cfg.get(LDAPConstants.CONNECTION_URL), cfg.get(LDAPConstants.BIND_DN), ComponentRepresentation.SECRET_VALUE,
    +                    cfg.get(LDAPConstants.USE_TRUSTSTORE_SPI), "1000",
    +                    cfg.get(LDAPConstants.START_TLS), cfg.get(LDAPConstants.AUTH_TYPE), ldapModelId));
    +            assertStatus(response, 204);
    +
    +            // test passing the secret but modifying the connection URL to plain ldap (different URL)
    +            response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION,
    +                    "ldap://localhost:10389", cfg.get(LDAPConstants.BIND_DN), ComponentRepresentation.SECRET_VALUE,
    +                    cfg.get(LDAPConstants.USE_TRUSTSTORE_SPI), cfg.get(LDAPConstants.CONNECTION_TIMEOUT),
    +                    cfg.get(LDAPConstants.START_TLS), cfg.get(LDAPConstants.AUTH_TYPE), ldapModelId));
    +            assertStatus(response, 400);
    +
    +            // test passing the secret but modifying the user DN
    +            response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION,
    +                    cfg.get(LDAPConstants.CONNECTION_URL), "uid=anotheradmin,ou=people,dc=keycloak,dc=org", ComponentRepresentation.SECRET_VALUE,
    +                    cfg.get(LDAPConstants.USE_TRUSTSTORE_SPI), cfg.get(LDAPConstants.CONNECTION_TIMEOUT),
    +                    cfg.get(LDAPConstants.START_TLS), cfg.get(LDAPConstants.AUTH_TYPE), ldapModelId));
    +            assertStatus(response, 400);
    +        } finally {
    +            adminClient.realm(REALM_NAME).components().removeComponent(ldapModelId);
    +        }
    +    }
    +
         @Test
         public void testLdapCapabilities() {
     
    
1f56a9e48bf9

Improvements for ldap test authentication

https://github.com/keycloak/keycloakrmartincJun 14, 2024via ghsa
3 files changed · +72 3
  • core/src/main/java/org/keycloak/representations/idm/TestLdapConnectionRepresentation.java+7 1 modified
    @@ -16,10 +16,15 @@ public TestLdapConnectionRepresentation() {
         }
     
         public TestLdapConnectionRepresentation(String action, String connectionUrl, String bindDn, String bindCredential, String useTruststoreSpi, String connectionTimeout) {
    -        this(action, connectionUrl, bindDn, bindCredential, useTruststoreSpi, connectionTimeout, null, null);
    +        this(action, connectionUrl, bindDn, bindCredential, useTruststoreSpi, connectionTimeout, null, null, null);
         }
     
         public TestLdapConnectionRepresentation(String action, String connectionUrl, String bindDn, String bindCredential, String useTruststoreSpi, String connectionTimeout, String startTls, String authType) {
    +        this(action, connectionUrl, bindDn, bindCredential, useTruststoreSpi, connectionTimeout, startTls, authType, null);
    +    }
    +
    +    public TestLdapConnectionRepresentation(String action, String connectionUrl, String bindDn, String bindCredential,
    +            String useTruststoreSpi, String connectionTimeout, String startTls, String authType, String componentId) {
             this.action = action;
             this.connectionUrl = connectionUrl;
             this.bindDn = bindDn;
    @@ -28,6 +33,7 @@ public TestLdapConnectionRepresentation(String action, String connectionUrl, Str
             this.connectionTimeout = connectionTimeout;
             this.startTls = startTls;
             this.authType = authType;
    +        this.componentId = componentId;
         }
     
         public String getAction() {
    
  • federation/ldap/src/main/java/org/keycloak/services/managers/LDAPServerCapabilitiesManager.java+15 2 modified
    @@ -16,12 +16,15 @@
      */
     package org.keycloak.services.managers;
     
    +import java.net.URI;
     import java.util.Collections;
    +import java.util.Objects;
     import java.util.Set;
     import javax.naming.ldap.LdapContext;
     
     import org.jboss.logging.Logger;
     import org.keycloak.common.util.MultivaluedHashMap;
    +import org.keycloak.component.ComponentModel;
     import org.keycloak.models.KeycloakSession;
     import org.keycloak.models.LDAPConstants;
     import org.keycloak.models.RealmModel;
    @@ -30,6 +33,7 @@
     import org.keycloak.services.ServicesLogger;
     import org.keycloak.storage.ldap.LDAPConfig;
     import org.keycloak.representations.idm.LDAPCapabilityRepresentation;
    +import org.keycloak.storage.ldap.idm.model.LDAPDn;
     import org.keycloak.storage.ldap.idm.store.ldap.LDAPContextManager;
     import org.keycloak.storage.ldap.idm.store.ldap.LDAPIdentityStore;
     import org.keycloak.storage.ldap.mappers.membership.group.GroupTreeResolver;
    @@ -63,8 +67,17 @@ private static int parseConnectionTimeout(String connectionTimeout) {
     
         public static LDAPConfig buildLDAPConfig(TestLdapConnectionRepresentation config, RealmModel realm) {
             String bindCredential = config.getBindCredential();
    -        if (config.getComponentId() != null && ComponentRepresentation.SECRET_VALUE.equals(bindCredential)) {
    -            bindCredential = realm.getComponent(config.getComponentId()).getConfig().getFirst(LDAPConstants.BIND_CREDENTIAL);
    +        if (config.getComponentId() != null && !LDAPConstants.AUTH_TYPE.equals(LDAPConstants.AUTH_TYPE_NONE)
    +                && ComponentRepresentation.SECRET_VALUE.equals(bindCredential)) {
    +            // check the connection URL and the bind DN are the same to allow using the same configured password
    +            ComponentModel component = realm.getComponent(config.getComponentId());
    +            if (component != null) {
    +                LDAPConfig ldapConfig = new LDAPConfig(component.getConfig());
    +                if (Objects.equals(URI.create(config.getConnectionUrl()), URI.create(ldapConfig.getConnectionUrl()))
    +                        && Objects.equals(LDAPDn.fromString(config.getBindDn()), LDAPDn.fromString(ldapConfig.getBindDN()))) {
    +                    bindCredential = ldapConfig.getBindCredential();
    +                }
    +            }
             }
             MultivaluedHashMap<String, String> configMap = new MultivaluedHashMap<>();
             configMap.putSingle(LDAPConstants.AUTH_TYPE, config.getAuthType());
    
  • testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationLdapConnectionTest.java+50 0 modified
    @@ -18,11 +18,13 @@
     package org.keycloak.testsuite.admin;
     
     import java.util.List;
    +import java.util.Map;
     
     import org.hamcrest.Matchers;
     import org.junit.ClassRule;
     import org.junit.Test;
     import org.keycloak.models.LDAPConstants;
    +import org.keycloak.representations.idm.ComponentRepresentation;
     import org.keycloak.representations.idm.LDAPCapabilityRepresentation;
     import org.keycloak.representations.idm.TestLdapConnectionRepresentation;
     import org.keycloak.services.managers.LDAPServerCapabilitiesManager;
    @@ -147,6 +149,54 @@ public void testLdapConnectionMoreServers() {
     
         }
     
    +    @Test
    +    public void testLdapConnectionComponentAlreadyCreated() {
    +        // create ldap componnet model using ldaps
    +        Map<String, String> cfg = ldapRule.getConfig();
    +        cfg.put(LDAPConstants.CONNECTION_URL, "ldaps://localhost:10636");
    +        cfg.put(LDAPConstants.START_TLS, "false");
    +        cfg.put(LDAPConstants.USE_TRUSTSTORE_SPI, "true");
    +        String ldapModelId = testingClient.testing().ldap(REALM_NAME).createLDAPProvider(cfg, false);
    +        try {
    +            // test passing everything with password included
    +            Response response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION,
    +                    cfg.get(LDAPConstants.CONNECTION_URL), cfg.get(LDAPConstants.BIND_DN), cfg.get(LDAPConstants.BIND_CREDENTIAL),
    +                    cfg.get(LDAPConstants.USE_TRUSTSTORE_SPI), cfg.get(LDAPConstants.CONNECTION_TIMEOUT),
    +                    cfg.get(LDAPConstants.START_TLS), cfg.get(LDAPConstants.AUTH_TYPE), ldapModelId));
    +            assertStatus(response, 204);
    +
    +            // test passing the secret but not changing anything
    +            response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION,
    +                    cfg.get(LDAPConstants.CONNECTION_URL), cfg.get(LDAPConstants.BIND_DN), ComponentRepresentation.SECRET_VALUE,
    +                    cfg.get(LDAPConstants.USE_TRUSTSTORE_SPI), cfg.get(LDAPConstants.CONNECTION_TIMEOUT),
    +                    cfg.get(LDAPConstants.START_TLS), cfg.get(LDAPConstants.AUTH_TYPE), ldapModelId));
    +            assertStatus(response, 204);
    +
    +            // test passing the secret and changing the connection timeout which is allowed
    +            response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION,
    +                    cfg.get(LDAPConstants.CONNECTION_URL), cfg.get(LDAPConstants.BIND_DN), ComponentRepresentation.SECRET_VALUE,
    +                    cfg.get(LDAPConstants.USE_TRUSTSTORE_SPI), "1000",
    +                    cfg.get(LDAPConstants.START_TLS), cfg.get(LDAPConstants.AUTH_TYPE), ldapModelId));
    +            assertStatus(response, 204);
    +
    +            // test passing the secret but modifying the connection URL to plain ldap (different URL)
    +            response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION,
    +                    "ldap://localhost:10389", cfg.get(LDAPConstants.BIND_DN), ComponentRepresentation.SECRET_VALUE,
    +                    cfg.get(LDAPConstants.USE_TRUSTSTORE_SPI), cfg.get(LDAPConstants.CONNECTION_TIMEOUT),
    +                    cfg.get(LDAPConstants.START_TLS), cfg.get(LDAPConstants.AUTH_TYPE), ldapModelId));
    +            assertStatus(response, 400);
    +
    +            // test passing the secret but modifying the user DN
    +            response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION,
    +                    cfg.get(LDAPConstants.CONNECTION_URL), "uid=anotheradmin,ou=people,dc=keycloak,dc=org", ComponentRepresentation.SECRET_VALUE,
    +                    cfg.get(LDAPConstants.USE_TRUSTSTORE_SPI), cfg.get(LDAPConstants.CONNECTION_TIMEOUT),
    +                    cfg.get(LDAPConstants.START_TLS), cfg.get(LDAPConstants.AUTH_TYPE), ldapModelId));
    +            assertStatus(response, 400);
    +        } finally {
    +            adminClient.realm(REALM_NAME).components().removeComponent(ldapModelId);
    +        }
    +    }
    +
         @Test
         public void testLdapCapabilities() {
     
    
bde8568d4174

Improvements for ldap test authentication

https://github.com/keycloak/keycloakrmartincJun 14, 2024via ghsa
3 files changed · +72 3
  • core/src/main/java/org/keycloak/representations/idm/TestLdapConnectionRepresentation.java+7 1 modified
    @@ -16,10 +16,15 @@ public TestLdapConnectionRepresentation() {
         }
     
         public TestLdapConnectionRepresentation(String action, String connectionUrl, String bindDn, String bindCredential, String useTruststoreSpi, String connectionTimeout) {
    -        this(action, connectionUrl, bindDn, bindCredential, useTruststoreSpi, connectionTimeout, null, null);
    +        this(action, connectionUrl, bindDn, bindCredential, useTruststoreSpi, connectionTimeout, null, null, null);
         }
     
         public TestLdapConnectionRepresentation(String action, String connectionUrl, String bindDn, String bindCredential, String useTruststoreSpi, String connectionTimeout, String startTls, String authType) {
    +        this(action, connectionUrl, bindDn, bindCredential, useTruststoreSpi, connectionTimeout, startTls, authType, null);
    +    }
    +
    +    public TestLdapConnectionRepresentation(String action, String connectionUrl, String bindDn, String bindCredential,
    +            String useTruststoreSpi, String connectionTimeout, String startTls, String authType, String componentId) {
             this.action = action;
             this.connectionUrl = connectionUrl;
             this.bindDn = bindDn;
    @@ -28,6 +33,7 @@ public TestLdapConnectionRepresentation(String action, String connectionUrl, Str
             this.connectionTimeout = connectionTimeout;
             this.startTls = startTls;
             this.authType = authType;
    +        this.componentId = componentId;
         }
     
         public String getAction() {
    
  • federation/ldap/src/main/java/org/keycloak/services/managers/LDAPServerCapabilitiesManager.java+15 2 modified
    @@ -16,11 +16,14 @@
      */
     package org.keycloak.services.managers;
     
    +import java.net.URI;
     import java.util.Collections;
    +import java.util.Objects;
     import java.util.Set;
     
     import org.jboss.logging.Logger;
     import org.keycloak.common.util.MultivaluedHashMap;
    +import org.keycloak.component.ComponentModel;
     import org.keycloak.models.KeycloakSession;
     import org.keycloak.models.LDAPConstants;
     import org.keycloak.models.RealmModel;
    @@ -29,6 +32,7 @@
     import org.keycloak.services.ServicesLogger;
     import org.keycloak.storage.ldap.LDAPConfig;
     import org.keycloak.representations.idm.LDAPCapabilityRepresentation;
    +import org.keycloak.storage.ldap.idm.model.LDAPDn;
     import org.keycloak.storage.ldap.idm.store.ldap.LDAPContextManager;
     import org.keycloak.storage.ldap.idm.store.ldap.LDAPIdentityStore;
     
    @@ -45,8 +49,17 @@ public class LDAPServerCapabilitiesManager {
     
         public static LDAPConfig buildLDAPConfig(TestLdapConnectionRepresentation config, RealmModel realm) {
             String bindCredential = config.getBindCredential();
    -        if (config.getComponentId() != null && ComponentRepresentation.SECRET_VALUE.equals(bindCredential)) {
    -            bindCredential = realm.getComponent(config.getComponentId()).getConfig().getFirst(LDAPConstants.BIND_CREDENTIAL);
    +        if (config.getComponentId() != null && !LDAPConstants.AUTH_TYPE.equals(LDAPConstants.AUTH_TYPE_NONE)
    +                && ComponentRepresentation.SECRET_VALUE.equals(bindCredential)) {
    +            // check the connection URL and the bind DN are the same to allow using the same configured password
    +            ComponentModel component = realm.getComponent(config.getComponentId());
    +            if (component != null) {
    +                LDAPConfig ldapConfig = new LDAPConfig(component.getConfig());
    +                if (Objects.equals(URI.create(config.getConnectionUrl()), URI.create(ldapConfig.getConnectionUrl()))
    +                        && Objects.equals(LDAPDn.fromString(config.getBindDn()), LDAPDn.fromString(ldapConfig.getBindDN()))) {
    +                    bindCredential = ldapConfig.getBindCredential();
    +                }
    +            }
             }
             MultivaluedHashMap<String, String> configMap = new MultivaluedHashMap<>();
             configMap.putSingle(LDAPConstants.AUTH_TYPE, config.getAuthType());
    
  • testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationLdapConnectionTest.java+50 0 modified
    @@ -18,11 +18,13 @@
     package org.keycloak.testsuite.admin;
     
     import java.util.List;
    +import java.util.Map;
     
     import org.hamcrest.Matchers;
     import org.junit.ClassRule;
     import org.junit.Test;
     import org.keycloak.models.LDAPConstants;
    +import org.keycloak.representations.idm.ComponentRepresentation;
     import org.keycloak.representations.idm.LDAPCapabilityRepresentation;
     import org.keycloak.representations.idm.TestLdapConnectionRepresentation;
     import org.keycloak.services.managers.LDAPServerCapabilitiesManager;
    @@ -136,6 +138,54 @@ public void testLdapConnectionMoreServers() {
     
         }
     
    +    @Test
    +    public void testLdapConnectionComponentAlreadyCreated() {
    +        // create ldap componnet model using ldaps
    +        Map<String, String> cfg = ldapRule.getConfig();
    +        cfg.put(LDAPConstants.CONNECTION_URL, "ldaps://localhost:10636");
    +        cfg.put(LDAPConstants.START_TLS, "false");
    +        cfg.put(LDAPConstants.USE_TRUSTSTORE_SPI, "true");
    +        String ldapModelId = testingClient.testing().ldap(REALM_NAME).createLDAPProvider(cfg, false);
    +        try {
    +            // test passing everything with password included
    +            Response response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION,
    +                    cfg.get(LDAPConstants.CONNECTION_URL), cfg.get(LDAPConstants.BIND_DN), cfg.get(LDAPConstants.BIND_CREDENTIAL),
    +                    cfg.get(LDAPConstants.USE_TRUSTSTORE_SPI), cfg.get(LDAPConstants.CONNECTION_TIMEOUT),
    +                    cfg.get(LDAPConstants.START_TLS), cfg.get(LDAPConstants.AUTH_TYPE), ldapModelId));
    +            assertStatus(response, 204);
    +
    +            // test passing the secret but not changing anything
    +            response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION,
    +                    cfg.get(LDAPConstants.CONNECTION_URL), cfg.get(LDAPConstants.BIND_DN), ComponentRepresentation.SECRET_VALUE,
    +                    cfg.get(LDAPConstants.USE_TRUSTSTORE_SPI), cfg.get(LDAPConstants.CONNECTION_TIMEOUT),
    +                    cfg.get(LDAPConstants.START_TLS), cfg.get(LDAPConstants.AUTH_TYPE), ldapModelId));
    +            assertStatus(response, 204);
    +
    +            // test passing the secret and changing the connection timeout which is allowed
    +            response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION,
    +                    cfg.get(LDAPConstants.CONNECTION_URL), cfg.get(LDAPConstants.BIND_DN), ComponentRepresentation.SECRET_VALUE,
    +                    cfg.get(LDAPConstants.USE_TRUSTSTORE_SPI), "1000",
    +                    cfg.get(LDAPConstants.START_TLS), cfg.get(LDAPConstants.AUTH_TYPE), ldapModelId));
    +            assertStatus(response, 204);
    +
    +            // test passing the secret but modifying the connection URL to plain ldap (different URL)
    +            response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION,
    +                    "ldap://localhost:10389", cfg.get(LDAPConstants.BIND_DN), ComponentRepresentation.SECRET_VALUE,
    +                    cfg.get(LDAPConstants.USE_TRUSTSTORE_SPI), cfg.get(LDAPConstants.CONNECTION_TIMEOUT),
    +                    cfg.get(LDAPConstants.START_TLS), cfg.get(LDAPConstants.AUTH_TYPE), ldapModelId));
    +            assertStatus(response, 400);
    +
    +            // test passing the secret but modifying the user DN
    +            response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION,
    +                    cfg.get(LDAPConstants.CONNECTION_URL), "uid=anotheradmin,ou=people,dc=keycloak,dc=org", ComponentRepresentation.SECRET_VALUE,
    +                    cfg.get(LDAPConstants.USE_TRUSTSTORE_SPI), cfg.get(LDAPConstants.CONNECTION_TIMEOUT),
    +                    cfg.get(LDAPConstants.START_TLS), cfg.get(LDAPConstants.AUTH_TYPE), ldapModelId));
    +            assertStatus(response, 400);
    +        } finally {
    +            adminClient.realm(REALM_NAME).components().removeComponent(ldapModelId);
    +        }
    +    }
    +
         @Test
         public void testLdapCapabilities() {
     
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

16

News mentions

0

No linked articles in our index yet.