Remove ldapsOnly (Java)

In `LDAPConstants.java`, the function to set the Truststore SPI system property was removed, as this is now handled by the `shouldUseTruststoreSpi` method in `LdapUtil`.

Closes: #9313
This commit is contained in:
Hynek Mlnarik 2023-06-16 16:22:58 +02:00 committed by Hynek Mlnařík
parent 59e1a5d992
commit c092c76ae8
12 changed files with 67 additions and 36 deletions

View file

@ -126,3 +126,12 @@ q=<name>:<value> <name>:<value> ...
```
Where `<name>` and `<value>` represent the attribute name and value, respectively.
= LDAPS-only Truststore option removed
LDAP option to use truststore SPI `Only for ldaps` has been removed. This parameter is used to
select truststore for TLS-secured LDAP connection: either internal Keycloak truststore is
picked (`Always`), or the global JVM one (`Never`).
Deployments where `Only for ldaps` was used will automatically behave as if `Always` option was
selected for TLS-secured LDAP connections.

View file

@ -78,7 +78,7 @@ When you configure a secure connection URL to your LDAP store (for example,`ldap
Configure the global truststore for {project_name} with the Truststore SPI. For more information about configuring the global truststore, see the https://www.keycloak.org/server/keycloak-truststore[Configuring a Truststore] {section}. If you do not configure the Truststore SPI, the truststore falls back to the default mechanism provided by Java, which can be the file supplied by the `javax.net.ssl.trustStore` system property or the cacerts file from the JDK if the system property is unset.
The `Use Truststore SPI` configuration property, in the LDAP federation provider configuration, controls the truststore SPI. By default, {project_name} sets the property to `Only for ldaps`, which is adequate for most deployments. {project_name} uses the Truststore SPI if the connection URL to LDAP starts with `ldaps` only.
The `Use Truststore SPI` configuration property, in the LDAP federation provider configuration, controls the truststore SPI. By default, {project_name} sets the property to `Always`, which is adequate for most deployments. {project_name} uses the Truststore SPI if the connection URL to LDAP starts with `ldaps` only.
==== Synchronizing LDAP users to {project_name}

View file

@ -25,7 +25,6 @@ import javax.naming.directory.SearchControls;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
@ -66,7 +65,12 @@ public class LDAPConfig {
}
public String getUseTruststoreSpi() {
return config.getFirst(LDAPConstants.USE_TRUSTSTORE_SPI);
String value = config.getFirst(LDAPConstants.USE_TRUSTSTORE_SPI);
if (LDAPConstants.USE_TRUSTSTORE_LDAPS_ONLY.equals(value)) {
value = LDAPConstants.USE_TRUSTSTORE_ALWAYS;
config.putSingle(LDAPConstants.USE_TRUSTSTORE_SPI, value);
}
return value;
}
public String getUsersDn() {

View file

@ -164,7 +164,7 @@ public class LDAPStorageProviderFactory implements UserStorageProviderFactory<LD
.add()
.property().name(LDAPConstants.USE_TRUSTSTORE_SPI)
.type(ProviderConfigProperty.STRING_TYPE)
.defaultValue("ldapsOnly")
.defaultValue("always")
.add()
.property().name(LDAPConstants.CONNECTION_POOLING)
.type(ProviderConfigProperty.BOOLEAN_TYPE)

View file

@ -80,8 +80,7 @@ public final class LDAPContextManager implements AutoCloseable {
ldapContext = new InitialLdapContext(connProp, null);
if (ldapConfig.isStartTls()) {
SSLSocketFactory sslSocketFactory = null;
String useTruststoreSpi = ldapConfig.getUseTruststoreSpi();
if (useTruststoreSpi != null && useTruststoreSpi.equals(LDAPConstants.USE_TRUSTSTORE_ALWAYS)) {
if (LDAPUtil.shouldUseTruststoreSpi(ldapConfig)) {
TruststoreProvider provider = session.getProvider(TruststoreProvider.class);
sslSocketFactory = provider.getSSLSocketFactory();
}
@ -191,9 +190,8 @@ public final class LDAPContextManager implements AutoCloseable {
// when using Start TLS, use default socket factory for LDAP client but pass the TrustStore SSL socket factory later
// when calling StartTlsResponse.negotiate(trustStoreSSLSocketFactory)
if (!ldapConfig.isStartTls()) {
String useTruststoreSpi = ldapConfig.getUseTruststoreSpi();
LDAPConstants.setTruststoreSpiIfNeeded(useTruststoreSpi, url, env);
if (LDAPUtil.shouldUseTruststoreSpi(ldapConfig)) {
env.put("java.naming.ldap.factory.socket", "org.keycloak.truststore.SSLSocketFactory");
}
String connectionPooling = ldapConfig.getConnectionPooling();

View file

@ -510,8 +510,7 @@ public class LDAPOperationManager {
authCtx = new InitialLdapContext(env, null);
if (config.isStartTls()) {
SSLSocketFactory sslSocketFactory = null;
String useTruststoreSpi = config.getUseTruststoreSpi();
if (useTruststoreSpi != null && useTruststoreSpi.equals(LDAPConstants.USE_TRUSTSTORE_ALWAYS)) {
if (LDAPUtil.shouldUseTruststoreSpi(config)) {
TruststoreProvider provider = session.getProvider(TruststoreProvider.class);
sslSocketFactory = provider.getSSLSocketFactory();
}

View file

@ -17,7 +17,9 @@
package org.keycloak.storage.ldap.idm.store.ldap;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ModelException;
import org.keycloak.storage.ldap.LDAPConfig;
import java.text.SimpleDateFormat;
import java.util.Date;
@ -260,5 +262,19 @@ public class LDAPUtil {
}
}
public static boolean shouldUseTruststoreSpi(LDAPConfig ldapConfig) {
boolean useSSL = ldapConfig.getConnectionUrl().toLowerCase().contains("ldaps://");
boolean defaultUseTruststore = useSSL || ldapConfig.isStartTls();
String useTruststoreSpi = ldapConfig.getUseTruststoreSpi();
if (useTruststoreSpi == null) {
return defaultUseTruststore;
}
if (LDAPConstants.USE_TRUSTSTORE_NEVER.equals(useTruststoreSpi)) {
return false;
}
return defaultUseTruststore;
}
}

View file

@ -33,6 +33,10 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-map</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi-private</artifactId>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>

View file

@ -96,8 +96,7 @@ public final class LdapMapContextManager implements AutoCloseable {
ldapContext = new InitialLdapContext(connProp, null);
if (ldapMapConfig.isStartTls()) {
SSLSocketFactory sslSocketFactory = null;
String useTruststoreSpi = ldapMapConfig.getUseTruststoreSpi();
if (useTruststoreSpi != null && useTruststoreSpi.equals(LDAPConstants.USE_TRUSTSTORE_ALWAYS)) {
if (LdapMapUtil.shouldUseTruststoreSpi(ldapMapConfig)) {
TruststoreProvider provider = session.getProvider(TruststoreProvider.class);
sslSocketFactory = provider.getSSLSocketFactory();
}
@ -204,9 +203,8 @@ public final class LdapMapContextManager implements AutoCloseable {
// when using Start TLS, use default socket factory for LDAP client but pass the TrustStore SSL socket factory later
// when calling StartTlsResponse.negotiate(trustStoreSSLSocketFactory)
if (!ldapMapConfig.isStartTls()) {
String useTruststoreSpi = ldapMapConfig.getUseTruststoreSpi();
LDAPConstants.setTruststoreSpiIfNeeded(useTruststoreSpi, url, env);
if (LdapMapUtil.shouldUseTruststoreSpi(ldapMapConfig)) {
env.put("java.naming.ldap.factory.socket", "org.keycloak.truststore.SSLSocketFactory");
}
String connectionPooling = ldapMapConfig.getConnectionPooling();

View file

@ -18,7 +18,9 @@
package org.keycloak.models.map.storage.ldap.store;
import org.jboss.logging.Logger;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ModelException;
import org.keycloak.models.map.storage.ldap.config.LdapMapConfig;
import java.text.SimpleDateFormat;
import java.util.Date;
@ -253,4 +255,19 @@ public class LdapMapUtil {
}
}
public static boolean shouldUseTruststoreSpi(LdapMapConfig ldapConfig) {
boolean useSSL = ldapConfig.getConnectionUrl().toLowerCase().contains("ldaps://");
boolean defaultUseTruststore = useSSL || ldapConfig.isStartTls();
String useTruststoreSpi = ldapConfig.getUseTruststoreSpi();
if (useTruststoreSpi == null) {
return defaultUseTruststore;
}
if (LDAPConstants.USE_TRUSTSTORE_NEVER.equals(useTruststoreSpi)) {
return false;
}
return defaultUseTruststore;
}
}

View file

@ -59,6 +59,11 @@ public class LDAPConstants {
public static final String USE_TRUSTSTORE_SPI = "useTruststoreSpi";
public static final String USE_TRUSTSTORE_ALWAYS = "always";
public static final String USE_TRUSTSTORE_NEVER = "never";
/**
* @deprecated Use {@link #USE_TRUSTSTORE_ALWAYS} instead.
*/
@Deprecated
public static final String USE_TRUSTSTORE_LDAPS_ONLY = "ldapsOnly";
public static final String SEARCH_SCOPE = "searchScope";
@ -157,25 +162,6 @@ public class LDAPConstants {
return ENTRY_UUID;
}
public static void setTruststoreSpiIfNeeded(String useTruststoreSpi, String url, Map<String, Object> env) {
boolean shouldSetTruststore;
if (useTruststoreSpi != null && useTruststoreSpi.equals(LDAPConstants.USE_TRUSTSTORE_ALWAYS)) {
shouldSetTruststore = true;
} else if (useTruststoreSpi != null && useTruststoreSpi.equals(LDAPConstants.USE_TRUSTSTORE_NEVER)) {
shouldSetTruststore = false;
} else {
shouldSetTruststore = toLdapUrls(url).stream()
.anyMatch(urlPart -> urlPart.toLowerCase().startsWith("ldaps"));
}
if (shouldSetTruststore) {
env.put("java.naming.ldap.factory.socket", "org.keycloak.truststore.SSLSocketFactory");
}
}
/**
* @see com.sun.jndi.ldap.LdapURL#fromList(String) (Not using it directly to avoid usage of internal Java classes)
*

View file

@ -249,7 +249,7 @@ public class LDAPRule extends ExternalResource {
// Default to startTLS disabled
config.put(LDAPConstants.START_TLS, "false");
// By default use truststore from TruststoreSPI only for LDAP over SSL connections
config.put(LDAPConstants.USE_TRUSTSTORE_SPI, LDAPConstants.USE_TRUSTSTORE_LDAPS_ONLY);
config.put(LDAPConstants.USE_TRUSTSTORE_SPI, LDAPConstants.USE_TRUSTSTORE_ALWAYS);
}
switch (defaultProperties.getProperty(LDAPEmbeddedServer.PROPERTY_SET_CONFIDENTIALITY_REQUIRED)) {
case "true":