From 109fd6f97da95ab78d20cbaddb2ca817e647cd1c Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Tue, 11 Feb 2025 10:20:32 +0100 Subject: [PATCH] Add SSL service connection support for MongoDB See gh-41137 --- .../mongo/MongoDataAutoConfiguration.java | 9 ++- .../mongo/MongoAutoConfiguration.java | 13 ++-- .../mongo/MongoConnectionDetails.java | 12 +++- .../mongo/MongoReactiveAutoConfiguration.java | 13 ++-- .../PropertiesMongoConnectionDetails.java | 24 ++++++- ...dMongoClientSettingsBuilderCustomizer.java | 46 +++++++++++-- .../mongo/MongoAutoConfigurationTests.java | 4 +- .../MongoReactiveAutoConfigurationTests.java | 4 +- ...PropertiesMongoConnectionDetailsTests.java | 67 ++++++++++++++----- ...ongoContainerConnectionDetailsFactory.java | 8 ++- ...ampleMongoApplicationReactiveSslTests.java | 11 +-- .../mongo/SampleMongoApplicationSslTests.java | 11 +-- 12 files changed, 167 insertions(+), 55 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfiguration.java index db1ac8876e..b14f931f52 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfiguration.java @@ -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"); * 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 org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 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.PropertiesMongoConnectionDetails; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.ssl.SslBundles; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.data.mongodb.core.MongoTemplate; @@ -59,8 +61,9 @@ public class MongoDataAutoConfiguration { @Bean @ConditionalOnMissingBean(MongoConnectionDetails.class) - PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties) { - return new PropertiesMongoConnectionDetails(properties); + PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties, + ObjectProvider sslBundles) { + return new PropertiesMongoConnectionDetails(properties, sslBundles.getIfAvailable()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java index 5905b2f224..d510c77dd8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java @@ -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"); * you may not use this file except in compliance with the License. @@ -48,8 +48,9 @@ public class MongoAutoConfiguration { @Bean @ConditionalOnMissingBean(MongoConnectionDetails.class) - PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties) { - return new PropertiesMongoConnectionDetails(properties); + PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties, + ObjectProvider sslBundles) { + return new PropertiesMongoConnectionDetails(properties, sslBundles.getIfAvailable()); } @Bean @@ -70,9 +71,9 @@ public class MongoAutoConfiguration { @Bean StandardMongoClientSettingsBuilderCustomizer standardMongoSettingsCustomizer(MongoProperties properties, - MongoConnectionDetails connectionDetails, ObjectProvider sslBundles) { - return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails.getConnectionString(), - properties.getUuidRepresentation(), properties.getSsl(), sslBundles.getIfAvailable()); + MongoConnectionDetails connectionDetails) { + return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails, + properties.getUuidRepresentation()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoConnectionDetails.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoConnectionDetails.java index bfd107505a..c625cc2a4b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoConnectionDetails.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoConnectionDetails.java @@ -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"); * 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 org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; +import org.springframework.boot.ssl.SslBundle; /** * Details required to establish a connection to a MongoDB service. @@ -36,6 +37,15 @@ public interface MongoConnectionDetails extends ConnectionDetails { */ ConnectionString getConnectionString(); + /** + * SSL bundle to use. + * @return the SSL bundle to use + * @since 3.5.0 + */ + default SslBundle getSslBundle() { + return null; + } + /** * GridFS configuration. * @return the GridFS configuration or {@code null} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java index 746bbe4587..2c3864907d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java @@ -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"); * you may not use this file except in compliance with the License. @@ -53,8 +53,9 @@ public class MongoReactiveAutoConfiguration { @Bean @ConditionalOnMissingBean(MongoConnectionDetails.class) - PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties) { - return new PropertiesMongoConnectionDetails(properties); + PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties, + ObjectProvider sslBundles) { + return new PropertiesMongoConnectionDetails(properties, sslBundles.getIfAvailable()); } @Bean @@ -77,9 +78,9 @@ public class MongoReactiveAutoConfiguration { @Bean StandardMongoClientSettingsBuilderCustomizer standardMongoSettingsCustomizer(MongoProperties properties, - MongoConnectionDetails connectionDetails, ObjectProvider sslBundles) { - return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails.getConnectionString(), - properties.getUuidRepresentation(), properties.getSsl(), sslBundles.getIfAvailable()); + MongoConnectionDetails connectionDetails) { + return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails, + properties.getUuidRepresentation()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetails.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetails.java index 04436c717b..ba8405fd8c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetails.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetails.java @@ -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"); * 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 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; /** @@ -38,8 +42,11 @@ public class PropertiesMongoConnectionDetails implements MongoConnectionDetails private final MongoProperties properties; - public PropertiesMongoConnectionDetails(MongoProperties properties) { + private final SslBundles sslBundles; + + public PropertiesMongoConnectionDetails(MongoProperties properties, SslBundles sslBundles) { this.properties = properties; + this.sslBundles = sslBundles; } @Override @@ -90,6 +97,19 @@ public class PropertiesMongoConnectionDetails implements MongoConnectionDetails 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 getOptions() { List options = new ArrayList<>(); if (StringUtils.hasText(this.properties.getReplicaSetName())) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/StandardMongoClientSettingsBuilderCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/StandardMongoClientSettingsBuilderCustomizer.java index 12e604491d..01d770a418 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/StandardMongoClientSettingsBuilderCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/StandardMongoClientSettingsBuilderCustomizer.java @@ -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"); * 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 MongoConnectionDetails connectionDetails; + private final MongoProperties.Ssl ssl; private final SslBundles sslBundles; 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, UuidRepresentation uuidRepresentation, MongoProperties.Ssl ssl, SslBundles sslBundles) { + this.connectionDetails = null; this.connectionString = connectionString; this.uuidRepresentation = uuidRepresentation; this.ssl = ssl; 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 public void customize(MongoClientSettings.Builder settingsBuilder) { settingsBuilder.uuidRepresentation(this.uuidRepresentation); - settingsBuilder.applyConnectionString(this.connectionString); - if (this.ssl.isEnabled()) { - settingsBuilder.applyToSslSettings(this::configureSsl); + if (this.connectionDetails != null) { + settingsBuilder.applyConnectionString(this.connectionDetails.getConnectionString()); + 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 public int getOrder() { return this.order; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfigurationTests.java index b2122ebecc..8667698a0f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfigurationTests.java @@ -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"); * 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) -> { SslSettings sslSettings = getSettings(context).getSslSettings(); assertThat(sslSettings.isEnabled()).isTrue(); - assertThat(sslSettings.getContext()).isNull(); + assertThat(sslSettings.getContext()).isNotNull(); }); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfigurationTests.java index a3225f4fea..54d81db709 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfigurationTests.java @@ -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"); * 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) -> { SslSettings sslSettings = getSettings(context).getSslSettings(); assertThat(sslSettings.isEnabled()).isTrue(); - assertThat(sslSettings.getContext()).isNull(); + assertThat(sslSettings.getContext()).isNotNull(); }); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetailsTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetailsTests.java index 0b529d6e33..6412a1b832 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetailsTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetailsTests.java @@ -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"); * 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 com.mongodb.ConnectionString; +import org.junit.jupiter.api.BeforeEach; 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.mockito.Mockito.mock; /** * Tests for {@link PropertiesMongoConnectionDetails}. * * @author Christoph Dreis * @author Scott Frederick + * @author Moritz Halbritter */ 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 void credentialsCanBeConfiguredWithUsername() { this.properties.setUsername("user"); - ConnectionString connectionString = getConnectionString(); + ConnectionString connectionString = this.connectionDetails.getConnectionString(); assertThat(connectionString.getUsername()).isEqualTo("user"); assertThat(connectionString.getPassword()).isEmpty(); assertThat(connectionString.getCredential().getUserName()).isEqualTo("user"); @@ -48,7 +65,7 @@ class PropertiesMongoConnectionDetailsTests { void credentialsCanBeConfiguredWithUsernameAndPassword() { this.properties.setUsername("user"); this.properties.setPassword("secret".toCharArray()); - ConnectionString connectionString = getConnectionString(); + ConnectionString connectionString = this.connectionDetails.getConnectionString(); assertThat(connectionString.getUsername()).isEqualTo("user"); assertThat(connectionString.getPassword()).isEqualTo("secret".toCharArray()); assertThat(connectionString.getCredential().getUserName()).isEqualTo("user"); @@ -59,13 +76,13 @@ class PropertiesMongoConnectionDetailsTests { @Test void databaseCanBeConfigured() { this.properties.setDatabase("db"); - ConnectionString connectionString = getConnectionString(); + ConnectionString connectionString = this.connectionDetails.getConnectionString(); assertThat(connectionString.getDatabase()).isEqualTo("db"); } @Test void databaseHasDefaultWhenNotConfigured() { - ConnectionString connectionString = getConnectionString(); + ConnectionString connectionString = this.connectionDetails.getConnectionString(); assertThat(connectionString.getDatabase()).isEqualTo("test"); } @@ -74,7 +91,7 @@ class PropertiesMongoConnectionDetailsTests { this.properties.setUsername("user"); this.properties.setDatabase("db"); this.properties.setAuthenticationDatabase("authdb"); - ConnectionString connectionString = getConnectionString(); + ConnectionString connectionString = this.connectionDetails.getConnectionString(); assertThat(connectionString.getDatabase()).isEqualTo("db"); assertThat(connectionString.getCredential().getSource()).isEqualTo("authdb"); assertThat(connectionString.getCredential().getUserName()).isEqualTo("user"); @@ -83,14 +100,14 @@ class PropertiesMongoConnectionDetailsTests { @Test void authenticationDatabaseIsNotConfiguredWhenUsernameIsNotConfigured() { this.properties.setAuthenticationDatabase("authdb"); - ConnectionString connectionString = getConnectionString(); + ConnectionString connectionString = this.connectionDetails.getConnectionString(); assertThat(connectionString.getCredential()).isNull(); } @Test void replicaSetCanBeConfigured() { this.properties.setReplicaSetName("test"); - ConnectionString connectionString = getConnectionString(); + ConnectionString connectionString = this.connectionDetails.getConnectionString(); assertThat(connectionString.getRequiredReplicaSetName()).isEqualTo("test"); } @@ -99,7 +116,7 @@ class PropertiesMongoConnectionDetailsTests { this.properties.setUsername("user"); this.properties.setDatabase("db"); this.properties.setReplicaSetName("test"); - ConnectionString connectionString = getConnectionString(); + ConnectionString connectionString = this.connectionDetails.getConnectionString(); assertThat(connectionString.getDatabase()).isEqualTo("db"); assertThat(connectionString.getRequiredReplicaSetName()).isEqualTo("test"); } @@ -107,14 +124,14 @@ class PropertiesMongoConnectionDetailsTests { @Test void replicaSetCanBeNull() { this.properties.setReplicaSetName(null); - ConnectionString connectionString = getConnectionString(); + ConnectionString connectionString = this.connectionDetails.getConnectionString(); assertThat(connectionString.getRequiredReplicaSetName()).isNull(); } @Test void replicaSetCanBeBlank() { this.properties.setReplicaSetName(""); - ConnectionString connectionString = getConnectionString(); + ConnectionString connectionString = this.connectionDetails.getConnectionString(); assertThat(connectionString.getRequiredReplicaSetName()).isNull(); } @@ -122,18 +139,32 @@ class PropertiesMongoConnectionDetailsTests { void whenAdditionalHostsAreConfiguredThenTheyAreIncludedInHostsOfConnectionString() { this.properties.setHost("mongo1.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", "mongo3.example.com"); } - private PropertiesMongoConnectionDetails createConnectionDetails() { - return new PropertiesMongoConnectionDetails(this.properties); + @Test + 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() { - PropertiesMongoConnectionDetails connectionDetails = createConnectionDetails(); - return connectionDetails.getConnectionString(); + @Test + void shouldReturnSystemDefaultBundleIfSslIsEnabledButBundleNotSet() { + 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(); } } diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/mongo/MongoContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/mongo/MongoContainerConnectionDetailsFactory.java index 619f42b271..90173be4ef 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/mongo/MongoContainerConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/mongo/MongoContainerConnectionDetailsFactory.java @@ -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"); * 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.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.ContainerConnectionSource; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; @@ -59,6 +60,11 @@ class MongoContainerConnectionDetailsFactory return new ConnectionString(getContainer().getReplicaSetUrl()); } + @Override + public SslBundle getSslBundle() { + return super.getSslBundle(); + } + } } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-mongo/src/dockerTest/java/smoketest/data/mongo/SampleMongoApplicationReactiveSslTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-mongo/src/dockerTest/java/smoketest/data/mongo/SampleMongoApplicationReactiveSslTests.java index c5a184b90d..ad696f5d54 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-mongo/src/dockerTest/java/smoketest/data/mongo/SampleMongoApplicationReactiveSslTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-mongo/src/dockerTest/java/smoketest/data/mongo/SampleMongoApplicationReactiveSslTests.java @@ -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"); * 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.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.testsupport.container.TestImage; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; @@ -37,14 +39,13 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Scott Frederick */ @Testcontainers(disabledWithoutDocker = true) -@SpringBootTest(properties = { "spring.data.mongodb.ssl.bundle=client", - "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" }) +@SpringBootTest class SampleMongoApplicationReactiveSslTests { @Container @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); @Autowired diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-mongo/src/dockerTest/java/smoketest/data/mongo/SampleMongoApplicationSslTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-mongo/src/dockerTest/java/smoketest/data/mongo/SampleMongoApplicationSslTests.java index 150e2efbb5..1ed16744c4 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-mongo/src/dockerTest/java/smoketest/data/mongo/SampleMongoApplicationSslTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-mongo/src/dockerTest/java/smoketest/data/mongo/SampleMongoApplicationSslTests.java @@ -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"); * 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.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.testsupport.container.TestImage; import org.springframework.data.mongodb.core.MongoTemplate; @@ -36,14 +38,13 @@ import static org.assertj.core.api.Assertions.assertThat; * @author EddĂș MelĂ©ndez */ @Testcontainers(disabledWithoutDocker = true) -@DataMongoTest(properties = { "spring.data.mongodb.ssl.bundle=client", - "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" }) +@DataMongoTest class SampleMongoApplicationSslTests { @Container @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); @Autowired