From 964e7125a9cb251d9094b3cd5c6c5e7c09499f51 Mon Sep 17 00:00:00 2001 From: Evgeniy Cheban Date: Mon, 11 May 2020 03:45:11 +0300 Subject: [PATCH] Support userInfo in elasticsearch uri Fixes gh-21291 --- ...ElasticsearchRestClientConfigurations.java | 31 +++++++- ...earchRestClientAutoConfigurationTests.java | 71 +++++++++++++++++++ 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java index b387ff0e2b9c..d7ff03005ba2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.elasticsearch; +import java.net.URI; import java.time.Duration; import org.apache.http.HttpHost; @@ -43,6 +44,7 @@ * @author Brian Clozel * @author Stephane Nicoll * @author Vedran Pavic + * @author Evgeniy Cheban */ class ElasticsearchRestClientConfigurations { @@ -58,7 +60,7 @@ RestClientBuilderCustomizer defaultRestClientBuilderCustomizer(ElasticsearchRest @Bean RestClientBuilder elasticsearchRestClientBuilder(ElasticsearchRestClientProperties properties, ObjectProvider builderCustomizers) { - HttpHost[] hosts = properties.getUris().stream().map(HttpHost::create).toArray(HttpHost[]::new); + HttpHost[] hosts = properties.getUris().stream().map(this::createHttpHost).toArray(HttpHost[]::new); RestClientBuilder builder = RestClient.builder(hosts); builder.setHttpClientConfigCallback((httpClientBuilder) -> { builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(httpClientBuilder)); @@ -72,6 +74,12 @@ RestClientBuilder elasticsearchRestClientBuilder(ElasticsearchRestClientProperti return builder; } + private HttpHost createHttpHost(String uri) { + URI parsedUri = URI.create(uri); + String userInfo = parsedUri.getUserInfo(); + return HttpHost.create((userInfo != null) ? uri.replace(userInfo + "@", "") : uri); + } + } @Configuration(proxyBeanMethods = false) @@ -124,15 +132,32 @@ public void customize(RestClientBuilder builder) { @Override public void customize(HttpAsyncClientBuilder builder) { + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + builder.setDefaultCredentialsProvider(credentialsProvider); + this.properties.getUris().stream().map(URI::create).filter((uri) -> uri.getUserInfo() != null) + .forEach((uri) -> { + AuthScope authScope = new AuthScope(uri.getHost(), uri.getPort()); + Credentials credentials = createCredentials(uri.getUserInfo()); + credentialsProvider.setCredentials(authScope, credentials); + }); map.from(this.properties::getUsername).whenHasText().to((username) -> { - CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); Credentials credentials = new UsernamePasswordCredentials(this.properties.getUsername(), this.properties.getPassword()); credentialsProvider.setCredentials(AuthScope.ANY, credentials); - builder.setDefaultCredentialsProvider(credentialsProvider); }); } + private Credentials createCredentials(String usernameAndPassword) { + int delimiter = usernameAndPassword.indexOf(":"); + if (delimiter == -1) { + return new UsernamePasswordCredentials(usernameAndPassword, null); + } + + String username = usernameAndPassword.substring(0, delimiter); + String password = usernameAndPassword.substring(delimiter + 1); + return new UsernamePasswordCredentials(username, password); + } + @Override public void customize(RequestConfig.Builder builder) { map.from(this.properties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationTests.java index f576423f6c17..5a4074d2356f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationTests.java @@ -20,10 +20,16 @@ import java.util.HashMap; import java.util.Map; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.Credentials; +import org.apache.http.client.CredentialsProvider; import org.apache.http.client.config.RequestConfig; import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; +import org.assertj.core.api.InstanceOfAssertFactories; import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.client.Node; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; @@ -47,6 +53,7 @@ * * @author Brian Clozel * @author Vedran Pavic + * @author Evgeniy Cheban */ @Testcontainers(disabledWithoutDocker = true) class ElasticsearchRestClientAutoConfigurationTests { @@ -156,6 +163,70 @@ void restClientCanQueryElasticsearchNode() { }); } + @Test + void configureUriWithUsernameOnly() { + this.contextRunner.withPropertyValues("spring.elasticsearch.rest.uris=http://user@localhost:9200") + .run((context) -> { + RestClient client = context.getBean(RestClient.class); + assertThat(client.getNodes().stream().map(Node::getHost).map(HttpHost::toString)) + .containsExactly("http://localhost:9200"); + assertThat(client).extracting("client") + .extracting("credentialsProvider", + InstanceOfAssertFactories.type(CredentialsProvider.class)) + .satisfies((credentialsProvider) -> { + Credentials credentials = credentialsProvider + .getCredentials(new AuthScope("localhost", 9200)); + assertThat(credentials.getUserPrincipal().getName()).isEqualTo("user"); + assertThat(credentials.getPassword()).isNull(); + }); + }); + } + + @Test + void configureUriWithUsernameAndEmptyPassword() { + this.contextRunner.withPropertyValues("spring.elasticsearch.rest.uris=http://user:@localhost:9200") + .run((context) -> { + RestClient client = context.getBean(RestClient.class); + assertThat(client.getNodes().stream().map(Node::getHost).map(HttpHost::toString)) + .containsExactly("http://localhost:9200"); + assertThat(client).extracting("client") + .extracting("credentialsProvider", + InstanceOfAssertFactories.type(CredentialsProvider.class)) + .satisfies((credentialsProvider) -> { + Credentials credentials = credentialsProvider + .getCredentials(new AuthScope("localhost", 9200)); + assertThat(credentials.getUserPrincipal().getName()).isEqualTo("user"); + assertThat(credentials.getPassword()).isEmpty(); + }); + }); + } + + @Test + void configureUriWithUsernameAndPasswordWhenUsernameAndPasswordPropertiesSet() { + this.contextRunner + .withPropertyValues("spring.elasticsearch.rest.uris=http://user:password@localhost:9200,localhost:9201", + "spring.elasticsearch.rest.username=admin", "spring.elasticsearch.rest.password=admin") + .run((context) -> { + RestClient client = context.getBean(RestClient.class); + assertThat(client.getNodes().stream().map(Node::getHost).map(HttpHost::toString)) + .containsExactly("http://localhost:9200", "http://localhost:9201"); + assertThat(client).extracting("client") + .extracting("credentialsProvider", + InstanceOfAssertFactories.type(CredentialsProvider.class)) + .satisfies((credentialsProvider) -> { + Credentials uriCredentials = credentialsProvider + .getCredentials(new AuthScope("localhost", 9200)); + assertThat(uriCredentials.getUserPrincipal().getName()).isEqualTo("user"); + assertThat(uriCredentials.getPassword()).isEqualTo("password"); + + Credentials defaultCredentials = credentialsProvider + .getCredentials(new AuthScope("localhost", 9201)); + assertThat(defaultCredentials.getUserPrincipal().getName()).isEqualTo("admin"); + assertThat(defaultCredentials.getPassword()).isEqualTo("admin"); + }); + }); + } + @Configuration(proxyBeanMethods = false) static class CustomRestClientConfiguration {