Merge pull request #11764 from eddumelendez

* pr/11764:
  Polish 'Add support for multi baseDn;
  Add support for multi baseDn
  Migrate LDAP tests to use ApplicationContextRunner
This commit is contained in:
Phillip Webb 2018-02-04 10:46:21 -08:00
commit b8e220c4f4
5 changed files with 260 additions and 91 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -33,7 +33,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 +49,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 +64,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";
@ -86,6 +85,7 @@ public class EmbeddedLdapAutoConfiguration {
this.properties = properties;
this.applicationContext = applicationContext;
this.environment = environment;
Assert.notEmpty(this.embeddedProperties.getBaseDn(), "No baseDn found.");
}
@Bean
@ -103,8 +103,8 @@ public class EmbeddedLdapAutoConfiguration {
@Bean
public InMemoryDirectoryServer directoryServer() throws LDAPException {
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(
this.embeddedProperties.getBaseDn());
String[] baseDn = this.embeddedProperties.getBaseDn().toArray(new String[0]);
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(baseDn);
if (hasCredentials(this.embeddedProperties.getCredential())) {
config.addAdditionalBindCredentials(
this.embeddedProperties.getCredential().getUsername(),

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -16,7 +16,11 @@
package org.springframework.boot.autoconfigure.ldap.embedded;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.convert.Delimiter;
import org.springframework.core.io.Resource;
/**
@ -40,9 +44,10 @@ public class EmbeddedLdapProperties {
private Credential credential = new Credential();
/**
* The base DN.
* List of base DN.
*/
private String baseDn;
@Delimiter(Delimiter.NONE)
private List<String> baseDn = new ArrayList<>();
/**
* Schema (LDIF) script resource reference.
@ -70,11 +75,11 @@ public class EmbeddedLdapProperties {
this.credential = credential;
}
public String getBaseDn() {
public List<String> getBaseDn() {
return this.baseDn;
}
public void setBaseDn(String baseDn) {
public void setBaseDn(List<String> baseDn) {
this.baseDn = baseDn;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -21,14 +21,14 @@ import com.unboundid.ldap.sdk.BindResult;
import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration;
import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
@ -44,121 +44,145 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public class EmbeddedLdapAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@Before
public void setup() {
this.context = new AnnotationConfigApplicationContext();
}
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(EmbeddedLdapAutoConfiguration.class));
@Test
public void testSetDefaultPort() {
load("spring.ldap.embedded.port:1234",
"spring.ldap.embedded.base-dn:dc=spring,dc=org");
InMemoryDirectoryServer server = this.context
.getBean(InMemoryDirectoryServer.class);
assertThat(server.getListenPort()).isEqualTo(1234);
this.contextRunner.withPropertyValues("spring.ldap.embedded.port:1234",
"spring.ldap.embedded.base-dn:dc=spring,dc=org").run(context -> {
InMemoryDirectoryServer server = context
.getBean(InMemoryDirectoryServer.class);
assertThat(server.getListenPort()).isEqualTo(1234);
});
}
@Test
public void testRandomPortWithEnvironment() {
load("spring.ldap.embedded.base-dn:dc=spring,dc=org");
InMemoryDirectoryServer server = this.context
.getBean(InMemoryDirectoryServer.class);
assertThat(server.getListenPort()).isEqualTo(this.context.getEnvironment()
.getProperty("local.ldap.port", Integer.class));
this.contextRunner
.withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org")
.run(context -> {
InMemoryDirectoryServer server = context
.getBean(InMemoryDirectoryServer.class);
assertThat(server.getListenPort()).isEqualTo(context.getEnvironment()
.getProperty("local.ldap.port", Integer.class));
});
}
@Test
public void testRandomPortWithValueAnnotation() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
TestPropertyValues.of("spring.ldap.embedded.base-dn:dc=spring,dc=org")
.applyTo(this.context);
this.context.register(EmbeddedLdapAutoConfiguration.class,
.applyTo(context);
context.register(EmbeddedLdapAutoConfiguration.class,
LdapClientConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
LDAPConnection connection = this.context.getBean(LDAPConnection.class);
assertThat(connection.getConnectedPort()).isEqualTo(this.context.getEnvironment()
.getProperty("local.ldap.port", Integer.class));
context.refresh();
LDAPConnection connection = context.getBean(LDAPConnection.class);
assertThat(connection.getConnectedPort()).isEqualTo(
context.getEnvironment().getProperty("local.ldap.port", Integer.class));
}
@Test
public void testSetCredentials() throws LDAPException {
load("spring.ldap.embedded.base-dn:dc=spring,dc=org",
"spring.ldap.embedded.credential.username:uid=root",
"spring.ldap.embedded.credential.password:boot");
InMemoryDirectoryServer server = this.context
.getBean(InMemoryDirectoryServer.class);
BindResult result = server.bind("uid=root", "boot");
assertThat(result).isNotNull();
public void testSetCredentials() {
this.contextRunner
.withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org",
"spring.ldap.embedded.credential.username:uid=root",
"spring.ldap.embedded.credential.password:boot")
.run(context -> {
InMemoryDirectoryServer server = context
.getBean(InMemoryDirectoryServer.class);
BindResult result = server.bind("uid=root", "boot");
assertThat(result).isNotNull();
});
}
@Test
public void testSetPartitionSuffix() throws LDAPException {
load("spring.ldap.embedded.base-dn:dc=spring,dc=org");
InMemoryDirectoryServer server = this.context
.getBean(InMemoryDirectoryServer.class);
assertThat(server.getBaseDNs()).containsExactly(new DN("dc=spring,dc=org"));
public void testSetPartitionSuffix() {
this.contextRunner
.withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org")
.run(context -> {
InMemoryDirectoryServer server = context
.getBean(InMemoryDirectoryServer.class);
assertThat(server.getBaseDNs())
.containsExactly(new DN("dc=spring,dc=org"));
});
}
@Test
public void testSetLdifFile() throws LDAPException {
load("spring.ldap.embedded.base-dn:dc=spring,dc=org");
InMemoryDirectoryServer server = this.context
.getBean(InMemoryDirectoryServer.class);
assertThat(server.countEntriesBelow("ou=company1,c=Sweden,dc=spring,dc=org"))
.isEqualTo(5);
public void testSetLdifFile() {
this.contextRunner
.withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org")
.run(context -> {
InMemoryDirectoryServer server = context
.getBean(InMemoryDirectoryServer.class);
assertThat(server
.countEntriesBelow("ou=company1,c=Sweden,dc=spring,dc=org"))
.isEqualTo(5);
});
}
@Test
public void testQueryEmbeddedLdap() {
TestPropertyValues.of("spring.ldap.embedded.base-dn:dc=spring,dc=org")
.applyTo(this.context);
this.context.register(EmbeddedLdapAutoConfiguration.class,
LdapAutoConfiguration.class, LdapDataAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBeanNamesForType(LdapTemplate.class).length)
.isEqualTo(1);
LdapTemplate ldapTemplate = this.context.getBean(LdapTemplate.class);
assertThat(ldapTemplate.list("ou=company1,c=Sweden,dc=spring,dc=org")).hasSize(4);
this.contextRunner
.withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org")
.withConfiguration(AutoConfigurations.of(LdapAutoConfiguration.class,
LdapDataAutoConfiguration.class))
.run(context -> {
assertThat(context.getBeanNamesForType(LdapTemplate.class).length)
.isEqualTo(1);
LdapTemplate ldapTemplate = context.getBean(LdapTemplate.class);
assertThat(ldapTemplate.list("ou=company1,c=Sweden,dc=spring,dc=org"))
.hasSize(4);
});
}
@Test
public void testDisableSchemaValidation() throws LDAPException {
load("spring.ldap.embedded.validation.enabled:false",
"spring.ldap.embedded.base-dn:dc=spring,dc=org");
InMemoryDirectoryServer server = this.context
.getBean(InMemoryDirectoryServer.class);
assertThat(server.getSchema()).isNull();
public void testDisableSchemaValidation() {
this.contextRunner
.withPropertyValues("spring.ldap.embedded.validation.enabled:false",
"spring.ldap.embedded.base-dn:dc=spring,dc=org")
.run(context -> {
InMemoryDirectoryServer server = context
.getBean(InMemoryDirectoryServer.class);
assertThat(server.getSchema()).isNull();
});
}
@Test
public void testCustomSchemaValidation() throws LDAPException {
load("spring.ldap.embedded.validation.schema:classpath:custom-schema.ldif",
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");
InMemoryDirectoryServer server = this.context
.getBean(InMemoryDirectoryServer.class);
"spring.ldap.embedded.base-dn:dc=spring,dc=org").run(context -> {
InMemoryDirectoryServer server = context
.getBean(InMemoryDirectoryServer.class);
assertThat(server.getSchema().getObjectClass("exampleAuxiliaryClass"))
.isNotNull();
assertThat(server.getSchema().getAttributeType("exampleAttributeName"))
.isNotNull();
assertThat(server.getSchema().getObjectClass("exampleAuxiliaryClass"))
.isNotNull();
assertThat(
server.getSchema().getAttributeType("exampleAttributeName"))
.isNotNull();
});
}
private void load(String... properties) {
TestPropertyValues.of(properties).applyTo(this.context);
this.context.register(EmbeddedLdapAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
@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

View File

@ -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

View File

@ -4417,6 +4417,32 @@ follows:
spring.ldap.embedded.base-dn=dc=spring,dc=io
----
[NOTE]
====
It is possible to define multiple base-dn values, however, since distinguished names
usually contain commas, they must be defined using the correct notation.
In yaml files, you can use the yaml list notation:
[source,yaml,indent=0]
----
spring.ldap.embedded.base-dn:
- dc=spring,dc=io
- dc=pivotal,dc=io
----
in properties files, you must include the index as part of the property name:
[source,properties,indent=0]
----
spring.ldap.embedded.base-dn[0]=dc=spring,dc=io
spring.ldap.embedded.base-dn[1]=dc=pivotal,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.