Add SSL service connection support for MongoDB

See gh-41137
This commit is contained in:
Moritz Halbritter 2025-02-11 10:20:32 +01:00
parent 0ccf1b81d8
commit 109fd6f97d
12 changed files with 167 additions and 55 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.data.mongo;
import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClient;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@ -27,6 +28,7 @@ import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
import org.springframework.boot.autoconfigure.mongo.MongoProperties; import org.springframework.boot.autoconfigure.mongo.MongoProperties;
import org.springframework.boot.autoconfigure.mongo.PropertiesMongoConnectionDetails; import org.springframework.boot.autoconfigure.mongo.PropertiesMongoConnectionDetails;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.MongoTemplate;
@ -59,8 +61,9 @@ public class MongoDataAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean(MongoConnectionDetails.class) @ConditionalOnMissingBean(MongoConnectionDetails.class)
PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties) { PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties,
return new PropertiesMongoConnectionDetails(properties); ObjectProvider<SslBundles> sslBundles) {
return new PropertiesMongoConnectionDetails(properties, sslBundles.getIfAvailable());
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2024 the original author or authors. * Copyright 2012-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -48,8 +48,9 @@ public class MongoAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean(MongoConnectionDetails.class) @ConditionalOnMissingBean(MongoConnectionDetails.class)
PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties) { PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties,
return new PropertiesMongoConnectionDetails(properties); ObjectProvider<SslBundles> sslBundles) {
return new PropertiesMongoConnectionDetails(properties, sslBundles.getIfAvailable());
} }
@Bean @Bean
@ -70,9 +71,9 @@ public class MongoAutoConfiguration {
@Bean @Bean
StandardMongoClientSettingsBuilderCustomizer standardMongoSettingsCustomizer(MongoProperties properties, StandardMongoClientSettingsBuilderCustomizer standardMongoSettingsCustomizer(MongoProperties properties,
MongoConnectionDetails connectionDetails, ObjectProvider<SslBundles> sslBundles) { MongoConnectionDetails connectionDetails) {
return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails.getConnectionString(), return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails,
properties.getUuidRepresentation(), properties.getSsl(), sslBundles.getIfAvailable()); properties.getUuidRepresentation());
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.mongo;
import com.mongodb.ConnectionString; import com.mongodb.ConnectionString;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
import org.springframework.boot.ssl.SslBundle;
/** /**
* Details required to establish a connection to a MongoDB service. * Details required to establish a connection to a MongoDB service.
@ -36,6 +37,15 @@ public interface MongoConnectionDetails extends ConnectionDetails {
*/ */
ConnectionString getConnectionString(); ConnectionString getConnectionString();
/**
* SSL bundle to use.
* @return the SSL bundle to use
* @since 3.5.0
*/
default SslBundle getSslBundle() {
return null;
}
/** /**
* GridFS configuration. * GridFS configuration.
* @return the GridFS configuration or {@code null} * @return the GridFS configuration or {@code null}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2024 the original author or authors. * Copyright 2012-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -53,8 +53,9 @@ public class MongoReactiveAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean(MongoConnectionDetails.class) @ConditionalOnMissingBean(MongoConnectionDetails.class)
PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties) { PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties,
return new PropertiesMongoConnectionDetails(properties); ObjectProvider<SslBundles> sslBundles) {
return new PropertiesMongoConnectionDetails(properties, sslBundles.getIfAvailable());
} }
@Bean @Bean
@ -77,9 +78,9 @@ public class MongoReactiveAutoConfiguration {
@Bean @Bean
StandardMongoClientSettingsBuilderCustomizer standardMongoSettingsCustomizer(MongoProperties properties, StandardMongoClientSettingsBuilderCustomizer standardMongoSettingsCustomizer(MongoProperties properties,
MongoConnectionDetails connectionDetails, ObjectProvider<SslBundles> sslBundles) { MongoConnectionDetails connectionDetails) {
return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails.getConnectionString(), return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails,
properties.getUuidRepresentation(), properties.getSsl(), sslBundles.getIfAvailable()); properties.getUuidRepresentation());
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2024 the original author or authors. * Copyright 2012-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,6 +23,10 @@ import java.util.List;
import com.mongodb.ConnectionString; import com.mongodb.ConnectionString;
import org.springframework.boot.autoconfigure.mongo.MongoProperties.Ssl;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -38,8 +42,11 @@ public class PropertiesMongoConnectionDetails implements MongoConnectionDetails
private final MongoProperties properties; private final MongoProperties properties;
public PropertiesMongoConnectionDetails(MongoProperties properties) { private final SslBundles sslBundles;
public PropertiesMongoConnectionDetails(MongoProperties properties, SslBundles sslBundles) {
this.properties = properties; this.properties = properties;
this.sslBundles = sslBundles;
} }
@Override @Override
@ -90,6 +97,19 @@ public class PropertiesMongoConnectionDetails implements MongoConnectionDetails
PropertiesMongoConnectionDetails.this.properties.getGridfs().getBucket()); PropertiesMongoConnectionDetails.this.properties.getGridfs().getBucket());
} }
@Override
public SslBundle getSslBundle() {
Ssl ssl = this.properties.getSsl();
if (!ssl.isEnabled()) {
return null;
}
if (StringUtils.hasLength(ssl.getBundle())) {
Assert.notNull(this.sslBundles, "SSL bundle name has been set but no SSL bundles found in context");
return this.sslBundles.getBundle(ssl.getBundle());
}
return SslBundle.systemDefault();
}
private List<String> getOptions() { private List<String> getOptions() {
List<String> options = new ArrayList<>(); List<String> options = new ArrayList<>();
if (StringUtils.hasText(this.properties.getReplicaSetName())) { if (StringUtils.hasText(this.properties.getReplicaSetName())) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -41,26 +41,55 @@ public class StandardMongoClientSettingsBuilderCustomizer implements MongoClient
private final UuidRepresentation uuidRepresentation; private final UuidRepresentation uuidRepresentation;
private final MongoConnectionDetails connectionDetails;
private final MongoProperties.Ssl ssl; private final MongoProperties.Ssl ssl;
private final SslBundles sslBundles; private final SslBundles sslBundles;
private int order = 0; private int order = 0;
/**
* Create a new instance.
* @param connectionString the connection string
* @param uuidRepresentation the uuid representation
* @param ssl the ssl properties
* @param sslBundles the ssl bundles
* @deprecated since 3.5.0 for removal in 3.7.0 in favor of
* {@link #StandardMongoClientSettingsBuilderCustomizer(MongoConnectionDetails, UuidRepresentation)}
*/
@Deprecated(forRemoval = true, since = "3.5.0")
public StandardMongoClientSettingsBuilderCustomizer(ConnectionString connectionString, public StandardMongoClientSettingsBuilderCustomizer(ConnectionString connectionString,
UuidRepresentation uuidRepresentation, MongoProperties.Ssl ssl, SslBundles sslBundles) { UuidRepresentation uuidRepresentation, MongoProperties.Ssl ssl, SslBundles sslBundles) {
this.connectionDetails = null;
this.connectionString = connectionString; this.connectionString = connectionString;
this.uuidRepresentation = uuidRepresentation; this.uuidRepresentation = uuidRepresentation;
this.ssl = ssl; this.ssl = ssl;
this.sslBundles = sslBundles; this.sslBundles = sslBundles;
} }
public StandardMongoClientSettingsBuilderCustomizer(MongoConnectionDetails connectionDetails,
UuidRepresentation uuidRepresentation) {
this.connectionString = null;
this.ssl = null;
this.sslBundles = null;
this.connectionDetails = connectionDetails;
this.uuidRepresentation = uuidRepresentation;
}
@Override @Override
public void customize(MongoClientSettings.Builder settingsBuilder) { public void customize(MongoClientSettings.Builder settingsBuilder) {
settingsBuilder.uuidRepresentation(this.uuidRepresentation); settingsBuilder.uuidRepresentation(this.uuidRepresentation);
settingsBuilder.applyConnectionString(this.connectionString); if (this.connectionDetails != null) {
if (this.ssl.isEnabled()) { settingsBuilder.applyConnectionString(this.connectionDetails.getConnectionString());
settingsBuilder.applyToSslSettings(this::configureSsl); settingsBuilder.applyToSslSettings(this::configureSslIfNeeded);
}
else {
settingsBuilder.uuidRepresentation(this.uuidRepresentation);
settingsBuilder.applyConnectionString(this.connectionString);
if (this.ssl.isEnabled()) {
settingsBuilder.applyToSslSettings(this::configureSsl);
}
} }
} }
@ -73,6 +102,15 @@ public class StandardMongoClientSettingsBuilderCustomizer implements MongoClient
} }
} }
private void configureSslIfNeeded(SslSettings.Builder settings) {
SslBundle sslBundle = this.connectionDetails.getSslBundle();
if (sslBundle != null) {
settings.enabled(true);
Assert.state(!sslBundle.getOptions().isSpecified(), "SSL options cannot be specified with MongoDB");
settings.context(sslBundle.createSslContext());
}
}
@Override @Override
public int getOrder() { public int getOrder() {
return this.order; return this.order;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2024 the original author or authors. * Copyright 2012-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -80,7 +80,7 @@ class MongoAutoConfigurationTests {
this.contextRunner.withPropertyValues("spring.data.mongodb.ssl.enabled=true").run((context) -> { this.contextRunner.withPropertyValues("spring.data.mongodb.ssl.enabled=true").run((context) -> {
SslSettings sslSettings = getSettings(context).getSslSettings(); SslSettings sslSettings = getSettings(context).getSslSettings();
assertThat(sslSettings.isEnabled()).isTrue(); assertThat(sslSettings.isEnabled()).isTrue();
assertThat(sslSettings.getContext()).isNull(); assertThat(sslSettings.getContext()).isNotNull();
}); });
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2024 the original author or authors. * Copyright 2012-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -88,7 +88,7 @@ class MongoReactiveAutoConfigurationTests {
this.contextRunner.withPropertyValues("spring.data.mongodb.ssl.enabled=true").run((context) -> { this.contextRunner.withPropertyValues("spring.data.mongodb.ssl.enabled=true").run((context) -> {
SslSettings sslSettings = getSettings(context).getSslSettings(); SslSettings sslSettings = getSettings(context).getSslSettings();
assertThat(sslSettings.isEnabled()).isTrue(); assertThat(sslSettings.isEnabled()).isTrue();
assertThat(sslSettings.getContext()).isNull(); assertThat(sslSettings.getContext()).isNotNull();
}); });
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2024 the original author or authors. * Copyright 2012-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,24 +19,41 @@ package org.springframework.boot.autoconfigure.mongo;
import java.util.List; import java.util.List;
import com.mongodb.ConnectionString; import com.mongodb.ConnectionString;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.ssl.DefaultSslBundleRegistry;
import org.springframework.boot.ssl.SslBundle;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link PropertiesMongoConnectionDetails}. * Tests for {@link PropertiesMongoConnectionDetails}.
* *
* @author Christoph Dreis * @author Christoph Dreis
* @author Scott Frederick * @author Scott Frederick
* @author Moritz Halbritter
*/ */
class PropertiesMongoConnectionDetailsTests { class PropertiesMongoConnectionDetailsTests {
private final MongoProperties properties = new MongoProperties(); private MongoProperties properties;
private DefaultSslBundleRegistry sslBundleRegistry;
private PropertiesMongoConnectionDetails connectionDetails;
@BeforeEach
void setUp() {
this.properties = new MongoProperties();
this.sslBundleRegistry = new DefaultSslBundleRegistry();
this.connectionDetails = new PropertiesMongoConnectionDetails(this.properties, this.sslBundleRegistry);
}
@Test @Test
void credentialsCanBeConfiguredWithUsername() { void credentialsCanBeConfiguredWithUsername() {
this.properties.setUsername("user"); this.properties.setUsername("user");
ConnectionString connectionString = getConnectionString(); ConnectionString connectionString = this.connectionDetails.getConnectionString();
assertThat(connectionString.getUsername()).isEqualTo("user"); assertThat(connectionString.getUsername()).isEqualTo("user");
assertThat(connectionString.getPassword()).isEmpty(); assertThat(connectionString.getPassword()).isEmpty();
assertThat(connectionString.getCredential().getUserName()).isEqualTo("user"); assertThat(connectionString.getCredential().getUserName()).isEqualTo("user");
@ -48,7 +65,7 @@ class PropertiesMongoConnectionDetailsTests {
void credentialsCanBeConfiguredWithUsernameAndPassword() { void credentialsCanBeConfiguredWithUsernameAndPassword() {
this.properties.setUsername("user"); this.properties.setUsername("user");
this.properties.setPassword("secret".toCharArray()); this.properties.setPassword("secret".toCharArray());
ConnectionString connectionString = getConnectionString(); ConnectionString connectionString = this.connectionDetails.getConnectionString();
assertThat(connectionString.getUsername()).isEqualTo("user"); assertThat(connectionString.getUsername()).isEqualTo("user");
assertThat(connectionString.getPassword()).isEqualTo("secret".toCharArray()); assertThat(connectionString.getPassword()).isEqualTo("secret".toCharArray());
assertThat(connectionString.getCredential().getUserName()).isEqualTo("user"); assertThat(connectionString.getCredential().getUserName()).isEqualTo("user");
@ -59,13 +76,13 @@ class PropertiesMongoConnectionDetailsTests {
@Test @Test
void databaseCanBeConfigured() { void databaseCanBeConfigured() {
this.properties.setDatabase("db"); this.properties.setDatabase("db");
ConnectionString connectionString = getConnectionString(); ConnectionString connectionString = this.connectionDetails.getConnectionString();
assertThat(connectionString.getDatabase()).isEqualTo("db"); assertThat(connectionString.getDatabase()).isEqualTo("db");
} }
@Test @Test
void databaseHasDefaultWhenNotConfigured() { void databaseHasDefaultWhenNotConfigured() {
ConnectionString connectionString = getConnectionString(); ConnectionString connectionString = this.connectionDetails.getConnectionString();
assertThat(connectionString.getDatabase()).isEqualTo("test"); assertThat(connectionString.getDatabase()).isEqualTo("test");
} }
@ -74,7 +91,7 @@ class PropertiesMongoConnectionDetailsTests {
this.properties.setUsername("user"); this.properties.setUsername("user");
this.properties.setDatabase("db"); this.properties.setDatabase("db");
this.properties.setAuthenticationDatabase("authdb"); this.properties.setAuthenticationDatabase("authdb");
ConnectionString connectionString = getConnectionString(); ConnectionString connectionString = this.connectionDetails.getConnectionString();
assertThat(connectionString.getDatabase()).isEqualTo("db"); assertThat(connectionString.getDatabase()).isEqualTo("db");
assertThat(connectionString.getCredential().getSource()).isEqualTo("authdb"); assertThat(connectionString.getCredential().getSource()).isEqualTo("authdb");
assertThat(connectionString.getCredential().getUserName()).isEqualTo("user"); assertThat(connectionString.getCredential().getUserName()).isEqualTo("user");
@ -83,14 +100,14 @@ class PropertiesMongoConnectionDetailsTests {
@Test @Test
void authenticationDatabaseIsNotConfiguredWhenUsernameIsNotConfigured() { void authenticationDatabaseIsNotConfiguredWhenUsernameIsNotConfigured() {
this.properties.setAuthenticationDatabase("authdb"); this.properties.setAuthenticationDatabase("authdb");
ConnectionString connectionString = getConnectionString(); ConnectionString connectionString = this.connectionDetails.getConnectionString();
assertThat(connectionString.getCredential()).isNull(); assertThat(connectionString.getCredential()).isNull();
} }
@Test @Test
void replicaSetCanBeConfigured() { void replicaSetCanBeConfigured() {
this.properties.setReplicaSetName("test"); this.properties.setReplicaSetName("test");
ConnectionString connectionString = getConnectionString(); ConnectionString connectionString = this.connectionDetails.getConnectionString();
assertThat(connectionString.getRequiredReplicaSetName()).isEqualTo("test"); assertThat(connectionString.getRequiredReplicaSetName()).isEqualTo("test");
} }
@ -99,7 +116,7 @@ class PropertiesMongoConnectionDetailsTests {
this.properties.setUsername("user"); this.properties.setUsername("user");
this.properties.setDatabase("db"); this.properties.setDatabase("db");
this.properties.setReplicaSetName("test"); this.properties.setReplicaSetName("test");
ConnectionString connectionString = getConnectionString(); ConnectionString connectionString = this.connectionDetails.getConnectionString();
assertThat(connectionString.getDatabase()).isEqualTo("db"); assertThat(connectionString.getDatabase()).isEqualTo("db");
assertThat(connectionString.getRequiredReplicaSetName()).isEqualTo("test"); assertThat(connectionString.getRequiredReplicaSetName()).isEqualTo("test");
} }
@ -107,14 +124,14 @@ class PropertiesMongoConnectionDetailsTests {
@Test @Test
void replicaSetCanBeNull() { void replicaSetCanBeNull() {
this.properties.setReplicaSetName(null); this.properties.setReplicaSetName(null);
ConnectionString connectionString = getConnectionString(); ConnectionString connectionString = this.connectionDetails.getConnectionString();
assertThat(connectionString.getRequiredReplicaSetName()).isNull(); assertThat(connectionString.getRequiredReplicaSetName()).isNull();
} }
@Test @Test
void replicaSetCanBeBlank() { void replicaSetCanBeBlank() {
this.properties.setReplicaSetName(""); this.properties.setReplicaSetName("");
ConnectionString connectionString = getConnectionString(); ConnectionString connectionString = this.connectionDetails.getConnectionString();
assertThat(connectionString.getRequiredReplicaSetName()).isNull(); assertThat(connectionString.getRequiredReplicaSetName()).isNull();
} }
@ -122,18 +139,32 @@ class PropertiesMongoConnectionDetailsTests {
void whenAdditionalHostsAreConfiguredThenTheyAreIncludedInHostsOfConnectionString() { void whenAdditionalHostsAreConfiguredThenTheyAreIncludedInHostsOfConnectionString() {
this.properties.setHost("mongo1.example.com"); this.properties.setHost("mongo1.example.com");
this.properties.setAdditionalHosts(List.of("mongo2.example.com", "mongo3.example.com")); this.properties.setAdditionalHosts(List.of("mongo2.example.com", "mongo3.example.com"));
ConnectionString connectionString = getConnectionString(); ConnectionString connectionString = this.connectionDetails.getConnectionString();
assertThat(connectionString.getHosts()).containsExactly("mongo1.example.com", "mongo2.example.com", assertThat(connectionString.getHosts()).containsExactly("mongo1.example.com", "mongo2.example.com",
"mongo3.example.com"); "mongo3.example.com");
} }
private PropertiesMongoConnectionDetails createConnectionDetails() { @Test
return new PropertiesMongoConnectionDetails(this.properties); void shouldReturnSslBundle() {
SslBundle bundle1 = mock(SslBundle.class);
this.sslBundleRegistry.registerBundle("bundle-1", bundle1);
this.properties.getSsl().setBundle("bundle-1");
SslBundle sslBundle = this.connectionDetails.getSslBundle();
assertThat(sslBundle).isSameAs(bundle1);
} }
private ConnectionString getConnectionString() { @Test
PropertiesMongoConnectionDetails connectionDetails = createConnectionDetails(); void shouldReturnSystemDefaultBundleIfSslIsEnabledButBundleNotSet() {
return connectionDetails.getConnectionString(); this.properties.getSsl().setEnabled(true);
SslBundle sslBundle = this.connectionDetails.getSslBundle();
assertThat(sslBundle).isNotNull();
}
@Test
void shouldReturnNullIfSslIsNotEnabled() {
this.properties.getSsl().setEnabled(false);
SslBundle sslBundle = this.connectionDetails.getSslBundle();
assertThat(sslBundle).isNull();
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,6 +20,7 @@ import com.mongodb.ConnectionString;
import org.testcontainers.containers.MongoDBContainer; import org.testcontainers.containers.MongoDBContainer;
import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails; import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory; import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource; import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
@ -59,6 +60,11 @@ class MongoContainerConnectionDetailsFactory
return new ConnectionString(getContainer().getReplicaSetUrl()); return new ConnectionString(getContainer().getReplicaSetUrl());
} }
@Override
public SslBundle getSslBundle() {
return super.getSslBundle();
}
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2024 the original author or authors. * Copyright 2012-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -25,6 +25,8 @@ import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.PemKeyStore;
import org.springframework.boot.testcontainers.service.connection.PemTrustStore;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.boot.testsupport.container.TestImage; import org.springframework.boot.testsupport.container.TestImage;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate; import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
@ -37,14 +39,13 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Scott Frederick * @author Scott Frederick
*/ */
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
@SpringBootTest(properties = { "spring.data.mongodb.ssl.bundle=client", @SpringBootTest
"spring.ssl.bundle.pem.client.keystore.certificate=classpath:ssl/test-client.crt",
"spring.ssl.bundle.pem.client.keystore.private-key=classpath:ssl/test-client.key",
"spring.ssl.bundle.pem.client.truststore.certificate=classpath:ssl/test-ca.crt" })
class SampleMongoApplicationReactiveSslTests { class SampleMongoApplicationReactiveSslTests {
@Container @Container
@ServiceConnection @ServiceConnection
@PemKeyStore(certificate = "classpath:ssl/test-client.crt", privateKey = "classpath:ssl/test-client.key")
@PemTrustStore("classpath:ssl/test-ca.crt")
static final MongoDBContainer mongoDb = TestImage.container(SecureMongoContainer.class); static final MongoDBContainer mongoDb = TestImage.container(SecureMongoContainer.class);
@Autowired @Autowired

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2024 the original author or authors. * Copyright 2012-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,6 +23,8 @@ import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.boot.testcontainers.service.connection.PemKeyStore;
import org.springframework.boot.testcontainers.service.connection.PemTrustStore;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.boot.testsupport.container.TestImage; import org.springframework.boot.testsupport.container.TestImage;
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.MongoTemplate;
@ -36,14 +38,13 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Eddú Meléndez * @author Eddú Meléndez
*/ */
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
@DataMongoTest(properties = { "spring.data.mongodb.ssl.bundle=client", @DataMongoTest
"spring.ssl.bundle.pem.client.keystore.certificate=classpath:ssl/test-client.crt",
"spring.ssl.bundle.pem.client.keystore.private-key=classpath:ssl/test-client.key",
"spring.ssl.bundle.pem.client.truststore.certificate=classpath:ssl/test-ca.crt" })
class SampleMongoApplicationSslTests { class SampleMongoApplicationSslTests {
@Container @Container
@ServiceConnection @ServiceConnection
@PemKeyStore(certificate = "classpath:ssl/test-client.crt", privateKey = "classpath:ssl/test-client.key")
@PemTrustStore("classpath:ssl/test-ca.crt")
static final MongoDBContainer mongoDb = TestImage.container(SecureMongoContainer.class); static final MongoDBContainer mongoDb = TestImage.container(SecureMongoContainer.class);
@Autowired @Autowired