Skip to content

Commit 0ccf1b8

Browse files
committed
Add SSL service connection support for Couchbase
See gh-41137
1 parent 9c520d6 commit 0ccf1b8

File tree

6 files changed

+62
-34
lines changed

6 files changed

+62
-34
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfiguration.java

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,16 @@ public class CouchbaseAutoConfiguration {
9393

9494
@Bean
9595
@ConditionalOnMissingBean(CouchbaseConnectionDetails.class)
96-
PropertiesCouchbaseConnectionDetails couchbaseConnectionDetails() {
97-
return new PropertiesCouchbaseConnectionDetails(this.properties);
96+
PropertiesCouchbaseConnectionDetails couchbaseConnectionDetails(ObjectProvider<SslBundles> sslBundles) {
97+
return new PropertiesCouchbaseConnectionDetails(this.properties, sslBundles.getIfAvailable());
9898
}
9999

100100
@Bean
101101
@ConditionalOnMissingBean
102102
public ClusterEnvironment couchbaseClusterEnvironment(
103-
ObjectProvider<ClusterEnvironmentBuilderCustomizer> customizers, ObjectProvider<SslBundles> sslBundles) {
104-
Builder builder = initializeEnvironmentBuilder(sslBundles.getIfAvailable());
103+
ObjectProvider<ClusterEnvironmentBuilderCustomizer> customizers,
104+
CouchbaseConnectionDetails connectionDetails) {
105+
Builder builder = initializeEnvironmentBuilder(connectionDetails);
105106
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
106107
return builder.build();
107108
}
@@ -143,7 +144,7 @@ public Cluster couchbaseCluster(ClusterEnvironment couchbaseClusterEnvironment,
143144
return Cluster.connect(connectionDetails.getConnectionString(), options);
144145
}
145146

146-
private ClusterEnvironment.Builder initializeEnvironmentBuilder(SslBundles sslBundles) {
147+
private ClusterEnvironment.Builder initializeEnvironmentBuilder(CouchbaseConnectionDetails connectionDetails) {
147148
ClusterEnvironment.Builder builder = ClusterEnvironment.builder();
148149
Timeouts timeouts = this.properties.getEnv().getTimeouts();
149150
builder.timeoutConfig((config) -> config.kvTimeout(timeouts.getKeyValue())
@@ -159,31 +160,24 @@ private ClusterEnvironment.Builder initializeEnvironmentBuilder(SslBundles sslBu
159160
builder.ioConfig((config) -> config.maxHttpConnections(io.getMaxEndpoints())
160161
.numKvConnections(io.getMinEndpoints())
161162
.idleHttpConnectionTimeout(io.getIdleHttpConnectionTimeout()));
162-
if (this.properties.getEnv().getSsl().getEnabled()) {
163-
configureSsl(builder, sslBundles);
163+
SslBundle sslBundle = connectionDetails.getSslBundle();
164+
if (sslBundle != null) {
165+
configureSsl(builder, sslBundle);
164166
}
165167
return builder;
166168
}
167169

168-
private void configureSsl(Builder builder, SslBundles sslBundles) {
169-
Ssl sslProperties = this.properties.getEnv().getSsl();
170-
SslBundle sslBundle = (StringUtils.hasText(sslProperties.getBundle()))
171-
? sslBundles.getBundle(sslProperties.getBundle()) : null;
172-
Assert.state(sslBundle == null || !sslBundle.getOptions().isSpecified(),
173-
"SSL Options cannot be specified with Couchbase");
170+
private void configureSsl(Builder builder, SslBundle sslBundle) {
171+
Assert.state(!sslBundle.getOptions().isSpecified(), "SSL Options cannot be specified with Couchbase");
174172
builder.securityConfig((config) -> {
175173
config.enableTls(true);
176-
TrustManagerFactory trustManagerFactory = getTrustManagerFactory(sslBundle);
174+
TrustManagerFactory trustManagerFactory = sslBundle.getManagers().getTrustManagerFactory();
177175
if (trustManagerFactory != null) {
178176
config.trustManagerFactory(trustManagerFactory);
179177
}
180178
});
181179
}
182180

183-
private TrustManagerFactory getTrustManagerFactory(SslBundle sslBundle) {
184-
return (sslBundle != null) ? sslBundle.getManagers().getTrustManagerFactory() : null;
185-
}
186-
187181
@Configuration(proxyBeanMethods = false)
188182
@ConditionalOnClass(ObjectMapper.class)
189183
static class JacksonConfiguration {
@@ -247,8 +241,11 @@ static final class PropertiesCouchbaseConnectionDetails implements CouchbaseConn
247241

248242
private final CouchbaseProperties properties;
249243

250-
PropertiesCouchbaseConnectionDetails(CouchbaseProperties properties) {
244+
private final SslBundles sslBundles;
245+
246+
PropertiesCouchbaseConnectionDetails(CouchbaseProperties properties, SslBundles sslBundles) {
251247
this.properties = properties;
248+
this.sslBundles = sslBundles;
252249
}
253250

254251
@Override
@@ -266,6 +263,19 @@ public String getPassword() {
266263
return this.properties.getPassword();
267264
}
268265

266+
@Override
267+
public SslBundle getSslBundle() {
268+
Ssl ssl = this.properties.getEnv().getSsl();
269+
if (!ssl.getEnabled()) {
270+
return null;
271+
}
272+
if (StringUtils.hasLength(ssl.getBundle())) {
273+
Assert.notNull(this.sslBundles, "SSL bundle name has been set but no SSL bundles found in context");
274+
return this.sslBundles.getBundle(ssl.getBundle());
275+
}
276+
return SslBundle.systemDefault();
277+
}
278+
269279
}
270280

271281
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseConnectionDetails.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.autoconfigure.couchbase;
1818

1919
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
20+
import org.springframework.boot.ssl.SslBundle;
2021

2122
/**
2223
* Details required to establish a connection to a Couchbase service.
@@ -46,4 +47,13 @@ public interface CouchbaseConnectionDetails extends ConnectionDetails {
4647
*/
4748
String getPassword();
4849

50+
/**
51+
* SSL bundle to use.
52+
* @return the SSL bundle to use
53+
* @since 3.5.0
54+
*/
55+
default SslBundle getSslBundle() {
56+
return null;
57+
}
58+
4959
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfigurationTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -196,7 +196,7 @@ void enableSsl() {
196196
testClusterEnvironment((env) -> {
197197
SecurityConfig securityConfig = env.securityConfig();
198198
assertThat(securityConfig.tlsEnabled()).isTrue();
199-
assertThat(securityConfig.trustManagerFactory()).isNull();
199+
assertThat(securityConfig.trustManagerFactory()).isNotNull();
200200
}, "spring.couchbase.env.ssl.enabled=true");
201201
}
202202

spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/couchbase/CouchbaseContainerConnectionDetailsFactory.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
1919
import org.testcontainers.couchbase.CouchbaseContainer;
2020

2121
import org.springframework.boot.autoconfigure.couchbase.CouchbaseConnectionDetails;
22+
import org.springframework.boot.ssl.SslBundle;
2223
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
2324
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
2425
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
@@ -66,6 +67,11 @@ public String getConnectionString() {
6667
return getContainer().getConnectionString();
6768
}
6869

70+
@Override
71+
public SslBundle getSslBundle() {
72+
return super.getSslBundle();
73+
}
74+
6975
}
7076

7177
}

spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-couchbase/src/dockerTest/java/smoketest/data/couchbase/SampleCouchbaseApplicationReactiveSslTests.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,6 +26,8 @@
2626

2727
import org.springframework.beans.factory.annotation.Autowired;
2828
import org.springframework.boot.test.context.SpringBootTest;
29+
import org.springframework.boot.testcontainers.service.connection.PemKeyStore;
30+
import org.springframework.boot.testcontainers.service.connection.PemTrustStore;
2931
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
3032
import org.springframework.boot.testsupport.container.TestImage;
3133
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
@@ -38,16 +40,15 @@
3840
* @author Scott Frederick
3941
*/
4042
@Testcontainers(disabledWithoutDocker = true)
41-
@SpringBootTest(properties = { "spring.couchbase.env.ssl.bundle=client", "spring.data.couchbase.bucket-name=cbbucket",
42-
"spring.ssl.bundle.pem.client.keystore.certificate=classpath:ssl/test-client.crt",
43-
"spring.ssl.bundle.pem.client.keystore.private-key=classpath:ssl/test-client.key",
44-
"spring.ssl.bundle.pem.client.truststore.certificate=classpath:ssl/test-ca.crt" })
43+
@SpringBootTest(properties = { "spring.data.couchbase.bucket-name=cbbucket" })
4544
class SampleCouchbaseApplicationReactiveSslTests {
4645

4746
private static final String BUCKET_NAME = "cbbucket";
4847

4948
@Container
5049
@ServiceConnection
50+
@PemKeyStore(certificate = "classpath:ssl/test-client.crt", privateKey = "classpath:ssl/test-client.key")
51+
@PemTrustStore(certificate = "classpath:ssl/test-ca.crt")
5152
static final CouchbaseContainer couchbase = TestImage.container(SecureCouchbaseContainer.class)
5253
.withBucket(new BucketDefinition(BUCKET_NAME));
5354

spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-couchbase/src/dockerTest/java/smoketest/data/couchbase/SampleCouchbaseApplicationSslTests.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@
2424

2525
import org.springframework.beans.factory.annotation.Autowired;
2626
import org.springframework.boot.test.autoconfigure.data.couchbase.DataCouchbaseTest;
27+
import org.springframework.boot.testcontainers.service.connection.PemKeyStore;
28+
import org.springframework.boot.testcontainers.service.connection.PemTrustStore;
2729
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
2830
import org.springframework.boot.testsupport.container.TestImage;
2931
import org.springframework.data.couchbase.core.CouchbaseTemplate;
@@ -36,17 +38,16 @@
3638
* @author Scott Frederick
3739
*/
3840
@Testcontainers(disabledWithoutDocker = true)
39-
@DataCouchbaseTest(properties = { "spring.couchbase.env.ssl.bundle=client", "spring.couchbase.env.timeouts.connect=2m",
40-
"spring.data.couchbase.bucket-name=cbbucket",
41-
"spring.ssl.bundle.pem.client.keystore.certificate=classpath:ssl/test-client.crt",
42-
"spring.ssl.bundle.pem.client.keystore.private-key=classpath:ssl/test-client.key",
43-
"spring.ssl.bundle.pem.client.truststore.certificate=classpath:ssl/test-ca.crt" })
41+
@DataCouchbaseTest(
42+
properties = { "spring.couchbase.env.timeouts.connect=2m", "spring.data.couchbase.bucket-name=cbbucket" })
4443
class SampleCouchbaseApplicationSslTests {
4544

4645
private static final String BUCKET_NAME = "cbbucket";
4746

4847
@Container
4948
@ServiceConnection
49+
@PemKeyStore(certificate = "classpath:ssl/test-client.crt", privateKey = "classpath:ssl/test-client.key")
50+
@PemTrustStore(certificate = "classpath:ssl/test-ca.crt")
5051
static final CouchbaseContainer couchbase = TestImage.container(SecureCouchbaseContainer.class)
5152
.withBucket(new BucketDefinition(BUCKET_NAME));
5253

0 commit comments

Comments
 (0)