From eb940c390777939329b979693f929add6d7dbc1b Mon Sep 17 00:00:00 2001 From: PhilKes Date: Sun, 21 Jan 2024 15:07:37 +0100 Subject: [PATCH 1/2] Add Docker Compose service connection support for OpenLDAP See gh-39258 --- .../ldap/LdapAutoConfiguration.java | 17 +++- .../ldap/LdapConnectionDetails.java | 59 +++++++++++ .../ldap/PropertiesLdapConnectionDetails.java | 58 +++++++++++ .../ldap/LdapAutoConfigurationTests.java | 51 ++++++++++ ...DockerComposeConnectionDetailsFactory.java | 99 +++++++++++++++++++ .../service/connection/ldap/package-info.java | 20 ++++ .../main/resources/META-INF/spring.factories | 1 + ...nectionDetailsFactoryIntegrationTests.java | 48 +++++++++ .../service/connection/ldap/ldap-compose.yaml | 11 +++ .../spring-boot-testcontainers/build.gradle | 1 + ...LdapContainerConnectionDetailsFactory.java | 90 +++++++++++++++++ .../service/connection/ldap/package-info.java | 20 ++++ .../main/resources/META-INF/spring.factories | 1 + ...nectionDetailsFactoryIntegrationTests.java | 76 ++++++++++++++ .../testcontainers/DockerImageNames.java | 10 ++ .../testcontainers/LdapContainer.java | 35 +++++++ 16 files changed, 592 insertions(+), 5 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapConnectionDetails.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/PropertiesLdapConnectionDetails.java create mode 100644 spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/LdapDockerComposeConnectionDetailsFactory.java create mode 100644 spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/package-info.java create mode 100644 spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/ldap/LdapDockerComposeConnectionDetailsFactoryIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-docker-compose/src/test/resources/org/springframework/boot/docker/compose/service/connection/ldap/ldap-compose.yaml create mode 100644 spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ldap/LdapContainerConnectionDetailsFactory.java create mode 100644 spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ldap/package-info.java create mode 100644 spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/ldap/LdapContainerConnectionDetailsFactoryIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/LdapContainer.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java index 0144f9bf99..0bd691445d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java @@ -46,18 +46,25 @@ import org.springframework.ldap.core.support.LdapContextSource; @EnableConfigurationProperties(LdapProperties.class) public class LdapAutoConfiguration { + @Bean + @ConditionalOnMissingBean(LdapConnectionDetails.class) + PropertiesLdapConnectionDetails propertiesLdapConnectionDetails(LdapProperties properties, + Environment environment) { + return new PropertiesLdapConnectionDetails(properties, environment); + } + @Bean @ConditionalOnMissingBean - public LdapContextSource ldapContextSource(LdapProperties properties, Environment environment, + public LdapContextSource ldapContextSource(LdapConnectionDetails connectionDetails, LdapProperties properties, ObjectProvider dirContextAuthenticationStrategy) { LdapContextSource source = new LdapContextSource(); dirContextAuthenticationStrategy.ifUnique(source::setAuthenticationStrategy); PropertyMapper propertyMapper = PropertyMapper.get().alwaysApplyingWhenNonNull(); - propertyMapper.from(properties.getUsername()).to(source::setUserDn); - propertyMapper.from(properties.getPassword()).to(source::setPassword); + propertyMapper.from(connectionDetails.getUsername()).to(source::setUserDn); + propertyMapper.from(connectionDetails.getPassword()).to(source::setPassword); propertyMapper.from(properties.getAnonymousReadOnly()).to(source::setAnonymousReadOnly); - propertyMapper.from(properties.getBase()).to(source::setBase); - propertyMapper.from(properties.determineUrls(environment)).to(source::setUrls); + propertyMapper.from(connectionDetails.getBase()).to(source::setBase); + propertyMapper.from(connectionDetails.getUrls()).to(source::setUrls); propertyMapper.from(properties.getBaseEnvironment()) .to((baseEnvironment) -> source.setBaseEnvironmentProperties(Collections.unmodifiableMap(baseEnvironment))); return source; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapConnectionDetails.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapConnectionDetails.java new file mode 100644 index 0000000000..9e034221ce --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapConnectionDetails.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2024 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.ldap; + +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; + +/** + * Details required to establish a connection to a Ldap service. + * + * @author Philipp Kessler + * @since 3.3.0 + */ +public interface LdapConnectionDetails extends ConnectionDetails { + + /** + * LDAP URLs of the server. + * @return list of the LDAP urls to use + */ + String[] getUrls(); + + /** + * Base suffix from which all operations should originate. + * @return base suffix from which all operations should originate or null. + */ + default String getBase() { + return null; + } + + /** + * Login username of the server. + * @return login username of the server or null. + */ + default String getUsername() { + return null; + } + + /** + * Login password of the server. + * @return login password of the server or null. + */ + default String getPassword() { + return null; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/PropertiesLdapConnectionDetails.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/PropertiesLdapConnectionDetails.java new file mode 100644 index 0000000000..517adc8116 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/PropertiesLdapConnectionDetails.java @@ -0,0 +1,58 @@ +/* + * Copyright 2012-2024 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.ldap; + +import org.springframework.core.env.Environment; + +/** + * Adapts {@link LdapProperties} to {@link LdapConnectionDetails}. + * + * @author Philipp Kessler + * @since 3.3.0 + */ +public class PropertiesLdapConnectionDetails implements LdapConnectionDetails { + + private final LdapProperties properties; + + private final Environment environment; + + PropertiesLdapConnectionDetails(LdapProperties properties, Environment environment) { + this.properties = properties; + this.environment = environment; + } + + @Override + public String[] getUrls() { + return this.properties.determineUrls(this.environment); + } + + @Override + public String getBase() { + return this.properties.getBase(); + } + + @Override + public String getUsername() { + return this.properties.getUsername(); + } + + @Override + public String getPassword() { + return this.properties.getPassword(); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java index bde394cc67..b4b9f8154a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java @@ -29,6 +29,7 @@ import org.springframework.ldap.core.support.LdapContextSource; import org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy; import org.springframework.ldap.pool2.factory.PoolConfig; import org.springframework.ldap.pool2.factory.PooledContextSource; +import org.springframework.ldap.support.LdapUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -112,6 +113,25 @@ class LdapAutoConfigurationTests { }); } + @Test + void definesPropertiesBasedConnectionDetailsByDefault() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(PropertiesLdapConnectionDetails.class)); + } + + @Test + void shouldUseCustomConnectionDetailsWhenDefined() { + this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(LdapContextSource.class) + .hasSingleBean(LdapConnectionDetails.class) + .doesNotHaveBean(PropertiesLdapConnectionDetails.class); + LdapContextSource contextSource = context.getBean(LdapContextSource.class); + assertThat(contextSource.getUrls()).isEqualTo(new String[] { "ldaps://ldap.example.com" }); + assertThat(contextSource.getBaseLdapName()).isEqualTo(LdapUtils.newLdapName("dc=base")); + assertThat(contextSource.getUserDn()).isEqualTo("ldap-user"); + assertThat(contextSource.getPassword()).isEqualTo("ldap-password"); + }); + } + @Test void templateExists() { this.contextRunner.withPropertyValues("spring.ldap.urls:ldap://localhost:389").run((context) -> { @@ -174,6 +194,37 @@ class LdapAutoConfigurationTests { }); } + @Configuration(proxyBeanMethods = false) + static class ConnectionDetailsConfiguration { + + @Bean + LdapConnectionDetails ldapConnectionDetails() { + return new LdapConnectionDetails() { + + @Override + public String[] getUrls() { + return new String[] { "ldaps://ldap.example.com" }; + } + + @Override + public String getBase() { + return "dc=base"; + } + + @Override + public String getUsername() { + return "ldap-user"; + } + + @Override + public String getPassword() { + return "ldap-password"; + } + }; + } + + } + @Configuration(proxyBeanMethods = false) static class PooledContextSourceConfig { diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/LdapDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/LdapDockerComposeConnectionDetailsFactory.java new file mode 100644 index 0000000000..59850f2228 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/LdapDockerComposeConnectionDetailsFactory.java @@ -0,0 +1,99 @@ +/* + * Copyright 2012-2024 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.docker.compose.service.connection.ldap; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +import org.springframework.boot.autoconfigure.ldap.LdapConnectionDetails; +import org.springframework.boot.docker.compose.core.RunningService; +import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory; +import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource; + +/** + * {@link DockerComposeConnectionDetailsFactory} to create {@link LdapConnectionDetails} + * for an {@code ldap} service. + * + * @author Philipp Kessler + */ +class LdapDockerComposeConnectionDetailsFactory extends DockerComposeConnectionDetailsFactory { + + protected LdapDockerComposeConnectionDetailsFactory() { + super("osixia/openldap"); + } + + @Override + protected LdapConnectionDetails getDockerComposeConnectionDetails(DockerComposeConnectionSource source) { + return new LdapDockerComposeConnectionDetails(source.getRunningService()); + } + + /** + * {@link LdapConnectionDetails} backed by an {@code openldap} {@link RunningService}. + */ + static class LdapDockerComposeConnectionDetails extends DockerComposeConnectionDetails + implements LdapConnectionDetails { + + private final String[] urls; + + private final String base; + + private final String username; + + private final String password; + + LdapDockerComposeConnectionDetails(RunningService service) { + super(service); + Map env = service.env(); + boolean usesTls = Boolean.parseBoolean(env.getOrDefault("LDAP_TLS", "true")); + String ldapPort = usesTls ? env.getOrDefault("LDAPS_PORT", "636") : env.getOrDefault("LDAP_PORT", "389"); + this.urls = new String[] { "%s://%s:%d".formatted(usesTls ? "ldaps" : "ldap", service.host(), + service.ports().get(Integer.parseInt(ldapPort))) }; + String baseDn = env.getOrDefault("LDAP_BASE_DN", null); + if (baseDn == null) { + baseDn = Arrays.stream(env.getOrDefault("LDAP_DOMAIN", "example.org").split("\\.")) + .map("dc=%s"::formatted) + .collect(Collectors.joining(",")); + } + this.base = baseDn; + this.password = env.getOrDefault("LDAP_ADMIN_PASSWORD", "admin"); + this.username = "cn=admin,%s".formatted(this.base); + } + + @Override + public String[] getUrls() { + return this.urls; + } + + @Override + public String getBase() { + return this.base; + } + + @Override + public String getUsername() { + return this.username; + } + + @Override + public String getPassword() { + return this.password; + } + + } + +} diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/package-info.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/package-info.java new file mode 100644 index 0000000000..1953e3f3b9 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2024 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Auto-configuration for docker compose Ldap service connections. + */ +package org.springframework.boot.docker.compose.service.connection.ldap; diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-docker-compose/src/main/resources/META-INF/spring.factories index f00fc19381..71a9417c44 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-docker-compose/src/main/resources/META-INF/spring.factories @@ -9,6 +9,7 @@ org.springframework.boot.docker.compose.service.connection.activemq.ActiveMQDock org.springframework.boot.docker.compose.service.connection.cassandra.CassandraDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.elasticsearch.ElasticsearchDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.flyway.JdbcAdaptingFlywayConnectionDetailsFactory,\ +org.springframework.boot.docker.compose.service.connection.ldap.LdapDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.liquibase.JdbcAdaptingLiquibaseConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.mariadb.MariaDbJdbcDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.mariadb.MariaDbR2dbcDockerComposeConnectionDetailsFactory,\ diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/ldap/LdapDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/ldap/LdapDockerComposeConnectionDetailsFactoryIntegrationTests.java new file mode 100644 index 0000000000..150e3e11d2 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/ldap/LdapDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012-2024 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.docker.compose.service.connection.ldap; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.ldap.LdapConnectionDetails; +import org.springframework.boot.docker.compose.service.connection.test.AbstractDockerComposeIntegrationTests; +import org.springframework.boot.testsupport.testcontainers.DockerImageNames; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link LdapDockerComposeConnectionDetailsFactory}. + * + * @author Philipp Kessler + */ +class LdapDockerComposeConnectionDetailsFactoryIntegrationTests extends AbstractDockerComposeIntegrationTests { + + LdapDockerComposeConnectionDetailsFactoryIntegrationTests() { + super("ldap-compose.yaml", DockerImageNames.ldap()); + } + + @Test + void runCreatesConnectionDetails() { + LdapConnectionDetails connectionDetails = run(LdapConnectionDetails.class); + assertThat(connectionDetails.getUsername()).isEqualTo("cn=admin,dc=ldap,dc=example,dc=org"); + assertThat(connectionDetails.getPassword()).isEqualTo("somepassword"); + assertThat(connectionDetails.getBase()).isEqualTo("dc=ldap,dc=example,dc=org"); + assertThat(connectionDetails.getUrls()).hasSize(1); + assertThat(connectionDetails.getUrls()[0]).startsWith("ldaps://"); + } + +} diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/resources/org/springframework/boot/docker/compose/service/connection/ldap/ldap-compose.yaml b/spring-boot-project/spring-boot-docker-compose/src/test/resources/org/springframework/boot/docker/compose/service/connection/ldap/ldap-compose.yaml new file mode 100644 index 0000000000..a55e16be43 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/test/resources/org/springframework/boot/docker/compose/service/connection/ldap/ldap-compose.yaml @@ -0,0 +1,11 @@ +services: + ldap: + image: '{imageName}' + environment: + - 'LDAP_DOMAIN=ldap.example.org' + - 'LDAP_ADMIN_PASSWORD=somepassword' + - 'LDAP_TLS=true' + hostname: ldap + ports: + - "389" + - "636" diff --git a/spring-boot-project/spring-boot-testcontainers/build.gradle b/spring-boot-project/spring-boot-testcontainers/build.gradle index 7d7792b8ad..28b6221321 100644 --- a/spring-boot-project/spring-boot-testcontainers/build.gradle +++ b/spring-boot-project/spring-boot-testcontainers/build.gradle @@ -57,6 +57,7 @@ dependencies { testImplementation("org.springframework:spring-r2dbc") testImplementation("org.springframework.amqp:spring-rabbit") testImplementation("org.springframework.kafka:spring-kafka") + testImplementation("org.springframework.ldap:spring-ldap-core") testImplementation("org.springframework.pulsar:spring-pulsar") testImplementation("org.testcontainers:junit-jupiter") diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ldap/LdapContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ldap/LdapContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..a708347bdf --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ldap/LdapContainerConnectionDetailsFactory.java @@ -0,0 +1,90 @@ +/* + * Copyright 2012-2024 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.testcontainers.service.connection.ldap; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +import org.testcontainers.containers.Container; +import org.testcontainers.containers.GenericContainer; + +import org.springframework.boot.autoconfigure.ldap.LdapConnectionDetails; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; + +/** + * {@link ContainerConnectionDetailsFactory} to create {@link LdapConnectionDetails} from + * a {@link ServiceConnection @ServiceConnection}-annotated {@link GenericContainer} using + * the {@code "osixia/openldap"} image. + * + * @author Philipp Kessler + */ +class LdapContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory, LdapConnectionDetails> { + + LdapContainerConnectionDetailsFactory() { + super("osixia/openldap"); + } + + @Override + protected LdapConnectionDetails getContainerConnectionDetails(ContainerConnectionSource> source) { + return new LdapContainerConnectionDetailsFactory.LdapContainerConnectionDetails(source); + } + + private static final class LdapContainerConnectionDetails extends ContainerConnectionDetails> + implements LdapConnectionDetails { + + private LdapContainerConnectionDetails(ContainerConnectionSource> source) { + super(source); + } + + @Override + public String[] getUrls() { + Map env = getContainer().getEnvMap(); + boolean usesTls = Boolean.parseBoolean(env.getOrDefault("LDAP_TLS", "true")); + String ldapPort = usesTls ? env.getOrDefault("LDAPS_PORT", "636") : env.getOrDefault("LDAP_PORT", "389"); + return new String[] { "%s://%s:%d".formatted(usesTls ? "ldaps" : "ldap", getContainer().getHost(), + getContainer().getMappedPort(Integer.parseInt(ldapPort))) }; + } + + @Override + public String getBase() { + String baseDn = getContainer().getEnvMap().getOrDefault("LDAP_BASE_DN", null); + if (baseDn == null) { + baseDn = Arrays + .stream(getContainer().getEnvMap().getOrDefault("LDAP_DOMAIN", "example.org").split("\\.")) + .map("dc=%s"::formatted) + .collect(Collectors.joining(",")); + } + return baseDn; + } + + @Override + public String getUsername() { + return "cn=admin,%s".formatted(getBase()); + } + + @Override + public String getPassword() { + return getContainer().getEnvMap().getOrDefault("LDAP_ADMIN_PASSWORD", "admin"); + } + + } + +} diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ldap/package-info.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ldap/package-info.java new file mode 100644 index 0000000000..0de36b5469 --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ldap/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2024 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Support for testcontainers Ldap service connections. + */ +package org.springframework.boot.testcontainers.service.connection.ldap; diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories index 386f2a1a35..a8851d15b9 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories @@ -16,6 +16,7 @@ org.springframework.boot.testcontainers.service.connection.flyway.FlywayContaine org.springframework.boot.testcontainers.service.connection.elasticsearch.ElasticsearchContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.jdbc.JdbcContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.kafka.KafkaContainerConnectionDetailsFactory,\ +org.springframework.boot.testcontainers.service.connection.ldap.LdapContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.liquibase.LiquibaseContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.mongo.MongoContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.neo4j.Neo4jContainerConnectionDetailsFactory,\ diff --git a/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/ldap/LdapContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/ldap/LdapContainerConnectionDetailsFactoryIntegrationTests.java new file mode 100644 index 0000000000..f44c47afff --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/ldap/LdapContainerConnectionDetailsFactoryIntegrationTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2024 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.testcontainers.service.connection.ldap; + +import java.util.List; + +import javax.naming.NamingException; +import javax.naming.directory.Attributes; + +import org.junit.jupiter.api.Test; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.boot.testsupport.testcontainers.LdapContainer; +import org.springframework.context.annotation.Configuration; +import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.query.LdapQueryBuilder; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link LdapContainerConnectionDetailsFactory}. + * + * @author Philipp Kessler + */ +@SpringJUnitConfig +@Testcontainers(disabledWithoutDocker = true) +class LdapContainerConnectionDetailsFactoryIntegrationTests { + + @Container + @ServiceConnection + static final LdapContainer openLdap = new LdapContainer().withEnv("LDAP_TLS", "false"); + + @Autowired + private LdapTemplate ldapTemplate; + + @Test + void connectionCanBeMadeToLdapContainer() { + List cn = this.ldapTemplate.search(LdapQueryBuilder.query().where("objectclass").is("dcObject"), + new AttributesMapper() { + @Override + public String mapFromAttributes(Attributes attributes) throws NamingException { + return attributes.get("dc").get().toString(); + } + }); + assertThat(cn).hasSize(1); + assertThat(cn.get(0)).isEqualTo("example"); + } + + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration({ LdapAutoConfiguration.class }) + static class TestConfiguration { + + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java index 4bb98d2201..19ee2b3d6e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java @@ -40,6 +40,8 @@ public final class DockerImageNames { private static final String KAFKA_VERSION = "7.4.0"; + private static final String LDAP_VERSION = "1.5.0"; + private static final String MARIADB_VERSION = "10.10"; private static final String MONGO_VERSION = "5.0.17"; @@ -119,6 +121,14 @@ public final class DockerImageNames { return DockerImageName.parse("confluentinc/cp-kafka").withTag(KAFKA_VERSION); } + /** + * Return a {@link DockerImageName} suitable for running OpenLdap. + * @return a docker image name for running OpenLdap + */ + public static DockerImageName ldap() { + return DockerImageName.parse("osixia/openldap").withTag(LDAP_VERSION); + } + /** * Return a {@link DockerImageName} suitable for running MariaDB. * @return a docker image name for running MariaDB diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/LdapContainer.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/LdapContainer.java new file mode 100644 index 0000000000..3405e138ed --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/LdapContainer.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012-2024 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.testsupport.testcontainers; + +import org.testcontainers.containers.GenericContainer; + +/** + * A {@link GenericContainer} for OpenLdap. + * + * @author Philipp Kessler + */ +public class LdapContainer extends GenericContainer { + + private static final int DEFAULT_LDAP_PORT = 389; + + public LdapContainer() { + super(DockerImageNames.ldap()); + addExposedPorts(DEFAULT_LDAP_PORT); + } + +} From bee6fe899c16b1bae911bf7e84e3aa78089fc80f Mon Sep 17 00:00:00 2001 From: Scott Frederick Date: Mon, 22 Jan 2024 16:33:59 -0600 Subject: [PATCH 2/2] Polish "Add Docker Compose service connection support for OpenLDAP" See gh-39258 --- .../ldap/LdapAutoConfiguration.java | 2 +- .../ldap/LdapConnectionDetails.java | 10 ++++---- .../ldap/LdapAutoConfigurationTests.java | 4 ++-- ...ockerComposeConnectionDetailsFactory.java} | 20 ++++++++-------- .../service/connection/ldap/package-info.java | 2 +- .../main/resources/META-INF/spring.factories | 2 +- ...ectionDetailsFactoryIntegrationTests.java} | 8 +++---- .../asciidoc/features/docker-compose.adoc | 3 +++ ...dapContainerConnectionDetailsFactory.java} | 23 +++++++++---------- .../main/resources/META-INF/spring.factories | 2 +- ...ectionDetailsFactoryIntegrationTests.java} | 18 ++++----------- .../testcontainers/DockerImageNames.java | 14 +++++------ ...pContainer.java => OpenLdapContainer.java} | 8 +++---- 13 files changed, 56 insertions(+), 60 deletions(-) rename spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/{LdapDockerComposeConnectionDetailsFactory.java => OpenLdapDockerComposeConnectionDetailsFactory.java} (80%) rename spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/ldap/{LdapDockerComposeConnectionDetailsFactoryIntegrationTests.java => OpenLdapDockerComposeConnectionDetailsFactoryIntegrationTests.java} (83%) rename spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ldap/{LdapContainerConnectionDetailsFactory.java => OpenLdapContainerConnectionDetailsFactory.java} (78%) rename spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/ldap/{LdapContainerConnectionDetailsFactoryIntegrationTests.java => OpenLdapContainerConnectionDetailsFactoryIntegrationTests.java} (78%) rename spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/{LdapContainer.java => OpenLdapContainer.java} (82%) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java index 0bd691445d..205af4c3e0 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapConnectionDetails.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapConnectionDetails.java index 9e034221ce..efec546594 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapConnectionDetails.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapConnectionDetails.java @@ -19,7 +19,7 @@ package org.springframework.boot.autoconfigure.ldap; import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; /** - * Details required to establish a connection to a Ldap service. + * Details required to establish a connection to an LDAP service. * * @author Philipp Kessler * @since 3.3.0 @@ -28,13 +28,13 @@ public interface LdapConnectionDetails extends ConnectionDetails { /** * LDAP URLs of the server. - * @return list of the LDAP urls to use + * @return the LDAP URLs to use */ String[] getUrls(); /** * Base suffix from which all operations should originate. - * @return base suffix from which all operations should originate or null. + * @return base suffix */ default String getBase() { return null; @@ -42,7 +42,7 @@ public interface LdapConnectionDetails extends ConnectionDetails { /** * Login username of the server. - * @return login username of the server or null. + * @return login username */ default String getUsername() { return null; @@ -50,7 +50,7 @@ public interface LdapConnectionDetails extends ConnectionDetails { /** * Login password of the server. - * @return login password of the server or null. + * @return login password */ default String getPassword() { return null; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java index b4b9f8154a..2d51f84a9b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -119,7 +119,7 @@ class LdapAutoConfigurationTests { } @Test - void shouldUseCustomConnectionDetailsWhenDefined() { + void usesCustomConnectionDetailsWhenDefined() { this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class).run((context) -> { assertThat(context).hasSingleBean(LdapContextSource.class) .hasSingleBean(LdapConnectionDetails.class) diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/LdapDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/OpenLdapDockerComposeConnectionDetailsFactory.java similarity index 80% rename from spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/LdapDockerComposeConnectionDetailsFactory.java rename to spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/OpenLdapDockerComposeConnectionDetailsFactory.java index 59850f2228..b5674f152f 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/LdapDockerComposeConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/OpenLdapDockerComposeConnectionDetailsFactory.java @@ -31,21 +31,22 @@ import org.springframework.boot.docker.compose.service.connection.DockerComposeC * * @author Philipp Kessler */ -class LdapDockerComposeConnectionDetailsFactory extends DockerComposeConnectionDetailsFactory { +class OpenLdapDockerComposeConnectionDetailsFactory + extends DockerComposeConnectionDetailsFactory { - protected LdapDockerComposeConnectionDetailsFactory() { + protected OpenLdapDockerComposeConnectionDetailsFactory() { super("osixia/openldap"); } @Override protected LdapConnectionDetails getDockerComposeConnectionDetails(DockerComposeConnectionSource source) { - return new LdapDockerComposeConnectionDetails(source.getRunningService()); + return new OpenLdapDockerComposeConnectionDetails(source.getRunningService()); } /** * {@link LdapConnectionDetails} backed by an {@code openldap} {@link RunningService}. */ - static class LdapDockerComposeConnectionDetails extends DockerComposeConnectionDetails + static class OpenLdapDockerComposeConnectionDetails extends DockerComposeConnectionDetails implements LdapConnectionDetails { private final String[] urls; @@ -56,20 +57,21 @@ class LdapDockerComposeConnectionDetailsFactory extends DockerComposeConnectionD private final String password; - LdapDockerComposeConnectionDetails(RunningService service) { + OpenLdapDockerComposeConnectionDetails(RunningService service) { super(service); Map env = service.env(); boolean usesTls = Boolean.parseBoolean(env.getOrDefault("LDAP_TLS", "true")); String ldapPort = usesTls ? env.getOrDefault("LDAPS_PORT", "636") : env.getOrDefault("LDAP_PORT", "389"); this.urls = new String[] { "%s://%s:%d".formatted(usesTls ? "ldaps" : "ldap", service.host(), service.ports().get(Integer.parseInt(ldapPort))) }; - String baseDn = env.getOrDefault("LDAP_BASE_DN", null); - if (baseDn == null) { - baseDn = Arrays.stream(env.getOrDefault("LDAP_DOMAIN", "example.org").split("\\.")) + if (env.containsKey("LDAP_BASE_DN")) { + this.base = env.get("LDAP_BASE_DN"); + } + else { + this.base = Arrays.stream(env.getOrDefault("LDAP_DOMAIN", "example.org").split("\\.")) .map("dc=%s"::formatted) .collect(Collectors.joining(",")); } - this.base = baseDn; this.password = env.getOrDefault("LDAP_ADMIN_PASSWORD", "admin"); this.username = "cn=admin,%s".formatted(this.base); } diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/package-info.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/package-info.java index 1953e3f3b9..489148d277 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/package-info.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ldap/package-info.java @@ -15,6 +15,6 @@ */ /** - * Auto-configuration for docker compose Ldap service connections. + * Auto-configuration for Docker Compose LDAP service connections. */ package org.springframework.boot.docker.compose.service.connection.ldap; diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-docker-compose/src/main/resources/META-INF/spring.factories index 71a9417c44..3ffd311e36 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-docker-compose/src/main/resources/META-INF/spring.factories @@ -9,7 +9,7 @@ org.springframework.boot.docker.compose.service.connection.activemq.ActiveMQDock org.springframework.boot.docker.compose.service.connection.cassandra.CassandraDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.elasticsearch.ElasticsearchDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.flyway.JdbcAdaptingFlywayConnectionDetailsFactory,\ -org.springframework.boot.docker.compose.service.connection.ldap.LdapDockerComposeConnectionDetailsFactory,\ +org.springframework.boot.docker.compose.service.connection.ldap.OpenLdapDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.liquibase.JdbcAdaptingLiquibaseConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.mariadb.MariaDbJdbcDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.mariadb.MariaDbR2dbcDockerComposeConnectionDetailsFactory,\ diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/ldap/LdapDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/ldap/OpenLdapDockerComposeConnectionDetailsFactoryIntegrationTests.java similarity index 83% rename from spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/ldap/LdapDockerComposeConnectionDetailsFactoryIntegrationTests.java rename to spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/ldap/OpenLdapDockerComposeConnectionDetailsFactoryIntegrationTests.java index 150e3e11d2..57756fe012 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/ldap/LdapDockerComposeConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/ldap/OpenLdapDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -25,14 +25,14 @@ import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import static org.assertj.core.api.Assertions.assertThat; /** - * Integration tests for {@link LdapDockerComposeConnectionDetailsFactory}. + * Integration tests for {@link OpenLdapDockerComposeConnectionDetailsFactory}. * * @author Philipp Kessler */ -class LdapDockerComposeConnectionDetailsFactoryIntegrationTests extends AbstractDockerComposeIntegrationTests { +class OpenLdapDockerComposeConnectionDetailsFactoryIntegrationTests extends AbstractDockerComposeIntegrationTests { - LdapDockerComposeConnectionDetailsFactoryIntegrationTests() { - super("ldap-compose.yaml", DockerImageNames.ldap()); + OpenLdapDockerComposeConnectionDetailsFactoryIntegrationTests() { + super("ldap-compose.yaml", DockerImageNames.openLdap()); } @Test diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/docker-compose.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/docker-compose.adoc index 6ee9c0172a..6b8c5688e2 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/docker-compose.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/docker-compose.adoc @@ -81,6 +81,9 @@ The following service connections are currently supported: | `JdbcConnectionDetails` | Containers named "gvenzl/oracle-free", "gvenzl/oracle-xe", "mariadb", "mssql/server", "mysql", or "postgres" +| `LdapConnectionDetails` +| Containers named "osixia/openldap" + | `MongoConnectionDetails` | Containers named "mongo" diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ldap/LdapContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ldap/OpenLdapContainerConnectionDetailsFactory.java similarity index 78% rename from spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ldap/LdapContainerConnectionDetailsFactory.java rename to spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ldap/OpenLdapContainerConnectionDetailsFactory.java index a708347bdf..fd752811cd 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ldap/LdapContainerConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ldap/OpenLdapContainerConnectionDetailsFactory.java @@ -35,22 +35,22 @@ import org.springframework.boot.testcontainers.service.connection.ServiceConnect * * @author Philipp Kessler */ -class LdapContainerConnectionDetailsFactory +class OpenLdapContainerConnectionDetailsFactory extends ContainerConnectionDetailsFactory, LdapConnectionDetails> { - LdapContainerConnectionDetailsFactory() { + OpenLdapContainerConnectionDetailsFactory() { super("osixia/openldap"); } @Override protected LdapConnectionDetails getContainerConnectionDetails(ContainerConnectionSource> source) { - return new LdapContainerConnectionDetailsFactory.LdapContainerConnectionDetails(source); + return new OpenLdapContainerConnectionDetails(source); } - private static final class LdapContainerConnectionDetails extends ContainerConnectionDetails> + private static final class OpenLdapContainerConnectionDetails extends ContainerConnectionDetails> implements LdapConnectionDetails { - private LdapContainerConnectionDetails(ContainerConnectionSource> source) { + private OpenLdapContainerConnectionDetails(ContainerConnectionSource> source) { super(source); } @@ -65,14 +65,13 @@ class LdapContainerConnectionDetailsFactory @Override public String getBase() { - String baseDn = getContainer().getEnvMap().getOrDefault("LDAP_BASE_DN", null); - if (baseDn == null) { - baseDn = Arrays - .stream(getContainer().getEnvMap().getOrDefault("LDAP_DOMAIN", "example.org").split("\\.")) - .map("dc=%s"::formatted) - .collect(Collectors.joining(",")); + Map env = getContainer().getEnvMap(); + if (env.containsKey("LDAP_BASE_DN")) { + return env.get("LDAP_BASE_DN"); } - return baseDn; + return Arrays.stream(env.getOrDefault("LDAP_DOMAIN", "example.org").split("\\.")) + .map("dc=%s"::formatted) + .collect(Collectors.joining(",")); } @Override diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories index a8851d15b9..5b8bda347f 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories @@ -16,7 +16,7 @@ org.springframework.boot.testcontainers.service.connection.flyway.FlywayContaine org.springframework.boot.testcontainers.service.connection.elasticsearch.ElasticsearchContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.jdbc.JdbcContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.kafka.KafkaContainerConnectionDetailsFactory,\ -org.springframework.boot.testcontainers.service.connection.ldap.LdapContainerConnectionDetailsFactory,\ +org.springframework.boot.testcontainers.service.connection.ldap.OpenLdapContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.liquibase.LiquibaseContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.mongo.MongoContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.neo4j.Neo4jContainerConnectionDetailsFactory,\ diff --git a/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/ldap/LdapContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/ldap/OpenLdapContainerConnectionDetailsFactoryIntegrationTests.java similarity index 78% rename from spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/ldap/LdapContainerConnectionDetailsFactoryIntegrationTests.java rename to spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/ldap/OpenLdapContainerConnectionDetailsFactoryIntegrationTests.java index f44c47afff..4bae83e11b 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/ldap/LdapContainerConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/ldap/OpenLdapContainerConnectionDetailsFactoryIntegrationTests.java @@ -18,9 +18,6 @@ package org.springframework.boot.testcontainers.service.connection.ldap; import java.util.List; -import javax.naming.NamingException; -import javax.naming.directory.Attributes; - import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -29,7 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; -import org.springframework.boot.testsupport.testcontainers.LdapContainer; +import org.springframework.boot.testsupport.testcontainers.OpenLdapContainer; import org.springframework.context.annotation.Configuration; import org.springframework.ldap.core.AttributesMapper; import org.springframework.ldap.core.LdapTemplate; @@ -39,17 +36,17 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link LdapContainerConnectionDetailsFactory}. + * Tests for {@link OpenLdapContainerConnectionDetailsFactory}. * * @author Philipp Kessler */ @SpringJUnitConfig @Testcontainers(disabledWithoutDocker = true) -class LdapContainerConnectionDetailsFactoryIntegrationTests { +class OpenLdapContainerConnectionDetailsFactoryIntegrationTests { @Container @ServiceConnection - static final LdapContainer openLdap = new LdapContainer().withEnv("LDAP_TLS", "false"); + static final OpenLdapContainer openLdap = new OpenLdapContainer().withEnv("LDAP_TLS", "false"); @Autowired private LdapTemplate ldapTemplate; @@ -57,12 +54,7 @@ class LdapContainerConnectionDetailsFactoryIntegrationTests { @Test void connectionCanBeMadeToLdapContainer() { List cn = this.ldapTemplate.search(LdapQueryBuilder.query().where("objectclass").is("dcObject"), - new AttributesMapper() { - @Override - public String mapFromAttributes(Attributes attributes) throws NamingException { - return attributes.get("dc").get().toString(); - } - }); + (AttributesMapper) (attributes) -> attributes.get("dc").get().toString()); assertThat(cn).hasSize(1); assertThat(cn.get(0)).isEqualTo("example"); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java index 19ee2b3d6e..81a5673d8f 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -40,8 +40,6 @@ public final class DockerImageNames { private static final String KAFKA_VERSION = "7.4.0"; - private static final String LDAP_VERSION = "1.5.0"; - private static final String MARIADB_VERSION = "10.10"; private static final String MONGO_VERSION = "5.0.17"; @@ -50,6 +48,8 @@ public final class DockerImageNames { private static final String NEO4J_VERSION = "4.4.11"; + private static final String OPEN_LDAP_VERSION = "1.5.0"; + private static final String ORACLE_FREE_VERSION = "23.3-slim"; private static final String ORACLE_XE_VERSION = "18.4.0-slim"; @@ -122,11 +122,11 @@ public final class DockerImageNames { } /** - * Return a {@link DockerImageName} suitable for running OpenLdap. - * @return a docker image name for running OpenLdap + * Return a {@link DockerImageName} suitable for running OpenLDAP. + * @return a docker image name for running OpenLDAP */ - public static DockerImageName ldap() { - return DockerImageName.parse("osixia/openldap").withTag(LDAP_VERSION); + public static DockerImageName openLdap() { + return DockerImageName.parse("osixia/openldap").withTag(OPEN_LDAP_VERSION); } /** diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/LdapContainer.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/OpenLdapContainer.java similarity index 82% rename from spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/LdapContainer.java rename to spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/OpenLdapContainer.java index 3405e138ed..93ac6e8abf 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/LdapContainer.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/OpenLdapContainer.java @@ -19,16 +19,16 @@ package org.springframework.boot.testsupport.testcontainers; import org.testcontainers.containers.GenericContainer; /** - * A {@link GenericContainer} for OpenLdap. + * A {@link GenericContainer} for OpenLDAP. * * @author Philipp Kessler */ -public class LdapContainer extends GenericContainer { +public class OpenLdapContainer extends GenericContainer { private static final int DEFAULT_LDAP_PORT = 389; - public LdapContainer() { - super(DockerImageNames.ldap()); + public OpenLdapContainer() { + super(DockerImageNames.openLdap()); addExposedPorts(DEFAULT_LDAP_PORT); }