Improvements for ldap test authentication

Closes #30434

Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
rmartinc 2024-06-14 12:12:48 +02:00 committed by Marek Posolda
parent 5c0dddd837
commit c51640546d
3 changed files with 72 additions and 3 deletions

View file

@ -16,10 +16,15 @@ public class TestLdapConnectionRepresentation {
} }
public TestLdapConnectionRepresentation(String action, String connectionUrl, String bindDn, String bindCredential, String useTruststoreSpi, String connectionTimeout) { 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) { 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.action = action;
this.connectionUrl = connectionUrl; this.connectionUrl = connectionUrl;
this.bindDn = bindDn; this.bindDn = bindDn;
@ -28,6 +33,7 @@ public class TestLdapConnectionRepresentation {
this.connectionTimeout = connectionTimeout; this.connectionTimeout = connectionTimeout;
this.startTls = startTls; this.startTls = startTls;
this.authType = authType; this.authType = authType;
this.componentId = componentId;
} }
public String getAction() { public String getAction() {

View file

@ -16,12 +16,15 @@
*/ */
package org.keycloak.services.managers; package org.keycloak.services.managers;
import java.net.URI;
import java.util.Collections; import java.util.Collections;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import javax.naming.ldap.LdapContext; import javax.naming.ldap.LdapContext;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.LDAPConstants; import org.keycloak.models.LDAPConstants;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
@ -30,6 +33,7 @@ import org.keycloak.representations.idm.TestLdapConnectionRepresentation;
import org.keycloak.services.ServicesLogger; import org.keycloak.services.ServicesLogger;
import org.keycloak.storage.ldap.LDAPConfig; import org.keycloak.storage.ldap.LDAPConfig;
import org.keycloak.representations.idm.LDAPCapabilityRepresentation; 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.LDAPContextManager;
import org.keycloak.storage.ldap.idm.store.ldap.LDAPIdentityStore; import org.keycloak.storage.ldap.idm.store.ldap.LDAPIdentityStore;
import org.keycloak.storage.ldap.mappers.membership.group.GroupTreeResolver; import org.keycloak.storage.ldap.mappers.membership.group.GroupTreeResolver;
@ -63,8 +67,17 @@ public class LDAPServerCapabilitiesManager {
public static LDAPConfig buildLDAPConfig(TestLdapConnectionRepresentation config, RealmModel realm) { public static LDAPConfig buildLDAPConfig(TestLdapConnectionRepresentation config, RealmModel realm) {
String bindCredential = config.getBindCredential(); String bindCredential = config.getBindCredential();
if (config.getComponentId() != null && ComponentRepresentation.SECRET_VALUE.equals(bindCredential)) { if (config.getComponentId() != null && !LDAPConstants.AUTH_TYPE.equals(LDAPConstants.AUTH_TYPE_NONE)
bindCredential = realm.getComponent(config.getComponentId()).getConfig().getFirst(LDAPConstants.BIND_CREDENTIAL); && 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<>(); MultivaluedHashMap<String, String> configMap = new MultivaluedHashMap<>();
configMap.putSingle(LDAPConstants.AUTH_TYPE, config.getAuthType()); configMap.putSingle(LDAPConstants.AUTH_TYPE, config.getAuthType());

View file

@ -18,11 +18,13 @@
package org.keycloak.testsuite.admin; package org.keycloak.testsuite.admin;
import java.util.List; import java.util.List;
import java.util.Map;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.ClassRule; import org.junit.ClassRule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.models.LDAPConstants; import org.keycloak.models.LDAPConstants;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.LDAPCapabilityRepresentation; import org.keycloak.representations.idm.LDAPCapabilityRepresentation;
import org.keycloak.representations.idm.TestLdapConnectionRepresentation; import org.keycloak.representations.idm.TestLdapConnectionRepresentation;
import org.keycloak.services.managers.LDAPServerCapabilitiesManager; 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<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 @Test
public void testLdapCapabilities() { public void testLdapCapabilities() {