diff --git a/ldap/src/integration-test/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java index 14618224d3..4246d22190 100644 --- a/ldap/src/integration-test/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java +++ b/ldap/src/integration-test/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java @@ -2,7 +2,9 @@ package org.springframework.security.ldap; import static org.junit.Assert.*; +import java.util.ArrayList; import java.util.Hashtable; +import java.util.List; import javax.naming.directory.DirContext; @@ -74,6 +76,35 @@ public class DefaultSpringSecurityContextSourceTests extends AbstractLdapIntegra contextSource.getContext("uid=space cadet,ou=space cadets,dc=springframework,dc=org", "spacecadetspassword"); } + @Test(expected=IllegalArgumentException.class) + public void instantiationFailsWithEmptyServerList() throws Exception { + List serverUrls = new ArrayList(); + DefaultSpringSecurityContextSource ctxSrc = new DefaultSpringSecurityContextSource(serverUrls, "dc=springframework,dc=org"); + ctxSrc.afterPropertiesSet(); + } + + @Test + public void instantiationSuceedsWithProperServerList() throws Exception { + List serverUrls = new ArrayList(); + serverUrls.add("ldap://foo:789"); + serverUrls.add("ldap://bar:389"); + serverUrls.add("ldaps://blah:636"); + DefaultSpringSecurityContextSource ctxSrc = new DefaultSpringSecurityContextSource(serverUrls, "dc=springframework,dc=org"); + + assertFalse(ctxSrc.isAnonymousReadOnly()); + assertTrue(ctxSrc.isPooled()); + } + + @Test(expected=IllegalArgumentException.class) + public void instantiationFailsWithIncorrectServerUrl() throws Exception { + List serverUrls = new ArrayList(); + // a simple trailing slash should be ok + serverUrls.add("ldaps://blah:636/"); + // this url should be rejected because the root DN goes into a separate parameter + serverUrls.add("ldap://bar:389/dc=foobar,dc=org"); + DefaultSpringSecurityContextSource ctxSrc = new DefaultSpringSecurityContextSource(serverUrls, "dc=springframework,dc=org"); + } + static class EnvExposingDefaultSpringSecurityContextSource extends DefaultSpringSecurityContextSource { public EnvExposingDefaultSpringSecurityContextSource(String providerUrl) { super(providerUrl); diff --git a/ldap/src/main/java/org/springframework/security/ldap/DefaultSpringSecurityContextSource.java b/ldap/src/main/java/org/springframework/security/ldap/DefaultSpringSecurityContextSource.java index 77e1af307b..d7492c91c1 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/DefaultSpringSecurityContextSource.java +++ b/ldap/src/main/java/org/springframework/security/ldap/DefaultSpringSecurityContextSource.java @@ -2,6 +2,7 @@ package org.springframework.security.ldap; import java.util.ArrayList; import java.util.Hashtable; +import java.util.List; import java.util.StringTokenizer; import org.apache.commons.logging.Log; @@ -33,7 +34,9 @@ public class DefaultSpringSecurityContextSource extends LdapContextSource { private String rootDn; /** - * Create and initialize an instance which will connect to the supplied LDAP URL. + * Create and initialize an instance which will connect to the supplied LDAP URL. If you + * want to use more than one server for fail-over, rather use + * the {@link #DefaultSpringSecurityContextSource(List, String)} constructor. * * @param providerUrl an LDAP URL of the form ldap://localhost:389/base_dn */ @@ -76,4 +79,64 @@ public class DefaultSpringSecurityContextSource extends LdapContextSource { } }); } + + /** + * Create and initialize an instance which will connect of the LDAP Spring Security + * Context Source. It will connect to any of the provided LDAP server URLs. + * + * @param urls + * A list of string values which are LDAP server URLs. An example would be + * ldap://ldap.company.com:389. LDAPS URLs (SSL-secured) may be used as well, + * given that Spring Security is able to connect to the server. + * Note that these URLs must not include the base DN! + * @param baseDn + * The common Base DN for all provided servers, e.g. + *
dc=company,dc=com
. + */ + public DefaultSpringSecurityContextSource(List urls, String baseDn) { + this(buildProviderUrl(urls, baseDn)); + } + + /** + * Builds a Spring LDAP-compliant Provider URL string, i.e. a space-separated list of LDAP servers + * with their base DNs. As the base DN must be identical for all servers, it needs to be supplied + * only once. + * + * @param urls + * A list of string values which are LDAP server URLs. An example would be + *
ldap://ldap.company.com:389
. LDAPS URLs may be used as well, + * given that Spring Security is able to connect to the server. + * @param baseDn + * The common Base DN for all provided servers, e.g. + *
dc=company,dc=com
. + * @return A Spring Security/Spring LDAP-compliant Provider URL string. + */ + private static String buildProviderUrl(List urls, String baseDn) { + Assert.notNull(baseDn, "The Base DN for the LDAP server must not be null."); + Assert.notEmpty(urls, "At least one LDAP server URL must be provided."); + + String trimmedBaseDn = baseDn.trim(); + StringBuilder providerUrl = new StringBuilder(); + + for (String serverUrl : urls) { + String trimmedUrl = serverUrl.trim(); + if ("".equals(trimmedUrl)) { + continue; + } + if (trimmedUrl.contains(trimmedBaseDn)) { + throw new IllegalArgumentException("LDAP URL string must not include the base DN! '" + trimmedUrl + "'"); + } + + providerUrl.append(trimmedUrl); + if (! trimmedUrl.endsWith("/")) { + providerUrl.append("/"); + } + providerUrl.append(trimmedBaseDn); + providerUrl.append(" "); + } + + return providerUrl.toString(); + + } + }