diff --git a/src/main/java/org/springframework/data/elasticsearch/client/ClientConfiguration.java b/src/main/java/org/springframework/data/elasticsearch/client/ClientConfiguration.java index f5f3bc669..4d7f107f8 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/ClientConfiguration.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/ClientConfiguration.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Optional; +import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import org.springframework.http.HttpHeaders; @@ -31,6 +32,7 @@ * @author Mark Paluch * @author Peter-Josef Meisch * @author Huw Ayling-Miller + * @author Henrique Amaral * @since 3.2 */ public interface ClientConfiguration { @@ -119,6 +121,13 @@ static ClientConfiguration create(InetSocketAddress socketAddress) { */ Optional getSslContext(); + /** + * Returns the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if unconfigured. + * + * @return the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if unconfigured. + */ + Optional getHostNameVerifier(); + /** * Returns the {@link java.time.Duration connect timeout}. * @@ -210,6 +219,16 @@ interface MaybeSecureClientConfigurationBuilder extends TerminalClientConfigurat * @return the {@link TerminalClientConfigurationBuilder}. */ TerminalClientConfigurationBuilder usingSsl(SSLContext sslContext); + + /** + * Connect via {@literal https} using the givens {@link SSLContext} and HostnameVerifier {@link HostnameVerifier} .
+ * + * NOTE You need to leave out the protocol in + * {@link ClientConfigurationBuilderWithRequiredEndpoint#connectedTo(String)}. + * + * @return the {@link TerminalClientConfigurationBuilder}. + */ + TerminalClientConfigurationBuilder usingSsl(SSLContext sslContext, HostnameVerifier hostnameVerifier); } /** diff --git a/src/main/java/org/springframework/data/elasticsearch/client/ClientConfigurationBuilder.java b/src/main/java/org/springframework/data/elasticsearch/client/ClientConfigurationBuilder.java index 60dfd20e2..562e294ad 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/ClientConfigurationBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/ClientConfigurationBuilder.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.stream.Collectors; +import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationBuilderWithRequiredEndpoint; @@ -38,6 +39,7 @@ * @author Mark Paluch * @author Peter-Josef Meisch * @author Huw Ayling-Miller + * @author Henrique Amaral * @since 3.2 */ class ClientConfigurationBuilder @@ -47,6 +49,7 @@ class ClientConfigurationBuilder private HttpHeaders headers = HttpHeaders.EMPTY; private boolean useSsl; private @Nullable SSLContext sslContext; + private @Nullable HostnameVerifier hostnameVerifier; private Duration connectTimeout = Duration.ofSeconds(10); private Duration soTimeout = Duration.ofSeconds(5); private String username; @@ -105,6 +108,22 @@ public TerminalClientConfigurationBuilder usingSsl(SSLContext sslContext) { return this; } + /* + * (non-Javadoc) + * @see org.springframework.data.elasticsearch.client.ClientConfiguration.MaybeSecureClientConfigurationBuilder#usingSsl(javax.net.ssl.SSLContext, javax.net.ssl.HostnameVerifier) + */ + @Override + public TerminalClientConfigurationBuilder usingSsl(SSLContext sslContext, HostnameVerifier hostnameVerifier) { + + Assert.notNull(sslContext, "SSL Context must not be null"); + Assert.notNull(hostnameVerifier, "Host Name Verifier must not be null"); + + this.useSsl = true; + this.sslContext = sslContext; + this.hostnameVerifier = hostnameVerifier; + return this; + } + /* * (non-Javadoc) * @see org.springframework.data.elasticsearch.client.ClientConfiguration.TerminalClientConfigurationBuilder#withDefaultHeaders(org.springframework.http.HttpHeaders) @@ -181,7 +200,7 @@ public ClientConfiguration build() { } return new DefaultClientConfiguration(this.hosts, this.headers, this.useSsl, this.sslContext, this.soTimeout, - this.connectTimeout, this.pathPrefix); + this.connectTimeout, this.pathPrefix, this.hostnameVerifier); } private static InetSocketAddress parse(String hostAndPort) { diff --git a/src/main/java/org/springframework/data/elasticsearch/client/DefaultClientConfiguration.java b/src/main/java/org/springframework/data/elasticsearch/client/DefaultClientConfiguration.java index cf6b6ba28..35367456a 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/DefaultClientConfiguration.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/DefaultClientConfiguration.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Optional; +import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import org.springframework.http.HttpHeaders; @@ -44,9 +45,10 @@ class DefaultClientConfiguration implements ClientConfiguration { private final Duration soTimeout; private final Duration connectTimeout; private final String pathPrefix; + private final @Nullable HostnameVerifier hostnameVerifier; DefaultClientConfiguration(List hosts, HttpHeaders headers, boolean useSsl, - @Nullable SSLContext sslContext, Duration soTimeout, Duration connectTimeout, @Nullable String pathPrefix) { + @Nullable SSLContext sslContext, Duration soTimeout, Duration connectTimeout, @Nullable String pathPrefix, @Nullable HostnameVerifier hostnameVerifier) { this.hosts = Collections.unmodifiableList(new ArrayList<>(hosts)); this.headers = new HttpHeaders(headers); @@ -55,6 +57,7 @@ class DefaultClientConfiguration implements ClientConfiguration { this.soTimeout = soTimeout; this.connectTimeout = connectTimeout; this.pathPrefix = pathPrefix; + this.hostnameVerifier = hostnameVerifier; } /* @@ -93,6 +96,15 @@ public Optional getSslContext() { return Optional.ofNullable(this.sslContext); } + /* + * (non-Javadoc) + * @see org.springframework.data.elasticsearch.client.ClientConfiguration#getHostNameVerifier() + */ + @Override + public Optional getHostNameVerifier() { + return Optional.ofNullable(this.hostnameVerifier); + } + /* * (non-Javadoc) * @see org.springframework.data.elasticsearch.client.ClientConfiguration#getConnectTimeout() diff --git a/src/main/java/org/springframework/data/elasticsearch/client/RestClients.java b/src/main/java/org/springframework/data/elasticsearch/client/RestClients.java index 8e3bd0c48..d4055ac36 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/RestClients.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/RestClients.java @@ -24,6 +24,7 @@ import java.util.Optional; import java.util.stream.Collectors; +import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import org.apache.http.Header; @@ -53,6 +54,7 @@ * @author Christoph Strobl * @author Mark Paluch * @author Huw Ayling-Miller + * @author Henrique Amaral * @since 3.2 */ public final class RestClients { @@ -93,7 +95,9 @@ public static ElasticsearchRestClient create(ClientConfiguration clientConfigura builder.setHttpClientConfigCallback(clientBuilder -> { Optional sslContext = clientConfiguration.getSslContext(); + Optional hostNameVerifier = clientConfiguration.getHostNameVerifier(); sslContext.ifPresent(clientBuilder::setSSLContext); + hostNameVerifier.ifPresent(clientBuilder::setSSLHostnameVerifier); if (ClientLogger.isEnabled()) { diff --git a/src/test/java/org/springframework/data/elasticsearch/client/ClientConfigurationUnitTests.java b/src/test/java/org/springframework/data/elasticsearch/client/ClientConfigurationUnitTests.java index d9cee06ed..4619fc4aa 100644 --- a/src/test/java/org/springframework/data/elasticsearch/client/ClientConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/client/ClientConfigurationUnitTests.java @@ -23,6 +23,7 @@ import javax.net.ssl.SSLContext; +import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.junit.Test; import org.springframework.http.HttpHeaders; @@ -32,6 +33,7 @@ * @author Mark Paluch * @author Peter-Josef Meisch * @author Huw Ayling-Miller + * @author Henrique Amaral */ public class ClientConfigurationUnitTests { @@ -120,6 +122,25 @@ public void shouldAddBasicAuthenticationHeaderAndKeepHeaders() { assertThat(defaultHeaders.get(HttpHeaders.AUTHORIZATION)).isNull(); } + @Test // DATAES-673 + public void shouldCreateSslConfigurationWithHostnameVerifier() { + + SSLContext sslContext = mock(SSLContext.class); + + ClientConfiguration clientConfiguration = ClientConfiguration.builder() // + .connectedTo("foo", "bar") // + .usingSsl(sslContext, NoopHostnameVerifier.INSTANCE) // + .build(); + + assertThat(clientConfiguration.getEndpoints()).containsOnly(InetSocketAddress.createUnresolved("foo", 9200), + InetSocketAddress.createUnresolved("bar", 9200)); + assertThat(clientConfiguration.useSsl()).isTrue(); + assertThat(clientConfiguration.getSslContext()).contains(sslContext); + assertThat(clientConfiguration.getConnectTimeout()).isEqualTo(Duration.ofSeconds(10)); + assertThat(clientConfiguration.getSocketTimeout()).isEqualTo(Duration.ofSeconds(5)); + assertThat(clientConfiguration.getHostNameVerifier()).contains(NoopHostnameVerifier.INSTANCE); + } + private static String buildBasicAuth(String username, String password) { HttpHeaders headers = new HttpHeaders();