Improve error messages when testing LDAP connection (#21013)
Closes #15434
This commit is contained in:
parent
0fc4d6120b
commit
91e543f415
6 changed files with 86 additions and 20 deletions
|
@ -71,11 +71,52 @@ public class LDAPServerCapabilitiesManager {
|
||||||
return new LDAPIdentityStore(session, ldapConfig).queryServerCapabilities();
|
return new LDAPIdentityStore(session, ldapConfig).queryServerCapabilities();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean testLDAP(TestLdapConnectionRepresentation config, KeycloakSession session, RealmModel realm) {
|
public static class InvalidBindDNException extends javax.naming.NamingException {
|
||||||
|
public InvalidBindDNException(String s) {
|
||||||
|
super(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getErrorCode(Throwable throwable) {
|
||||||
|
String errorMsg = "UnknownError";
|
||||||
|
if (throwable instanceof javax.naming.NamingException)
|
||||||
|
errorMsg = "NamingError";
|
||||||
|
if (throwable instanceof javax.naming.AuthenticationException)
|
||||||
|
errorMsg = "AuthenticationFailure";
|
||||||
|
if (throwable instanceof javax.naming.CommunicationException)
|
||||||
|
errorMsg = "CommunicationError";
|
||||||
|
if (throwable instanceof javax.naming.ServiceUnavailableException)
|
||||||
|
errorMsg = "ServiceUnavailable";
|
||||||
|
if (throwable instanceof javax.naming.InvalidNameException)
|
||||||
|
errorMsg = "InvalidName";
|
||||||
|
if (throwable instanceof javax.naming.ServiceUnavailableException)
|
||||||
|
errorMsg = "ServiceUnavailable";
|
||||||
|
if (throwable instanceof InvalidBindDNException)
|
||||||
|
errorMsg = "InvalidBindDN";
|
||||||
|
|
||||||
|
if (throwable instanceof javax.naming.NamingException) {
|
||||||
|
Throwable rootCause = ((javax.naming.NamingException)throwable).getRootCause();
|
||||||
|
if (rootCause instanceof java.net.MalformedURLException)
|
||||||
|
errorMsg = "MalformedURL";
|
||||||
|
if (rootCause instanceof java.net.NoRouteToHostException)
|
||||||
|
errorMsg = "NoRouteToHost";
|
||||||
|
if (rootCause instanceof java.net.ConnectException)
|
||||||
|
errorMsg = "ConnectionFailed";
|
||||||
|
if (rootCause instanceof java.net.UnknownHostException)
|
||||||
|
errorMsg = "UnknownHost";
|
||||||
|
if (rootCause instanceof javax.net.ssl.SSLHandshakeException)
|
||||||
|
errorMsg = "SSLHandshakeFailed";
|
||||||
|
if (rootCause instanceof java.net.SocketException)
|
||||||
|
errorMsg = "SocketReset";
|
||||||
|
}
|
||||||
|
return errorMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void testLDAP(TestLdapConnectionRepresentation config, KeycloakSession session, RealmModel realm) throws javax.naming.NamingException {
|
||||||
|
|
||||||
if (!TEST_CONNECTION.equals(config.getAction()) && !TEST_AUTHENTICATION.equals(config.getAction())) {
|
if (!TEST_CONNECTION.equals(config.getAction()) && !TEST_AUTHENTICATION.equals(config.getAction())) {
|
||||||
ServicesLogger.LOGGER.unknownAction(config.getAction());
|
ServicesLogger.LOGGER.unknownAction(config.getAction());
|
||||||
return false;
|
throw new javax.naming.NamingException("testLDAP unknown action");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TEST_AUTHENTICATION.equals(config.getAction())) {
|
if (TEST_AUTHENTICATION.equals(config.getAction())) {
|
||||||
|
@ -83,8 +124,7 @@ public class LDAPServerCapabilitiesManager {
|
||||||
// LDAPContextManager is responsible for correct order of addition of credentials to context in case
|
// LDAPContextManager is responsible for correct order of addition of credentials to context in case
|
||||||
// tls is true
|
// tls is true
|
||||||
if ((config.getBindDn() == null || config.getBindDn().isEmpty()) && LDAPConstants.AUTH_TYPE_SIMPLE.equals(config.getAuthType())) {
|
if ((config.getBindDn() == null || config.getBindDn().isEmpty()) && LDAPConstants.AUTH_TYPE_SIMPLE.equals(config.getAuthType())) {
|
||||||
logger.error("Unknown bind DN");
|
throw new InvalidBindDNException("Unknown bind DN");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// only test the connection.
|
// only test the connection.
|
||||||
|
@ -97,14 +137,11 @@ public class LDAPServerCapabilitiesManager {
|
||||||
// is not needed anymore
|
// is not needed anymore
|
||||||
try (LDAPContextManager ldapContextManager = LDAPContextManager.create(session, ldapConfig)) {
|
try (LDAPContextManager ldapContextManager = LDAPContextManager.create(session, ldapConfig)) {
|
||||||
ldapContextManager.getLdapContext();
|
ldapContextManager.getLdapContext();
|
||||||
|
|
||||||
// Connection was successful, no exception was raised returning true
|
|
||||||
return true;
|
|
||||||
} catch (Exception ne) {
|
} catch (Exception ne) {
|
||||||
String errorMessage = (TEST_AUTHENTICATION.equals(config.getAction())) ? "Error when authenticating to LDAP: "
|
String errorMessage = (TEST_AUTHENTICATION.equals(config.getAction())) ? "Error when authenticating to LDAP: "
|
||||||
: "Error when connecting to LDAP: ";
|
: "Error when connecting to LDAP: ";
|
||||||
ServicesLogger.LOGGER.errorAuthenticating(ne, errorMessage + ne.getMessage());
|
ServicesLogger.LOGGER.errorAuthenticating(ne, errorMessage + ne.getMessage());
|
||||||
return false;
|
throw ne;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,10 +72,13 @@ public class TestLdapConnectionResource {
|
||||||
|
|
||||||
TestLdapConnectionRepresentation config = new TestLdapConnectionRepresentation(action, connectionUrl, bindDn, bindCredential, useTruststoreSpi, connectionTimeout, startTls, LDAPConstants.AUTH_TYPE_SIMPLE);
|
TestLdapConnectionRepresentation config = new TestLdapConnectionRepresentation(action, connectionUrl, bindDn, bindCredential, useTruststoreSpi, connectionTimeout, startTls, LDAPConstants.AUTH_TYPE_SIMPLE);
|
||||||
config.setComponentId(componentId);
|
config.setComponentId(componentId);
|
||||||
if (! LDAPServerCapabilitiesManager.testLDAP(config, session, realm)) {
|
try {
|
||||||
throw ErrorResponse.error("LDAP test error", Response.Status.BAD_REQUEST);
|
LDAPServerCapabilitiesManager.testLDAP(config, session, realm);
|
||||||
}
|
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
|
} catch(Exception e) {
|
||||||
|
String errorMsg = LDAPServerCapabilitiesManager.getErrorCode(e);
|
||||||
|
throw ErrorResponse.error(errorMsg, Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,10 +89,13 @@ public class TestLdapConnectionResource {
|
||||||
@NoCache
|
@NoCache
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
public Response testLDAPConnection(TestLdapConnectionRepresentation config) {
|
public Response testLDAPConnection(TestLdapConnectionRepresentation config) {
|
||||||
if (! LDAPServerCapabilitiesManager.testLDAP(config, session, realm)) {
|
try {
|
||||||
throw ErrorResponse.error("LDAP test error", Response.Status.BAD_REQUEST);
|
LDAPServerCapabilitiesManager.testLDAP(config, session, realm);
|
||||||
}
|
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
|
} catch(Exception e) {
|
||||||
|
String errorMsg = LDAPServerCapabilitiesManager.getErrorCode(e);
|
||||||
|
throw ErrorResponse.error(errorMsg, Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,9 @@ public final class LDAPContextManager implements AutoCloseable {
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Could not negotiate TLS", e);
|
logger.error("Could not negotiate TLS", e);
|
||||||
throw new AuthenticationException("Could not negotiate TLS");
|
NamingException ne = new AuthenticationException("Could not negotiate TLS");
|
||||||
|
ne.setRootCause(e);
|
||||||
|
throw ne;
|
||||||
}
|
}
|
||||||
|
|
||||||
// throws AuthenticationException when authentication fails
|
// throws AuthenticationException when authentication fails
|
||||||
|
|
|
@ -93,8 +93,7 @@ const userImportingDisabledFailMessage =
|
||||||
"User federation provider could not be saved: Can not disable Importing users when LDAP provider mode is UNSYNCED";
|
"User federation provider could not be saved: Can not disable Importing users when LDAP provider mode is UNSYNCED";
|
||||||
|
|
||||||
const ldapTestSuccessMsg = "Successfully connected to LDAP";
|
const ldapTestSuccessMsg = "Successfully connected to LDAP";
|
||||||
const ldapTestFailMsg =
|
const ldapTestFailMsg = "Error when trying to connect to LDAP: 'SocketReset'";
|
||||||
"Error when trying to connect to LDAP. See server.log for details. LDAP test error";
|
|
||||||
|
|
||||||
describe("User Federation LDAP tests", () => {
|
describe("User Federation LDAP tests", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
"queryExtensions": "Query Supported Extensions",
|
"queryExtensions": "Query Supported Extensions",
|
||||||
"testAuthentication": "Test authentication",
|
"testAuthentication": "Test authentication",
|
||||||
"testSuccess": "Successfully connected to LDAP",
|
"testSuccess": "Successfully connected to LDAP",
|
||||||
"testError": "Error when trying to connect to LDAP. See server.log for details. {{error}}",
|
"testError": "Error when trying to connect to LDAP: '{{error}}'",
|
||||||
"learnMore": "Learn more",
|
"learnMore": "Learn more",
|
||||||
"managePriorities": "Manage priorities",
|
"managePriorities": "Manage priorities",
|
||||||
"managePriorityOrder": "Manage priority order",
|
"managePriorityOrder": "Manage priority order",
|
||||||
|
|
|
@ -26,6 +26,8 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.services.ServicesLogger;
|
import org.keycloak.services.ServicesLogger;
|
||||||
import org.keycloak.storage.managers.UserStorageSyncManager;
|
import org.keycloak.storage.managers.UserStorageSyncManager;
|
||||||
|
import org.keycloak.services.ErrorResponse;
|
||||||
|
import org.keycloak.services.managers.LDAPServerCapabilitiesManager;
|
||||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.storage.UserStorageProviderModel;
|
import org.keycloak.storage.UserStorageProviderModel;
|
||||||
|
@ -42,6 +44,7 @@ import jakarta.ws.rs.Produces;
|
||||||
import jakarta.ws.rs.QueryParam;
|
import jakarta.ws.rs.QueryParam;
|
||||||
import jakarta.ws.rs.core.HttpHeaders;
|
import jakarta.ws.rs.core.HttpHeaders;
|
||||||
import jakarta.ws.rs.core.MediaType;
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -74,6 +77,15 @@ public class UserStorageProviderResource {
|
||||||
this.headers = session.getContext().getRequestHeaders();
|
this.headers = session.getContext().getRequestHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getErrorCode(Throwable throwable) {
|
||||||
|
if (throwable instanceof org.keycloak.models.ModelException) {
|
||||||
|
if (throwable.getCause() != null) {
|
||||||
|
return getErrorCode(throwable.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LDAPServerCapabilitiesManager.getErrorCode(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Need this for admin console to display simple name of provider when displaying user detail
|
* Need this for admin console to display simple name of provider when displaying user detail
|
||||||
*
|
*
|
||||||
|
@ -138,9 +150,19 @@ public class UserStorageProviderResource {
|
||||||
UserStorageSyncManager syncManager = new UserStorageSyncManager();
|
UserStorageSyncManager syncManager = new UserStorageSyncManager();
|
||||||
SynchronizationResult syncResult;
|
SynchronizationResult syncResult;
|
||||||
if ("triggerFullSync".equals(action)) {
|
if ("triggerFullSync".equals(action)) {
|
||||||
|
try {
|
||||||
syncResult = syncManager.syncAllUsers(session.getKeycloakSessionFactory(), realm.getId(), providerModel);
|
syncResult = syncManager.syncAllUsers(session.getKeycloakSessionFactory(), realm.getId(), providerModel);
|
||||||
|
} catch(Exception e) {
|
||||||
|
String errorMsg = getErrorCode(e);
|
||||||
|
throw ErrorResponse.error(errorMsg, Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
} else if ("triggerChangedUsersSync".equals(action)) {
|
} else if ("triggerChangedUsersSync".equals(action)) {
|
||||||
|
try {
|
||||||
syncResult = syncManager.syncChangedUsers(session.getKeycloakSessionFactory(), realm.getId(), providerModel);
|
syncResult = syncManager.syncChangedUsers(session.getKeycloakSessionFactory(), realm.getId(), providerModel);
|
||||||
|
} catch(Exception e) {
|
||||||
|
String errorMsg = getErrorCode(e);
|
||||||
|
throw ErrorResponse.error(errorMsg, Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
} else if (action == null || action == "") {
|
} else if (action == null || action == "") {
|
||||||
logger.debug("Missing action");
|
logger.debug("Missing action");
|
||||||
throw new BadRequestException("Missing action");
|
throw new BadRequestException("Missing action");
|
||||||
|
|
Loading…
Reference in a new issue