KEYCLOAK-4175 Provide a way to set the connect and read timeout for ldap connections
This commit is contained in:
parent
be4f2aff63
commit
c32620b718
13 changed files with 85 additions and 14 deletions
|
@ -109,6 +109,14 @@ public class LDAPConfig {
|
|||
return config.getFirst(LDAPConstants.CONNECTION_POOLING);
|
||||
}
|
||||
|
||||
public String getConnectionTimeout() {
|
||||
return config.getFirst(LDAPConstants.CONNECTION_TIMEOUT);
|
||||
}
|
||||
|
||||
public String getReadTimeout() {
|
||||
return config.getFirst(LDAPConstants.READ_TIMEOUT);
|
||||
}
|
||||
|
||||
public Properties getAdditionalConnectionProperties() {
|
||||
// not supported for now
|
||||
return null;
|
||||
|
|
|
@ -145,6 +145,12 @@ public class LDAPStorageProviderFactory implements UserStorageProviderFactory<LD
|
|||
.type(ProviderConfigProperty.BOOLEAN_TYPE)
|
||||
.defaultValue("true")
|
||||
.add()
|
||||
.property().name(LDAPConstants.CONNECTION_TIMEOUT)
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.add()
|
||||
.property().name(LDAPConstants.READ_TIMEOUT)
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.add()
|
||||
.property().name(LDAPConstants.PAGINATION)
|
||||
.type(ProviderConfigProperty.BOOLEAN_TYPE)
|
||||
.defaultValue("true")
|
||||
|
@ -212,6 +218,25 @@ public class LDAPStorageProviderFactory implements UserStorageProviderFactory<LD
|
|||
LDAPConfig cfg = new LDAPConfig(config.getConfig());
|
||||
String customFilter = cfg.getCustomUserSearchFilter();
|
||||
LDAPUtils.validateCustomLdapFilter(customFilter);
|
||||
|
||||
String connectionTimeout = cfg.getConnectionTimeout();
|
||||
if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
|
||||
try {
|
||||
Long.parseLong(connectionTimeout);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new ComponentValidationException("ldapErrorConnectionTimeoutNotNumber");
|
||||
}
|
||||
}
|
||||
|
||||
String readTimeout = cfg.getReadTimeout();
|
||||
if (readTimeout != null && !readTimeout.isEmpty()) {
|
||||
try {
|
||||
Long.parseLong(readTimeout);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new ComponentValidationException("ldapErrorReadTimeoutNotNumber");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -507,6 +507,16 @@ public class LDAPOperationManager {
|
|||
env.put("com.sun.jndi.ldap.connect.pool", connectionPooling);
|
||||
}
|
||||
|
||||
String connectionTimeout = config.getConnectionTimeout();
|
||||
if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
|
||||
env.put("com.sun.jndi.ldap.connect.timeout", connectionTimeout);
|
||||
}
|
||||
|
||||
String readTimeout = config.getReadTimeout();
|
||||
if (readTimeout != null && !readTimeout.isEmpty()) {
|
||||
env.put("com.sun.jndi.ldap.read.timeout", readTimeout);
|
||||
}
|
||||
|
||||
// Just dump the additional properties
|
||||
Properties additionalProperties = this.config.getAdditionalConnectionProperties();
|
||||
if (additionalProperties != null) {
|
||||
|
|
|
@ -177,7 +177,7 @@ public interface RealmResource {
|
|||
@NoCache
|
||||
Response testLDAPConnection(@QueryParam("action") String action, @QueryParam("connectionUrl") String connectionUrl,
|
||||
@QueryParam("bindDn") String bindDn, @QueryParam("bindCredential") String bindCredential,
|
||||
@QueryParam("useTruststoreSpi") String useTruststoreSpi);
|
||||
@QueryParam("useTruststoreSpi") String useTruststoreSpi, @QueryParam("connectionTimeout") String connectionTimeout);
|
||||
|
||||
@Path("clear-realm-cache")
|
||||
@POST
|
||||
|
|
|
@ -57,6 +57,8 @@ public class LDAPConstants {
|
|||
|
||||
public static final String SEARCH_SCOPE = "searchScope";
|
||||
public static final String CONNECTION_POOLING = "connectionPooling";
|
||||
public static final String CONNECTION_TIMEOUT = "connectionTimeout";
|
||||
public static final String READ_TIMEOUT = "readTimeout";
|
||||
public static final String PAGINATION = "pagination";
|
||||
|
||||
public static final String EDIT_MODE = "editMode";
|
||||
|
|
|
@ -35,7 +35,7 @@ public class LDAPConnectionTestManager {
|
|||
public static final String TEST_CONNECTION = "testConnection";
|
||||
public static final String TEST_AUTHENTICATION = "testAuthentication";
|
||||
|
||||
public boolean testLDAP(String action, String connectionUrl, String bindDn, String bindCredential, String useTruststoreSpi) {
|
||||
public boolean testLDAP(String action, String connectionUrl, String bindDn, String bindCredential, String useTruststoreSpi, String connectionTimeout) {
|
||||
if (!TEST_CONNECTION.equals(action) && !TEST_AUTHENTICATION.equals(action)) {
|
||||
ServicesLogger.LOGGER.unknownAction(action);
|
||||
return false;
|
||||
|
@ -70,6 +70,10 @@ public class LDAPConnectionTestManager {
|
|||
|
||||
LDAPConstants.setTruststoreSpiIfNeeded(useTruststoreSpi, connectionUrl, env);
|
||||
|
||||
if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
|
||||
env.put("com.sun.jndi.ldap.connect.timeout", connectionTimeout);
|
||||
}
|
||||
|
||||
ldapContext = new InitialLdapContext(env, null);
|
||||
return true;
|
||||
} catch (Exception ne) {
|
||||
|
|
|
@ -750,14 +750,15 @@ public class RealmAdminResource {
|
|||
@NoCache
|
||||
public Response testLDAPConnection(@QueryParam("action") String action, @QueryParam("connectionUrl") String connectionUrl,
|
||||
@QueryParam("bindDn") String bindDn, @QueryParam("bindCredential") String bindCredential,
|
||||
@QueryParam("useTruststoreSpi") String useTruststoreSpi, @QueryParam("componentId") String componentId) {
|
||||
@QueryParam("useTruststoreSpi") String useTruststoreSpi, @QueryParam("connectionTimeout") String connectionTimeout,
|
||||
@QueryParam("componentId") String componentId) {
|
||||
auth.init(RealmAuth.Resource.REALM).requireManage();
|
||||
|
||||
if (componentId != null && bindCredential.equals(ComponentRepresentation.SECRET_VALUE)) {
|
||||
bindCredential = realm.getComponent(componentId).getConfig().getFirst(LDAPConstants.BIND_CREDENTIAL);
|
||||
}
|
||||
|
||||
boolean result = new LDAPConnectionTestManager().testLDAP(action, connectionUrl, bindDn, bindCredential, useTruststoreSpi);
|
||||
boolean result = new LDAPConnectionTestManager().testLDAP(action, connectionUrl, bindDn, bindCredential, useTruststoreSpi, connectionTimeout);
|
||||
return result ? Response.noContent().build() : ErrorResponse.error("LDAP test error", Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
|
|
|
@ -271,7 +271,7 @@ public class PermissionsTest extends AbstractKeycloakTest {
|
|||
|
||||
invoke(new InvocationWithResponse() {
|
||||
public void invoke(RealmResource realm, AtomicReference<Response> response) {
|
||||
response.set(realm.testLDAPConnection("nosuch", "nosuch", "nosuch", "nosuch", "nosuch"));
|
||||
response.set(realm.testLDAPConnection("nosuch", "nosuch", "nosuch", "nosuch", "nosuch", "nosuch"));
|
||||
}
|
||||
}, Resource.REALM, true);
|
||||
|
||||
|
|
|
@ -36,23 +36,23 @@ public class UserFederationLdapConnectionTest extends AbstractAdminTest {
|
|||
@Test
|
||||
public void testLdapConnections1() {
|
||||
// Unknown action
|
||||
Response response = realm.testLDAPConnection("unknown", "ldap://localhost:10389", "foo", "bar", "false");
|
||||
Response response = realm.testLDAPConnection("unknown", "ldap://localhost:10389", "foo", "bar", "false", null);
|
||||
assertStatus(response, 400);
|
||||
|
||||
// Bad host
|
||||
response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_CONNECTION, "ldap://localhostt:10389", "foo", "bar", "false");
|
||||
response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_CONNECTION, "ldap://localhostt:10389", "foo", "bar", "false", null);
|
||||
assertStatus(response, 400);
|
||||
|
||||
// Connection success
|
||||
response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_CONNECTION, "ldap://localhost:10389", "foo", "bar", "false");
|
||||
response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_CONNECTION, "ldap://localhost:10389", "foo", "bar", "false", null);
|
||||
assertStatus(response, 204);
|
||||
|
||||
// Bad authentication
|
||||
response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_AUTHENTICATION, "ldap://localhost:10389", "foo", "bar", "false");
|
||||
response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_AUTHENTICATION, "ldap://localhost:10389", "foo", "bar", "false", "10000");
|
||||
assertStatus(response, 400);
|
||||
|
||||
// Authentication success
|
||||
response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_AUTHENTICATION, "ldap://localhost:10389", "uid=admin,ou=system", "secret", "false");
|
||||
response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_AUTHENTICATION, "ldap://localhost:10389", "uid=admin,ou=system", "secret", "false", null);
|
||||
assertStatus(response, 204);
|
||||
|
||||
}
|
||||
|
@ -60,16 +60,16 @@ public class UserFederationLdapConnectionTest extends AbstractAdminTest {
|
|||
@Test
|
||||
public void testLdapConnectionsSsl() {
|
||||
|
||||
Response response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_CONNECTION, "ldaps://localhost:10636", "foo", "bar", "false");
|
||||
Response response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_CONNECTION, "ldaps://localhost:10636", "foo", "bar", "false", null);
|
||||
assertStatus(response, 204);
|
||||
|
||||
response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_CONNECTION, "ldaps://localhostt:10636", "foo", "bar", "false");
|
||||
response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_CONNECTION, "ldaps://localhostt:10636", "foo", "bar", "false", null);
|
||||
assertStatus(response, 400);
|
||||
|
||||
response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_AUTHENTICATION, "ldaps://localhost:10636", "foo", "bar", "false");
|
||||
response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_AUTHENTICATION, "ldaps://localhost:10636", "foo", "bar", "false", null);
|
||||
assertStatus(response, 400);
|
||||
|
||||
response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_AUTHENTICATION, "ldaps://localhost:10636", "uid=admin,ou=system", "secret", "true");
|
||||
response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_AUTHENTICATION, "ldaps://localhost:10636", "uid=admin,ou=system", "secret", "true", null);
|
||||
assertStatus(response, 204);
|
||||
}
|
||||
|
||||
|
|
|
@ -744,6 +744,10 @@ ldap.search-scope.tooltip=For one level, we search for users just in DNs specifi
|
|||
use-truststore-spi=Use Truststore SPI
|
||||
ldap.use-truststore-spi.tooltip=Specifies whether LDAP connection will use the truststore SPI with the truststore configured in standalone.xml/domain.xml. 'Always' means that it will always use it. 'Never' means that it won't use it. 'Only for ldaps' means that it will use if your connection URL use ldaps. Note even if standalone.xml/domain.xml is not configured, the default Java cacerts or certificate specified by 'javax.net.ssl.trustStore' property will be used.
|
||||
connection-pooling=Connection Pooling
|
||||
ldap-connection-timeout=Connection Timeout
|
||||
ldap.connection-timeout.tooltip=LDAP Connection Timeout in milliseconds
|
||||
ldap-read-timeout=Read Timeout
|
||||
ldap.read-timeout.tooltip=LDAP Read Timeout in milliseconds. This timeout applies for LDAP read operations
|
||||
ldap.connection-pooling.tooltip=Does Keycloak should use connection pooling for accessing LDAP server
|
||||
ldap.pagination.tooltip=Does the LDAP server support pagination.
|
||||
kerberos-integration=Kerberos Integration
|
||||
|
|
|
@ -8,6 +8,8 @@ invalidPasswordRegexPatternMessage=Invalid password: fails to match regex patter
|
|||
invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords.
|
||||
|
||||
ldapErrorInvalidCustomFilter=Custom configured LDAP filter does not start with "(" or does not end with ")".
|
||||
ldapErrorConnectionTimeoutNotNumber=Connection Timeout must be a number
|
||||
ldapErrorReadTimeoutNotNumber=Read Timeout must be a number
|
||||
ldapErrorMissingClientId=Client ID needs to be provided in config when Realm Roles Mapping is not used.
|
||||
ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType=Not possible to preserve group inheritance and use UID membership type together.
|
||||
ldapErrorCantWriteOnlyForReadOnlyLdap=Can't set write only when LDAP provider mode is not WRITABLE
|
||||
|
|
|
@ -1262,6 +1262,7 @@ module.controller('LDAPUserStorageCtrl', function($scope, $location, Notificatio
|
|||
bindDn: ldapConfig.bindDn,
|
||||
bindCredential: ldapConfig.bindCredential,
|
||||
useTruststoreSpi: ldapConfig.useTruststoreSpi,
|
||||
connectionTimeout: ldapConfig.connectionTimeout,
|
||||
componentId: instance.id
|
||||
};
|
||||
};
|
||||
|
|
|
@ -186,6 +186,20 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'ldap.connection-pooling.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-md-2 control-label" for="connectionTimeout">{{:: 'ldap-connection-timeout' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<input class="form-control" id="connectionTimeout" type="text" ng-model="instance.config['connectionTimeout'][0]" placeholder="{{:: 'ldap-connection-timeout' | translate}}">
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'ldap.connection-timeout.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-md-2 control-label" for="readTimeout">{{:: 'ldap-read-timeout' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<input class="form-control" id="readTimeout" type="text" ng-model="instance.config['readTimeout'][0]" placeholder="{{:: 'ldap-read-timeout' | translate}}">
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'ldap.read-timeout.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-md-2 control-label" for="pagination">{{:: 'pagination' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
|
|
Loading…
Reference in a new issue