diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfiguration.java index c4605e9c10..74d5807659 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfiguration.java @@ -20,6 +20,7 @@ import java.io.InputStream; import java.util.HashMap; import java.util.Map; +import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import com.unboundid.ldap.listener.InMemoryDirectoryServer; @@ -33,7 +34,6 @@ import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration; import org.springframework.boot.autoconfigure.ldap.LdapProperties; import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapProperties.Credential; @@ -50,6 +50,7 @@ import org.springframework.core.env.PropertySource; import org.springframework.core.io.Resource; import org.springframework.ldap.core.ContextSource; import org.springframework.ldap.core.support.LdapContextSource; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -64,7 +65,6 @@ import org.springframework.util.StringUtils; @EnableConfigurationProperties({ LdapProperties.class, EmbeddedLdapProperties.class }) @AutoConfigureBefore(LdapAutoConfiguration.class) @ConditionalOnClass(InMemoryDirectoryServer.class) -@ConditionalOnProperty(prefix = "spring.ldap.embedded", name = "base-dn") public class EmbeddedLdapAutoConfiguration { private static final String PROPERTY_SOURCE_NAME = "ldap.ports"; @@ -88,6 +88,11 @@ public class EmbeddedLdapAutoConfiguration { this.environment = environment; } + @PostConstruct + public void validateBaseDns() { + Assert.notEmpty(this.embeddedProperties.getBaseDn(), "No baseDn found."); + } + @Bean @DependsOn("directoryServer") @ConditionalOnMissingBean diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapProperties.java index 6b82c7a94f..a42bd12069 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapProperties.java @@ -40,9 +40,9 @@ public class EmbeddedLdapProperties { private Credential credential = new Credential(); /** - * The base DN. + * List of base DN. */ - private String baseDn; + private String[] baseDn = new String[0]; /** * Schema (LDIF) script resource reference. @@ -70,11 +70,11 @@ public class EmbeddedLdapProperties { this.credential = credential; } - public String getBaseDn() { + public String[] getBaseDn() { return this.baseDn; } - public void setBaseDn(String baseDn) { + public void setBaseDn(String[] baseDn) { this.baseDn = baseDn; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfigurationTests.java index aa0cda4895..b07364fbd1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfigurationTests.java @@ -50,8 +50,10 @@ public class EmbeddedLdapAutoConfigurationTests { @Test public void testSetDefaultPort() { - this.contextRunner.withPropertyValues("spring.ldap.embedded.port:1234", - "spring.ldap.embedded.base-dn:dc=spring,dc=org").run(context -> { + this.contextRunner + .withPropertyValues("spring.ldap.embedded.port:1234", + "spring.ldap.embedded.base-dn[0]:dc=spring,dc=org") + .run(context -> { InMemoryDirectoryServer server = context .getBean(InMemoryDirectoryServer.class); assertThat(server.getListenPort()).isEqualTo(1234); @@ -61,7 +63,7 @@ public class EmbeddedLdapAutoConfigurationTests { @Test public void testRandomPortWithEnvironment() { this.contextRunner - .withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org") + .withPropertyValues("spring.ldap.embedded.base-dn[0]:dc=spring,dc=org") .run(context -> { InMemoryDirectoryServer server = context .getBean(InMemoryDirectoryServer.class); @@ -73,7 +75,7 @@ public class EmbeddedLdapAutoConfigurationTests { @Test public void testRandomPortWithValueAnnotation() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - TestPropertyValues.of("spring.ldap.embedded.base-dn:dc=spring,dc=org") + TestPropertyValues.of("spring.ldap.embedded.base-dn[0]:dc=spring,dc=org") .applyTo(context); context.register(EmbeddedLdapAutoConfiguration.class, LdapClientConfiguration.class, @@ -87,7 +89,7 @@ public class EmbeddedLdapAutoConfigurationTests { @Test public void testSetCredentials() { this.contextRunner - .withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org", + .withPropertyValues("spring.ldap.embedded.base-dn[0]:dc=spring,dc=org", "spring.ldap.embedded.credential.username:uid=root", "spring.ldap.embedded.credential.password:boot") .run(context -> { @@ -101,7 +103,7 @@ public class EmbeddedLdapAutoConfigurationTests { @Test public void testSetPartitionSuffix() { this.contextRunner - .withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org") + .withPropertyValues("spring.ldap.embedded.base-dn[0]:dc=spring,dc=org") .run(context -> { InMemoryDirectoryServer server = context .getBean(InMemoryDirectoryServer.class); @@ -113,7 +115,7 @@ public class EmbeddedLdapAutoConfigurationTests { @Test public void testSetLdifFile() { this.contextRunner - .withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org") + .withPropertyValues("spring.ldap.embedded.base-dn[0]:dc=spring,dc=org") .run(context -> { InMemoryDirectoryServer server = context .getBean(InMemoryDirectoryServer.class); @@ -126,7 +128,7 @@ public class EmbeddedLdapAutoConfigurationTests { @Test public void testQueryEmbeddedLdap() { this.contextRunner - .withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org") + .withPropertyValues("spring.ldap.embedded.base-dn[0]:dc=spring,dc=org") .withConfiguration(AutoConfigurations.of(LdapAutoConfiguration.class, LdapDataAutoConfiguration.class)) .run(context -> { @@ -142,7 +144,7 @@ public class EmbeddedLdapAutoConfigurationTests { public void testDisableSchemaValidation() { this.contextRunner .withPropertyValues("spring.ldap.embedded.validation.enabled:false", - "spring.ldap.embedded.base-dn:dc=spring,dc=org") + "spring.ldap.embedded.base-dn[0]:dc=spring,dc=org") .run(context -> { InMemoryDirectoryServer server = context .getBean(InMemoryDirectoryServer.class); @@ -152,10 +154,12 @@ public class EmbeddedLdapAutoConfigurationTests { @Test public void testCustomSchemaValidation() { - this.contextRunner.withPropertyValues( - "spring.ldap.embedded.validation.schema:classpath:custom-schema.ldif", - "spring.ldap.embedded.ldif:classpath:custom-schema-sample.ldif", - "spring.ldap.embedded.base-dn:dc=spring,dc=org").run(context -> { + this.contextRunner + .withPropertyValues( + "spring.ldap.embedded.validation.schema:classpath:custom-schema.ldif", + "spring.ldap.embedded.ldif:classpath:custom-schema-sample.ldif", + "spring.ldap.embedded.base-dn[0]:dc=spring,dc=org") + .run(context -> { InMemoryDirectoryServer server = context .getBean(InMemoryDirectoryServer.class); @@ -167,6 +171,24 @@ public class EmbeddedLdapAutoConfigurationTests { }); } + @Test + public void testMultiBaseDn() { + this.contextRunner + .withPropertyValues( + "spring.ldap.embedded.ldif:classpath:schema-multi-basedn.ldif", + "spring.ldap.embedded.base-dn[0]:dc=spring,dc=org", + "spring.ldap.embedded.base-dn[1]:dc=pivotal,dc=io") + .run(context -> { + InMemoryDirectoryServer server = context + .getBean(InMemoryDirectoryServer.class); + assertThat(server + .countEntriesBelow("ou=company1,c=Sweden,dc=spring,dc=org")) + .isEqualTo(5); + assertThat(server.countEntriesBelow("c=Sweden,dc=pivotal,dc=io")) + .isEqualTo(2); + }); + } + @Configuration static class LdapClientConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/schema-multi-basedn.ldif b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/schema-multi-basedn.ldif new file mode 100644 index 0000000000..1bf396af5b --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/schema-multi-basedn.ldif @@ -0,0 +1,114 @@ +dn: dc=spring,dc=org +objectclass: top +objectclass: domain +objectclass: extensibleObject +dc: spring + +dn: ou=groups,dc=spring,dc=org +objectclass: top +objectclass: organizationalUnit +ou: groups + +dn: cn=ROLE_USER,ou=groups,dc=spring,dc=org +objectclass: top +objectclass: groupOfUniqueNames +cn: ROLE_USER +uniqueMember: cn=Some Person,ou=company1,c=Sweden,dc=spring,dc=org +uniqueMember: cn=Some Person2,ou=company1,c=Sweden,dc=spring,dc=org +uniqueMember: cn=Some Person,ou=company1,c=Sweden,dc=spring,dc=org +uniqueMember: cn=Some Person3,ou=company1,c=Sweden,dc=spring,dc=org + +dn: cn=ROLE_ADMIN,ou=groups,dc=spring,dc=org +objectclass: top +objectclass: groupOfUniqueNames +cn: ROLE_ADMIN +uniqueMember: cn=Some Person2,ou=company1,c=Sweden,dc=spring,dc=org + +dn: c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: country +c: Sweden +description: The country of Sweden + +dn: ou=company1,c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: organizationalUnit +ou: company1 +description: First company in Sweden + +dn: cn=Some Person,ou=company1,c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person +userPassword: password +cn: Some Person +sn: Person +description: Sweden, Company1, Some Person +telephoneNumber: +46 555-123456 + +dn: cn=Some Person2,ou=company1,c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person2 +userPassword: password +cn: Some Person2 +sn: Person2 +description: Sweden, Company1, Some Person2 +telephoneNumber: +46 555-654321 + +dn: cn=Some Person3,ou=company1,c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person3 +userPassword: password +cn: Some Person3 +sn: Person3 +description: Sweden, Company1, Some Person3 +telephoneNumber: +46 555-123654 + +dn: cn=Some Person4,ou=company1,c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person4 +userPassword: password +cn: Some Person +sn: Person +description: Sweden, Company1, Some Person +telephoneNumber: +46 555-456321 + +dn: dc=pivotal,dc=io +objectclass: top +objectclass: domain +objectclass: extensibleObject +dc: pivotal + +dn: ou=groups,dc=pivotal,dc=io +objectclass: top +objectclass: organizationalUnit +ou: groups + +dn: c=Sweden,dc=pivotal,dc=io +objectclass: top +objectclass: country +c: Sweden +description:The country of Sweden + +dn: cn=Some Random Person,c=Sweden,dc=pivotal,dc=io +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.random.person +userPassword: password +cn: Some Random Person +sn: Person +description: Sweden, Pivotal, Some Random Person +telephoneNumber: +46 555-123456 diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 955f1e5a7b..95b5532f76 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -4417,6 +4417,8 @@ follows: spring.ldap.embedded.base-dn=dc=spring,dc=io ---- +WARNING: `spring.ldap.embedded.base-dn` supports multi base DN, so it must define as follows `spring.ldap.embedded.base-dn[0]=dc=spring,dc=io` + By default, the server starts on a random port and triggers the regular LDAP support. There is no need to specify a `spring.ldap.urls` property.