Skip to content

Commit afbdb49

Browse files
l-trottawilkinsona
authored andcommitted
Add support for Elasticsearch API-key-based authentication
See spring-projectsgh-46167 Signed-off-by: Laura Trotta <laura.trotta@elastic.co>
1 parent 795c5f6 commit afbdb49

File tree

4 files changed

+56
-0
lines changed

4 files changed

+56
-0
lines changed

module/spring-boot-elasticsearch/src/main/java/org/springframework/boot/elasticsearch/autoconfigure/ElasticsearchConnectionDetails.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ public interface ElasticsearchConnectionDetails extends ConnectionDetails {
5757
return null;
5858
}
5959

60+
/**
61+
* APIKey for authentication with Elasticsearch.
62+
* @return APIKey for authentication with Elasticsearch or {@code null}
63+
*/
64+
default @Nullable String getAPIKey() {
65+
return null;
66+
}
67+
6068
/**
6169
* Prefix added to the path of every request sent to Elasticsearch.
6270
* @return prefix added to the path of every request sent to Elasticsearch or

module/spring-boot-elasticsearch/src/main/java/org/springframework/boot/elasticsearch/autoconfigure/ElasticsearchProperties.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ public class ElasticsearchProperties {
4848
* Password for authentication with Elasticsearch.
4949
*/
5050
private @Nullable String password;
51+
/**
52+
* APIKey for authentication with Elasticsearch.
53+
*/
54+
private @Nullable String APIKey;
5155

5256
/**
5357
* Connection timeout used when communicating with Elasticsearch.
@@ -95,6 +99,14 @@ public void setPassword(@Nullable String password) {
9599
this.password = password;
96100
}
97101

102+
public @Nullable String getAPIKey() {
103+
return this.APIKey;
104+
}
105+
106+
public void setAPIKey(@Nullable String APIKey) {
107+
this.APIKey = APIKey;
108+
}
109+
98110
public Duration getConnectionTimeout() {
99111
return this.connectionTimeout;
100112
}

module/spring-boot-elasticsearch/src/main/java/org/springframework/boot/elasticsearch/autoconfigure/ElasticsearchRestClientConfigurations.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
3737
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
3838
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
39+
import org.apache.hc.core5.http.Header;
3940
import org.apache.hc.core5.http.HttpHost;
41+
import org.apache.hc.core5.http.message.BasicHeader;
4042
import org.apache.hc.core5.reactor.IOReactorConfig;
4143
import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
4244
import org.apache.hc.core5.util.Timeout;
@@ -67,6 +69,7 @@
6769
* @author Moritz Halbritter
6870
* @author Andy Wilkinson
6971
* @author Phillip Webb
72+
* @author Laura Trotta
7073
*/
7174
class ElasticsearchRestClientConfigurations {
7275

@@ -99,6 +102,11 @@ Rest5ClientBuilder elasticsearchRestClientBuilder(ElasticsearchConnectionDetails
99102
.stream()
100103
.map((node) -> new HttpHost(node.protocol().getScheme(), node.hostname(), node.port()))
101104
.toArray(HttpHost[]::new));
105+
if (connectionDetails.getAPIKey() != null) {
106+
builder.setDefaultHeaders(new Header[]{
107+
new BasicHeader("Authorization", "ApiKey " + connectionDetails.getAPIKey()),
108+
});
109+
}
102110
builder.setHttpClientConfigCallback((httpClientBuilder) -> builderCustomizers.orderedStream()
103111
.forEach((customizer) -> customizer.customize(httpClientBuilder)));
104112
builder.setConnectionManagerCallback((connectionManagerBuilder) -> builderCustomizers.orderedStream()
@@ -275,6 +283,11 @@ public List<Node> getNodes() {
275283
return this.properties.getPassword();
276284
}
277285

286+
@Override
287+
public @Nullable String getAPIKey() {
288+
return this.properties.getAPIKey();
289+
}
290+
278291
@Override
279292
public @Nullable String getPathPrefix() {
280293
return this.properties.getPathPrefix();

module/spring-boot-elasticsearch/src/test/java/org/springframework/boot/elasticsearch/autoconfigure/ElasticsearchRestClientAutoConfigurationTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.time.Duration;
2020
import java.util.ArrayList;
2121
import java.util.List;
22+
import java.util.Optional;
2223

2324
import co.elastic.clients.transport.rest5_client.low_level.Node;
2425
import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
@@ -33,6 +34,7 @@
3334
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
3435
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
3536
import org.apache.hc.core5.function.Resolver;
37+
import org.apache.hc.core5.http.Header;
3638
import org.apache.hc.core5.http.HttpHost;
3739
import org.apache.hc.core5.http.config.Registry;
3840
import org.apache.hc.core5.util.Timeout;
@@ -47,6 +49,7 @@
4749
import org.springframework.boot.testsupport.classpath.resources.WithPackageResources;
4850
import org.springframework.context.annotation.Bean;
4951
import org.springframework.context.annotation.Configuration;
52+
import org.springframework.web.client.RestClient;
5053

5154
import static org.assertj.core.api.Assertions.assertThat;
5255
import static org.mockito.BDDMockito.then;
@@ -62,6 +65,7 @@
6265
* @author Andy Wilkinson
6366
* @author Moritz Halbritter
6467
* @author Phillip Webb
68+
* @author Laura Trotta
6569
*/
6670
class ElasticsearchRestClientAutoConfigurationTests {
6771

@@ -134,6 +138,25 @@ void configureUriWithNoScheme() {
134138
});
135139
}
136140

141+
@Test
142+
void configureUriWithAPiKey() {
143+
this.contextRunner.withPropertyValues("spring.elasticsearch.uris=http://user@localhost:9200","spring.elasticsearch.apikey=some-apiKey").run((context) -> {
144+
Rest5Client client = context.getBean(Rest5Client.class);
145+
assertThat(client.getNodes().stream().map(Node::getHost).map(HttpHost::toString))
146+
.containsExactly("http://localhost:9200");
147+
assertThat(client)
148+
.extracting("defaultHeaders", InstanceOfAssertFactories.list(Header.class))
149+
.satisfies(( defaultHeaders) -> {
150+
Optional<? extends Header> authHeader = defaultHeaders.stream()
151+
.filter(x -> x.getName().equals("Authorization"))
152+
.findFirst();
153+
assertThat(authHeader).isPresent();
154+
assertThat(authHeader.get().getValue()).isEqualTo("ApiKey some-apiKey");
155+
});
156+
});
157+
}
158+
159+
137160
@Test
138161
void configureUriWithUsernameOnly() {
139162
this.contextRunner.withPropertyValues("spring.elasticsearch.uris=http://user@localhost:9200").run((context) -> {

0 commit comments

Comments
 (0)