Polish SslOptions usage

Add helper method and tighten usage so that exceptions are thrown when
options cannot be applied.

See gh-34814
pull/35180/head
Phillip Webb 2 years ago
parent 423c60acfa
commit b5c9e7c06a

@ -58,6 +58,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -158,7 +159,8 @@ public class CassandraAutoConfiguration {
private void configureSsl(CqlSessionBuilder builder, SslBundle sslBundle) { private void configureSsl(CqlSessionBuilder builder, SslBundle sslBundle) {
SslOptions options = sslBundle.getOptions(); SslOptions options = sslBundle.getOptions();
String[] ciphers = (options.getCiphers() != null) ? options.getCiphers().toArray(String[]::new) : null; Assert.state(options.getEnabledProtocols() == null, "SSL protocol options cannot be specified with Cassandra");
String[] ciphers = SslOptions.toArray(options.getCiphers());
builder.withSslEngineFactory(new ProgrammaticSslEngineFactory(sslBundle.createSslContext(), ciphers)); builder.withSslEngineFactory(new ProgrammaticSslEngineFactory(sslBundle.createSslContext(), ciphers));
} }

@ -41,6 +41,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration.CouchbaseCondition; import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration.CouchbaseCondition;
import org.springframework.boot.autoconfigure.couchbase.CouchbaseProperties.Ssl;
import org.springframework.boot.autoconfigure.couchbase.CouchbaseProperties.Timeouts; import org.springframework.boot.autoconfigure.couchbase.CouchbaseProperties.Timeouts;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -50,7 +51,9 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils; import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for Couchbase. * {@link EnableAutoConfiguration Auto-configuration} for Couchbase.
@ -125,10 +128,14 @@ public class CouchbaseAutoConfiguration {
} }
private void configureSsl(Builder builder, SslBundles sslBundles) { private void configureSsl(Builder builder, SslBundles sslBundles) {
Ssl sslProperties = this.properties.getEnv().getSsl();
SslBundle sslBundle = (StringUtils.hasText(sslProperties.getBundle()))
? sslBundles.getBundle(sslProperties.getBundle()) : null;
Assert.state(sslBundle == null || !sslBundle.getOptions().isSpecified(),
"SSL Options cannot be specified with Couchbase");
builder.securityConfig((config) -> { builder.securityConfig((config) -> {
config.enableTls(true); config.enableTls(true);
TrustManagerFactory trustManagerFactory = getTrustManagerFactory(this.properties.getEnv().getSsl(), TrustManagerFactory trustManagerFactory = getTrustManagerFactory(sslProperties, sslBundle);
sslBundles);
if (trustManagerFactory != null) { if (trustManagerFactory != null) {
config.trustManagerFactory(trustManagerFactory); config.trustManagerFactory(trustManagerFactory);
} }
@ -136,15 +143,11 @@ public class CouchbaseAutoConfiguration {
} }
@SuppressWarnings("removal") @SuppressWarnings("removal")
private TrustManagerFactory getTrustManagerFactory(CouchbaseProperties.Ssl ssl, SslBundles sslBundles) { private TrustManagerFactory getTrustManagerFactory(CouchbaseProperties.Ssl sslProperties, SslBundle sslBundle) {
if (ssl.getKeyStore() != null) { if (sslProperties.getKeyStore() != null) {
return loadTrustManagerFactory(ssl); return loadTrustManagerFactory(sslProperties);
} }
if (ssl.getBundle() != null) { return (sslBundle != null) ? sslBundle.getManagers().getTrustManagerFactory() : null;
SslBundle bundle = sslBundles.getBundle(ssl.getBundle());
return bundle.getManagers().getTrustManagerFactory();
}
return null;
} }
@SuppressWarnings("removal") @SuppressWarnings("removal")

@ -19,7 +19,6 @@ package org.springframework.boot.autoconfigure.elasticsearch;
import java.net.URI; import java.net.URI;
import java.time.Duration; import java.time.Duration;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HostnameVerifier;
@ -117,16 +116,12 @@ class ElasticsearchRestClientConfigurations {
private void configureSsl(HttpAsyncClientBuilder httpClientBuilder, SslBundle sslBundle) { private void configureSsl(HttpAsyncClientBuilder httpClientBuilder, SslBundle sslBundle) {
SSLContext sslcontext = sslBundle.createSslContext(); SSLContext sslcontext = sslBundle.createSslContext();
SslOptions sslOptions = sslBundle.getOptions(); SslOptions sslOptions = sslBundle.getOptions();
String[] enabledProtocols = toArray(sslOptions.getEnabledProtocols()); String[] enabledProtocols = SslOptions.toArray(sslOptions.getEnabledProtocols());
String[] ciphers = toArray(sslOptions.getCiphers()); String[] ciphers = SslOptions.toArray(sslOptions.getCiphers());
httpClientBuilder.setSSLStrategy( httpClientBuilder.setSSLStrategy(
new SSLIOSessionStrategy(sslcontext, enabledProtocols, ciphers, (HostnameVerifier) null)); new SSLIOSessionStrategy(sslcontext, enabledProtocols, ciphers, (HostnameVerifier) null));
} }
private static String[] toArray(Set<String> set) {
return (set != null) ? set.toArray(String[]::new) : null;
}
} }
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)

@ -24,6 +24,7 @@ import org.bson.UuidRepresentation;
import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.SslBundles; import org.springframework.boot.ssl.SslBundles;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.util.Assert;
/** /**
* A {@link MongoClientSettingsBuilderCustomizer} that applies standard settings to a * A {@link MongoClientSettingsBuilderCustomizer} that applies standard settings to a
@ -67,6 +68,7 @@ public class StandardMongoClientSettingsBuilderCustomizer implements MongoClient
settings.enabled(true); settings.enabled(true);
if (this.ssl.getBundle() != null) { if (this.ssl.getBundle() != null) {
SslBundle sslBundle = this.sslBundles.getBundle(this.ssl.getBundle()); SslBundle sslBundle = this.sslBundles.getBundle(this.ssl.getBundle());
Assert.state(!sslBundle.getOptions().isSpecified(), "SSL options cannot be specified with MongoDB");
settings.context(sslBundle.createSslContext()); settings.context(sslBundle.createSslContext());
} }
} }

@ -52,10 +52,10 @@ class HttpComponentsClientHttpConnectorFactory
@Override @Override
public TlsDetails verify(NamedEndpoint endpoint, SSLEngine sslEngine) throws SSLException { public TlsDetails verify(NamedEndpoint endpoint, SSLEngine sslEngine) throws SSLException {
if (options.getCiphers() != null) { if (options.getCiphers() != null) {
sslEngine.setEnabledCipherSuites(options.getCiphers().toArray(String[]::new)); sslEngine.setEnabledCipherSuites(SslOptions.toArray(options.getCiphers()));
} }
if (options.getEnabledProtocols() != null) { if (options.getEnabledProtocols() != null) {
sslEngine.setEnabledProtocols(options.getEnabledProtocols().toArray(String[]::new)); sslEngine.setEnabledProtocols(SslOptions.toArray(options.getEnabledProtocols()));
} }
return null; return null;
} }

@ -18,14 +18,13 @@ package org.springframework.boot.autoconfigure.web.reactive.function.client;
import java.net.http.HttpClient; import java.net.http.HttpClient;
import java.net.http.HttpClient.Builder; import java.net.http.HttpClient.Builder;
import java.util.Set;
import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLParameters;
import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.SslOptions;
import org.springframework.http.client.reactive.JdkClientHttpConnector; import org.springframework.http.client.reactive.JdkClientHttpConnector;
import org.springframework.http.client.reactive.JettyClientHttpConnector; import org.springframework.http.client.reactive.JettyClientHttpConnector;
import org.springframework.util.CollectionUtils;
/** /**
* {@link ClientHttpConnectorFactory} for {@link JettyClientHttpConnector}. * {@link ClientHttpConnectorFactory} for {@link JettyClientHttpConnector}.
@ -38,17 +37,14 @@ class JdkClientHttpConnectorFactory implements ClientHttpConnectorFactory<JdkCli
public JdkClientHttpConnector createClientHttpConnector(SslBundle sslBundle) { public JdkClientHttpConnector createClientHttpConnector(SslBundle sslBundle) {
Builder builder = HttpClient.newBuilder(); Builder builder = HttpClient.newBuilder();
if (sslBundle != null) { if (sslBundle != null) {
SslOptions options = sslBundle.getOptions();
builder.sslContext(sslBundle.createSslContext()); builder.sslContext(sslBundle.createSslContext());
SSLParameters parameters = new SSLParameters(); SSLParameters parameters = new SSLParameters();
parameters.setCipherSuites(asArray(sslBundle.getOptions().getCiphers())); parameters.setCipherSuites(SslOptions.toArray(options.getCiphers()));
parameters.setProtocols(asArray(sslBundle.getOptions().getEnabledProtocols())); parameters.setProtocols(SslOptions.toArray(options.getEnabledProtocols()));
builder.sslParameters(parameters); builder.sslParameters(parameters);
} }
return new JdkClientHttpConnector(builder.build()); return new JdkClientHttpConnector(builder.build());
} }
private String[] asArray(Set<String> set) {
return (CollectionUtils.isEmpty(set)) ? null : set.toArray(String[]::new);
}
} }

@ -46,11 +46,11 @@ class JettyClientHttpConnectorFactory implements ClientHttpConnectorFactory<Jett
if (sslBundle != null) { if (sslBundle != null) {
SslOptions options = sslBundle.getOptions(); SslOptions options = sslBundle.getOptions();
if (options.getCiphers() != null) { if (options.getCiphers() != null) {
sslContextFactory.setIncludeCipherSuites(options.getCiphers().toArray(String[]::new)); sslContextFactory.setIncludeCipherSuites(SslOptions.toArray(options.getCiphers()));
sslContextFactory.setExcludeCipherSuites(); sslContextFactory.setExcludeCipherSuites();
} }
if (options.getEnabledProtocols() != null) { if (options.getEnabledProtocols() != null) {
sslContextFactory.setIncludeProtocols(options.getEnabledProtocols().toArray(String[]::new)); sslContextFactory.setIncludeProtocols(SslOptions.toArray(options.getEnabledProtocols()));
sslContextFactory.setExcludeProtocols(); sslContextFactory.setExcludeProtocols();
} }
sslContextFactory.setSslContext(sslBundle.createSslContext()); sslContextFactory.setSslContext(sslBundle.createSslContext());

@ -17,6 +17,7 @@
package org.springframework.boot.ssl; package org.springframework.boot.ssl;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Set; import java.util.Set;
@ -37,6 +38,14 @@ public interface SslOptions {
*/ */
SslOptions NONE = of((Set<String>) null, (Set<String>) null); SslOptions NONE = of((Set<String>) null, (Set<String>) null);
/**
* Return if any SSL options have been specified.
* @return {@true} if SSL options have been specified
*/
default boolean isSpecified() {
return (getCiphers() != null) && (getEnabledProtocols() != null);
}
/** /**
* Return the ciphers that can be used or an empty set. The cipher names in this set * Return the ciphers that can be used or an empty set. The cipher names in this set
* should be compatible with those supported by * should be compatible with those supported by
@ -86,6 +95,16 @@ public interface SslOptions {
} }
/**
* Helper method that provides a null-safe way to convert a {@link Collection} to a
* {@code String[]} for client libraries to use.
* @param collection the collection to convert
* @return a string array or {@code null}
*/
static String[] toArray(Collection<String> collection) {
return (collection != null) ? collection.toArray(String[]::new) : null;
}
private static Set<String> asSet(String[] array) { private static Set<String> asSet(String[] array) {
return (array != null) ? Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(array))) : null; return (array != null) ? Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(array))) : null;
} }

@ -22,7 +22,6 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.time.Duration; import java.time.Duration;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -172,8 +171,8 @@ public final class ClientHttpRequestFactories {
} }
if (sslBundle != null) { if (sslBundle != null) {
SslOptions options = sslBundle.getOptions(); SslOptions options = sslBundle.getOptions();
String[] enabledProtocols = toArray(options.getEnabledProtocols()); String[] enabledProtocols = SslOptions.toArray(options.getEnabledProtocols());
String[] ciphers = toArray(options.getCiphers()); String[] ciphers = SslOptions.toArray(options.getCiphers());
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslBundle.createSslContext(), SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslBundle.createSslContext(),
enabledProtocols, ciphers, new DefaultHostnameVerifier()); enabledProtocols, ciphers, new DefaultHostnameVerifier());
connectionManagerBuilder.setSSLSocketFactory(socketFactory); connectionManagerBuilder.setSSLSocketFactory(socketFactory);
@ -182,10 +181,6 @@ public final class ClientHttpRequestFactories {
return HttpClientBuilder.create().setConnectionManager(connectionManager).build(); return HttpClientBuilder.create().setConnectionManager(connectionManager).build();
} }
private static String[] toArray(Set<String> set) {
return (set != null) ? set.toArray(String[]::new) : null;
}
} }
/** /**
@ -205,6 +200,7 @@ public final class ClientHttpRequestFactories {
private static OkHttp3ClientHttpRequestFactory createRequestFactory(SslBundle sslBundle) { private static OkHttp3ClientHttpRequestFactory createRequestFactory(SslBundle sslBundle) {
if (sslBundle != null) { if (sslBundle != null) {
Assert.state(!sslBundle.getOptions().isSpecified(), "SSL Options cannot be specified with OkHttp");
SSLSocketFactory socketFactory = sslBundle.createSslContext().getSocketFactory(); SSLSocketFactory socketFactory = sslBundle.createSslContext().getSocketFactory();
TrustManager[] trustManagers = sslBundle.getManagers().getTrustManagers(); TrustManager[] trustManagers = sslBundle.getManagers().getTrustManagers();
Assert.state(trustManagers.length == 1, Assert.state(trustManagers.length == 1,
@ -228,6 +224,8 @@ public final class ClientHttpRequestFactories {
SslBundle sslBundle = settings.sslBundle(); SslBundle sslBundle = settings.sslBundle();
SimpleClientHttpRequestFactory requestFactory = (sslBundle != null) SimpleClientHttpRequestFactory requestFactory = (sslBundle != null)
? new SimpleClientHttpsRequestFactory(sslBundle) : new SimpleClientHttpRequestFactory(); ? new SimpleClientHttpsRequestFactory(sslBundle) : new SimpleClientHttpRequestFactory();
Assert.state(sslBundle == null || !sslBundle.getOptions().isSpecified(),
"SSL Options cannot be specified with Java connections");
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(settings::readTimeout).asInt(Duration::toMillis).to(requestFactory::setReadTimeout); map.from(settings::readTimeout).asInt(Duration::toMillis).to(requestFactory::setReadTimeout);
map.from(settings::connectTimeout).asInt(Duration::toMillis).to(requestFactory::setConnectTimeout); map.from(settings::connectTimeout).asInt(Duration::toMillis).to(requestFactory::setConnectTimeout);

@ -179,11 +179,12 @@ class SslServerCustomizer implements JettyServerCustomizer {
} }
factory.setCertAlias(key.getAlias()); factory.setCertAlias(key.getAlias());
if (options.getCiphers() != null) { if (options.getCiphers() != null) {
factory.setIncludeCipherSuites(options.getCiphers().toArray(String[]::new)); factory.setIncludeCipherSuites(SslOptions.toArray(options.getCiphers()));
factory.setExcludeCipherSuites(); factory.setExcludeCipherSuites();
} }
if (options.getEnabledProtocols() != null) { if (options.getEnabledProtocols() != null) {
factory.setIncludeProtocols(options.getEnabledProtocols().toArray(String[]::new)); factory.setIncludeProtocols(SslOptions.toArray(options.getEnabledProtocols()));
factory.setExcludeProtocols();
} }
try { try {
if (key.getPassword() != null) { if (key.getPassword() != null) {

Loading…
Cancel
Save