You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ro...@apache.org on 2019/11/14 09:13:23 UTC

[james-project] 03/07: JAMES-2905 Update ClientProvider to configure ES authentication

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 <dt...@linagora.com>
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: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org