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 18eb96b4b8..49dba42a79 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 @@ -27,6 +27,7 @@ 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.ModelValidationException; import org.keycloak.models.RealmModel; import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.representations.idm.TestLdapConnectionRepresentation; @@ -73,7 +74,7 @@ public class LDAPServerCapabilitiesManager { 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())) + if (checkLdapConnectionUrl(config, ldapConfig) && config.getBindDn() != null && config.getBindDn().equalsIgnoreCase(ldapConfig.getBindDN())) { bindCredential = ldapConfig.getBindCredential(); } @@ -94,6 +95,28 @@ public class LDAPServerCapabilitiesManager { return new LDAPConfig(configMap); } + /** + * Ensure provided connection URI matches parsed LDAP connection URI. + * + * See: https://docs.oracle.com/javase/jndi/tutorial/ldap/misc/url.html + * @param config + * @param ldapConfig + * @return + */ + private static boolean checkLdapConnectionUrl(TestLdapConnectionRepresentation config, LDAPConfig ldapConfig) { + // There could be multiple connection URIs separated via spaces. + String[] configConnectionUrls = config.getConnectionUrl().trim().split(" "); + String[] ldapConfigConnectionUrls = ldapConfig.getConnectionUrl().trim().split(" "); + if (configConnectionUrls.length != ldapConfigConnectionUrls.length) { + return false; + } + boolean urlsMatch = true; + for (int i = 0; i < configConnectionUrls.length && urlsMatch; i++) { + urlsMatch = Objects.equals(URI.create(configConnectionUrls[i]), URI.create(ldapConfigConnectionUrls[i])); + } + return urlsMatch; + } + public static Set queryServerCapabilities(TestLdapConnectionRepresentation config, KeycloakSession session, RealmModel realm) { 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 ecc4382fd3..b372fa7b92 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 @@ -131,6 +131,7 @@ public class UserFederationLdapConnectionTest extends AbstractAdminTest { @Test public void testLdapConnectionMoreServers() { + // Both servers work Response response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION, "ldap://localhost:10389 ldaps://localhost:10636", "uid=admin,ou=system", "secret", "true", null)); assertStatus(response, 204); @@ -155,11 +156,23 @@ public class UserFederationLdapConnectionTest extends AbstractAdminTest { response = realm.testLDAPConnection(new TestLdapConnectionRepresentation(LDAPServerCapabilitiesManager.TEST_AUTHENTICATION, "ldap://localhostt:10389 ldaps://localhostt:10636", "uid=admin,ou=system", "secret", "true", null)); assertStatus(response, 400); + // create LDAP component model using ldap + Map cfg = ldapRule.getConfig(); + cfg.put(LDAPConstants.CONNECTION_URL, "ldap://invalid:10389 ldap://localhost:10389"); + cfg.put(LDAPConstants.CONNECTION_TIMEOUT, "1000"); + String ldapModelId = testingClient.testing().ldap(REALM_NAME).createLDAPProvider(cfg, false); + + // Only 2nd server works with stored LDAP federation provider + 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 public void testLdapConnectionComponentAlreadyCreated() { - // create ldap componnet model using ldaps + // create ldap component model using ldaps Map cfg = ldapRule.getConfig(); cfg.put(LDAPConstants.CONNECTION_URL, "ldaps://localhost:10636"); cfg.put(LDAPConstants.START_TLS, "false");