This is an automated email from the ASF dual-hosted git repository. rouazana pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 0b50da794a0bd00fef3758afe274fe30ba6a0053 Author: Tran Tien Duc <[email protected]> AuthorDate: Tue Nov 12 16:36:31 2019 +0700 JAMES-2905 Update ClientProvider to configure ES authentication --- .../apache/james/backends/es/ClientProvider.java | 124 ++++++++++++++++++--- 1 file changed, 106 insertions(+), 18 deletions(-) diff --git a/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ClientProvider.java b/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ClientProvider.java index 4f35e38..923a820 100644 --- a/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ClientProvider.java +++ b/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ClientProvider.java @@ -18,23 +18,35 @@ ****************************************************************/ package org.apache.james.backends.es; +import java.io.File; import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import java.time.Duration; import java.time.LocalDateTime; -import java.util.Optional; import javax.annotation.PreDestroy; import javax.inject.Inject; import javax.inject.Provider; +import javax.net.ssl.SSLContext; +import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.time.DurationFormatUtils; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; +import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.ssl.TrustStrategy; +import org.apache.james.backends.es.ElasticSearchConfiguration.HostScheme; +import org.apache.james.backends.es.ElasticSearchConfiguration.SSLTrustConfiguration.SSLTrustStore; +import org.apache.james.backends.es.ElasticSearchConfiguration.SSLTrustConfiguration.SSLValidationStrategy; import org.elasticsearch.client.RestClient; -import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.client.RestHighLevelClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,14 +58,103 @@ import reactor.core.scheduler.Schedulers; public class ClientProvider implements Provider<RestHighLevelClient> { + private static class HttpAsyncClientConfigurer { + + private static final TrustStrategy TRUST_ALL = (x509Certificates, authType) -> true; + + private final ElasticSearchConfiguration configuration; + + private HttpAsyncClientConfigurer(ElasticSearchConfiguration configuration) { + this.configuration = configuration; + } + + private HttpAsyncClientBuilder configure(HttpAsyncClientBuilder builder) { + configureAuthentication(builder); + configureHostScheme(builder); + + return builder; + } + + private void configureHostScheme(HttpAsyncClientBuilder builder) { + HostScheme scheme = configuration.getHostScheme(); + + switch (scheme) { + case HTTP: + return; + case HTTPS: + configureSSLOptions(builder); + return; + default: + throw new NotImplementedException( + String.format("unrecognized hostScheme '%s'", scheme.name())); + } + } + + private void configureSSLOptions(HttpAsyncClientBuilder builder) { + try { + builder + .setSSLContext(sslContext()) + .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE); + } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | CertificateException | IOException e) { + throw new RuntimeException("Cannot set SSL options to the builder", e); + } + } + + private SSLContext sslContext() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException, + CertificateException, IOException { + + SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); + + SSLValidationStrategy strategy = configuration.getSslTrustConfiguration() + .getStrategy(); + + switch (strategy) { + case DEFAULT: + return sslContextBuilder.build(); + case IGNORE: + return sslContextBuilder.loadTrustMaterial(TRUST_ALL) + .build(); + case OVERRIDE: + return applyTrustStore(sslContextBuilder) + .build(); + default: + throw new NotImplementedException( + String.format("unrecognized strategy '%s'", strategy.name())); + } + } + + private SSLContextBuilder applyTrustStore(SSLContextBuilder sslContextBuilder) throws CertificateException, NoSuchAlgorithmException, + KeyStoreException, IOException { + + SSLTrustStore trustStore = configuration.getSslTrustConfiguration() + .getTrustStore() + .orElseThrow(() -> new IllegalStateException("SSLTrustStore cannot to be empty")); + + return sslContextBuilder + .loadTrustMaterial(new File(trustStore.getFilePath()), trustStore.getPassword().toCharArray()); + } + + private void configureAuthentication(HttpAsyncClientBuilder builder) { + configuration.getCredential() + .ifPresent(credential -> { + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(credential.getUsername(), credential.getPassword())); + builder.setDefaultCredentialsProvider(credentialsProvider); + }); + } + } + private static final Logger LOGGER = LoggerFactory.getLogger(ClientProvider.class); private final ElasticSearchConfiguration configuration; private final RestHighLevelClient client; + private final HttpAsyncClientConfigurer httpAsyncClientConfigurer; @Inject @VisibleForTesting ClientProvider(ElasticSearchConfiguration configuration) { + this.httpAsyncClientConfigurer = new HttpAsyncClientConfigurer(configuration); this.configuration = configuration; this.client = connect(configuration); } @@ -73,27 +174,14 @@ public class ClientProvider implements Provider<RestHighLevelClient> { private RestHighLevelClient connectToCluster(ElasticSearchConfiguration configuration) { LOGGER.info("Trying to connect to ElasticSearch service at {}", LocalDateTime.now()); - RestClientBuilder restClientBuilder = RestClient.builder(hostsToHttpHosts()); return new RestHighLevelClient( - credentialsProvider(configuration) - .map(provider -> restClientBuilder.setHttpClientConfigCallback( - httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(provider))) - .orElse(restClientBuilder) + RestClient + .builder(hostsToHttpHosts()) + .setHttpClientConfigCallback(httpAsyncClientConfigurer::configure) .setMaxRetryTimeoutMillis(Math.toIntExact(configuration.getRequestTimeout().toMillis()))); } - private Optional<CredentialsProvider> credentialsProvider(ElasticSearchConfiguration configuration) { - return configuration.getCredential() - .map(credential -> { - CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(AuthScope.ANY, - new UsernamePasswordCredentials(credential.getUsername(), credential.getPassword())); - - return credentialsProvider; - }); - } - private HttpHost[] hostsToHttpHosts() { return configuration.getHosts().stream() .map(host -> new HttpHost(host.getHostName(), host.getPort(), configuration.getHostScheme().name())) --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
