diff --git a/core/src/main/java/org/keycloak/representations/idm/TestLdapConnectionRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/TestLdapConnectionRepresentation.java index fbf75feedb..715ac6f659 100644 --- a/core/src/main/java/org/keycloak/representations/idm/TestLdapConnectionRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/TestLdapConnectionRepresentation.java @@ -16,10 +16,15 @@ public class 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 class TestLdapConnectionRepresentation { this.connectionTimeout = connectionTimeout; this.startTls = startTls; this.authType = authType; + this.componentId = componentId; } public String getAction() { diff --git a/federation/ldap/src/main/java/org/keycloak/services/managers/LDAPServerCapabilitiesManager.java b/federation/ldap/src/main/java/org/keycloak/services/managers/LDAPServerCapabilitiesManager.java index 224e1eaaff..e43da43876 100755 --- a/federation/ldap/src/main/java/org/keycloak/services/managers/LDAPServerCapabilitiesManager.java +++ b/federation/ldap/src/main/java/org/keycloak/services/managers/LDAPServerCapabilitiesManager.java @@ -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.representations.idm.TestLdapConnectionRepresentation; 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 @@ 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 configMap = new MultivaluedHashMap<>(); configMap.putSingle(LDAPConstants.AUTH_TYPE, config.getAuthType()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationLdapConnectionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationLdapConnectionTest.java index 07a69e8b13..37f9462523 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationLdapConnectionTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationLdapConnectionTest.java @@ -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 class UserFederationLdapConnectionTest extends AbstractAdminTest { } + @Test + public void testLdapConnectionComponentAlreadyCreated() { + // create ldap componnet model using ldaps + Map 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() {