EmbeddedLdapServer builder
Closes #938 Signed-off-by: Emanuel Trandafir <emanueltrandafir1993@gmail.com> Signed-off-by: etrandafir93 <emanueltrandafir1993@gmail.com>
This commit is contained in:
committed by
Josh Cummings
parent
f154e64760
commit
8d44ee31a8
@@ -182,17 +182,19 @@ testCompile "com.unboundid:unboundid-ldapsdk:3.1.1"
|
||||
The following bean definition creates an embedded LDAP server:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
[source,java]
|
||||
----
|
||||
<bean id="embeddedLdapServer" class="org.springframework.ldap.test.unboundid.EmbeddedLdapServerFactoryBean">
|
||||
<property name="partitionName" value="example"/>
|
||||
<property name="partitionSuffix" value="dc=261consulting,dc=com" />
|
||||
<property name="port" value="9321" />
|
||||
</bean>
|
||||
@Bean
|
||||
EmbeddedLdapServer embeddedLdapServer() {
|
||||
return EmbeddedLdapServer.withPartitionSuffix("dc=jayway,dc=se")
|
||||
.partitionName("jayway")
|
||||
.port(18881)
|
||||
.configurationCustomizer((config) -> config.setCodeLogDetails(tempLogFile, true))
|
||||
.build();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
`spring-ldap-test` provides a way to populate the LDAP server by using `org.springframework.ldap.test.unboundid.LdifPopulator`. To use it, create a bean similar to the following:
|
||||
Alternatively, you can use the `org.springframework.ldap.test.unboundid.LdifPopulator` to create and populate the LDAP server. To use it, create a bean similar to the following:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
|
||||
package org.springframework.ldap.test.unboundid;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.naming.InvalidNameException;
|
||||
import javax.naming.ldap.LdapName;
|
||||
import javax.naming.ldap.Rdn;
|
||||
|
||||
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
|
||||
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
|
||||
import com.unboundid.ldap.listener.InMemoryListenerConfig;
|
||||
@@ -24,6 +31,7 @@ import com.unboundid.ldap.sdk.Entry;
|
||||
import com.unboundid.ldap.sdk.LDAPException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Helper class for embedded Unboundid ldap server.
|
||||
@@ -46,26 +54,29 @@ public final class EmbeddedLdapServer implements AutoCloseable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and starts new embedded LDAP server.
|
||||
* Creates a new {@link Builder} with a given partition suffix.
|
||||
*
|
||||
* @since 3.3
|
||||
*/
|
||||
public static Builder withPartitionSuffix(String partitionSuffix) {
|
||||
return new Builder(partitionSuffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and starts new embedded LDAP server.
|
||||
* @deprecated Use the builder pattern exposed via
|
||||
* {@link #withPartitionSuffix(String)} instead.
|
||||
*/
|
||||
@Deprecated(since = "3.3")
|
||||
public static EmbeddedLdapServer newEmbeddedServer(String defaultPartitionName, String defaultPartitionSuffix,
|
||||
int port) throws Exception {
|
||||
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(defaultPartitionSuffix);
|
||||
config.addAdditionalBindCredentials("uid=admin,ou=system", "secret");
|
||||
int port) {
|
||||
EmbeddedLdapServer server = EmbeddedLdapServer.withPartitionSuffix(defaultPartitionSuffix)
|
||||
.partitionName(defaultPartitionName)
|
||||
.port(port)
|
||||
.build();
|
||||
|
||||
config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("LDAP", port));
|
||||
|
||||
config.setEnforceSingleStructuralObjectClass(false);
|
||||
config.setEnforceAttributeSyntaxCompliance(true);
|
||||
|
||||
Entry entry = new Entry(new DN(defaultPartitionSuffix));
|
||||
entry.addAttribute("objectClass", "top", "domain", "extensibleObject");
|
||||
entry.addAttribute("dc", defaultPartitionName);
|
||||
|
||||
InMemoryDirectoryServer directoryServer = new InMemoryDirectoryServer(config);
|
||||
directoryServer.add(entry);
|
||||
directoryServer.startListening();
|
||||
return new EmbeddedLdapServer(directoryServer);
|
||||
server.start();
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,4 +113,119 @@ public final class EmbeddedLdapServer implements AutoCloseable {
|
||||
this.directoryServer.shutDown(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for embedded Unboundid ldap server.
|
||||
*
|
||||
* @author Emanuel Trandafir
|
||||
* @since 3.3
|
||||
*/
|
||||
public static final class Builder {
|
||||
|
||||
private final String partitionSuffix;
|
||||
|
||||
private String partitionName;
|
||||
|
||||
private int port = 0;
|
||||
|
||||
private Consumer<InMemoryDirectoryServerConfig> configurationCustomizer = (__) -> {
|
||||
};
|
||||
|
||||
private Builder(String partitionSuffix) {
|
||||
this.partitionSuffix = partitionSuffix;
|
||||
this.partitionName = leftMostElement(partitionSuffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the port for the embedded LDAP server.
|
||||
* @param port the port for the embedded LDAP server. Defaults to 0 in which case
|
||||
* the server should automatically choose an available port.
|
||||
* @return this {@link Builder} instance.
|
||||
*/
|
||||
public Builder port(int port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a customizer for the {@link InMemoryDirectoryServerConfig}.
|
||||
* @param configurationCustomizer a {@link Consumer} function that will be applied
|
||||
* to the {@link InMemoryDirectoryServerConfig} before creating the
|
||||
* {@link InMemoryDirectoryServer}. The default values, it a Consumer function
|
||||
* that does nothing: (config) -> {}
|
||||
* @return this {@link Builder} instance.
|
||||
*/
|
||||
public Builder configurationCustomizer(Consumer<InMemoryDirectoryServerConfig> configurationCustomizer) {
|
||||
this.configurationCustomizer = configurationCustomizer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the partition name for the embedded LDAP server.
|
||||
* @param partitionName the partition name for the embedded LDAP server. Defaults
|
||||
* to the left most element of the partition suffix.
|
||||
* @return this {@link Builder} instance.
|
||||
*/
|
||||
public Builder partitionName(String partitionName) {
|
||||
this.partitionName = partitionName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a {@link EmbeddedLdapServer}.
|
||||
* <p>
|
||||
* In order to start the server, you should call
|
||||
* {@link EmbeddedLdapServer#start()}.
|
||||
* @return a new {@link EmbeddedLdapServer}.
|
||||
*/
|
||||
public EmbeddedLdapServer build() {
|
||||
try {
|
||||
InMemoryDirectoryServerConfig config = inMemoryDirectoryServerConfig(this.partitionSuffix, this.port);
|
||||
this.configurationCustomizer.accept(config);
|
||||
|
||||
Entry entry = ldapEntry(this.partitionName, this.partitionSuffix);
|
||||
InMemoryDirectoryServer directoryServer = inMemoryDirectoryServer(config, entry);
|
||||
return new EmbeddedLdapServer(directoryServer);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static String leftMostElement(String partitionSuffix) {
|
||||
try {
|
||||
List<Rdn> rdns = new LdapName(partitionSuffix).getRdns();
|
||||
return CollectionUtils.lastElement(rdns).getValue().toString();
|
||||
}
|
||||
catch (InvalidNameException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static InMemoryDirectoryServerConfig inMemoryDirectoryServerConfig(String partitionSuffix, int port)
|
||||
throws LDAPException {
|
||||
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(partitionSuffix);
|
||||
config.addAdditionalBindCredentials("uid=admin,ou=system", "secret");
|
||||
config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("LDAP", port));
|
||||
config.setEnforceSingleStructuralObjectClass(false);
|
||||
config.setEnforceAttributeSyntaxCompliance(true);
|
||||
return config;
|
||||
}
|
||||
|
||||
private static Entry ldapEntry(String defaultPartitionName, String defaultPartitionSuffix)
|
||||
throws LDAPException {
|
||||
Entry entry = new Entry(new DN(defaultPartitionSuffix));
|
||||
entry.addAttribute("objectClass", "top", "domain", "extensibleObject");
|
||||
entry.addAttribute("dc", defaultPartitionName);
|
||||
return entry;
|
||||
}
|
||||
|
||||
private static InMemoryDirectoryServer inMemoryDirectoryServer(InMemoryDirectoryServerConfig config,
|
||||
Entry entry) throws LDAPException {
|
||||
InMemoryDirectoryServer directoryServer = new InMemoryDirectoryServer(config);
|
||||
directoryServer.add(entry);
|
||||
return directoryServer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,7 +48,13 @@ public class EmbeddedLdapServerFactoryBean extends AbstractFactoryBean<EmbeddedL
|
||||
|
||||
@Override
|
||||
protected EmbeddedLdapServer createInstance() throws Exception {
|
||||
return EmbeddedLdapServer.newEmbeddedServer(this.partitionName, this.partitionSuffix, this.port);
|
||||
EmbeddedLdapServer server = EmbeddedLdapServer.withPartitionSuffix(this.partitionSuffix)
|
||||
.partitionName(this.partitionName)
|
||||
.port(this.port)
|
||||
.build();
|
||||
|
||||
server.start();
|
||||
return server;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -78,7 +78,12 @@ public final class LdapTestUtils {
|
||||
}
|
||||
|
||||
try {
|
||||
embeddedServer = EmbeddedLdapServer.newEmbeddedServer(defaultPartitionName, defaultPartitionSuffix, port);
|
||||
embeddedServer = EmbeddedLdapServer.withPartitionSuffix(defaultPartitionSuffix)
|
||||
.partitionName(defaultPartitionName)
|
||||
.port(port)
|
||||
.build();
|
||||
|
||||
embeddedServer.start();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new UncategorizedLdapException("Failed to start embedded server", ex);
|
||||
|
||||
@@ -19,12 +19,22 @@ package org.springframework.ldap.test.unboundid;
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.Attributes;
|
||||
|
||||
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
|
||||
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
|
||||
import com.unboundid.ldap.listener.InMemoryListenerConfig;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.ldap.core.AttributesMapper;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.ldap.core.support.LdapContextSource;
|
||||
import org.springframework.ldap.query.LdapQueryBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
@@ -97,6 +107,50 @@ public class EmbeddedLdapServerTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBuildButNotStartTheServer() throws IOException {
|
||||
int port = getFreePort();
|
||||
EmbeddedLdapServer.withPartitionSuffix("dc=jayway,dc=se").port(port).build();
|
||||
assertThat(isPortOpen(port)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBuildTheServerWithCustomPort() throws IOException {
|
||||
int port = getFreePort();
|
||||
EmbeddedLdapServer.Builder serverBuilder = EmbeddedLdapServer.withPartitionSuffix("dc=jayway,dc=se").port(port);
|
||||
|
||||
try (EmbeddedLdapServer server = serverBuilder.build()) {
|
||||
server.start();
|
||||
assertThat(isPortOpen(port)).isTrue();
|
||||
}
|
||||
assertThat(isPortOpen(port)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBuildLdapServerAndApplyCustomConfiguration() throws IOException {
|
||||
int port = getFreePort();
|
||||
String tempLogFile = Files.createTempFile("ldap-log-", ".txt").toAbsolutePath().toString();
|
||||
|
||||
EmbeddedLdapServer.Builder serverBuilder = EmbeddedLdapServer.withPartitionSuffix("dc=jayway,dc=se")
|
||||
.port(port)
|
||||
.configurationCustomizer((config) -> config.setCodeLogDetails(tempLogFile, true));
|
||||
|
||||
try (EmbeddedLdapServer server = serverBuilder.build()) {
|
||||
server.start();
|
||||
|
||||
ldapTemplate("dc=jayway,dc=se", port).search(LdapQueryBuilder.query().where("objectclass").is("person"),
|
||||
new AttributesMapper<>() {
|
||||
public String mapFromAttributes(Attributes attrs) throws NamingException {
|
||||
return (String) attrs.get("cn").get();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
assertThat(Path.of(tempLogFile))
|
||||
.as("Applying the custom configuration should create a log file and populate it with the request")
|
||||
.isNotEmptyFile();
|
||||
}
|
||||
|
||||
static boolean isPortOpen(int port) {
|
||||
try (Socket ignored = new Socket("localhost", port)) {
|
||||
return true;
|
||||
@@ -112,4 +166,14 @@ public class EmbeddedLdapServerTests {
|
||||
}
|
||||
}
|
||||
|
||||
static LdapTemplate ldapTemplate(String base, int port) {
|
||||
LdapContextSource ctx = new LdapContextSource();
|
||||
ctx.setBase(base);
|
||||
ctx.setUrl("ldap://127.0.0.1:" + port);
|
||||
ctx.setUserDn("uid=admin,ou=system");
|
||||
ctx.setPassword("secret");
|
||||
ctx.afterPropertiesSet();
|
||||
return new LdapTemplate(ctx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user