diff --git a/core/src/main/java/org/springframework/security/ldap/DefaultSpringSecurityContextSource.java b/core/src/main/java/org/springframework/security/ldap/DefaultSpringSecurityContextSource.java index ae0c466f93..ca70de7f6a 100644 --- a/core/src/main/java/org/springframework/security/ldap/DefaultSpringSecurityContextSource.java +++ b/core/src/main/java/org/springframework/security/ldap/DefaultSpringSecurityContextSource.java @@ -1,11 +1,14 @@ package org.springframework.security.ldap; import java.util.ArrayList; +import java.util.Hashtable; import java.util.StringTokenizer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.ldap.core.support.DirContextAuthenticationStrategy; import org.springframework.ldap.core.support.LdapContextSource; +import org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy; import org.springframework.util.Assert; /** @@ -15,6 +18,11 @@ import org.springframework.util.Assert; * From Spring Security 2.5, Spring LDAP 1.3 is used and the ContextSource interface * provides support for binding with a username and password. As a result, Spring LDAP ContextSource * implementations such as LdapContextSource may be used directly with Spring Security. + *

+ * Spring LDAP 1.3 doesn't have JVM-level LDAP connection pooling enabled by default. This class sets the + * pooled property to true, but customizes the {@link DirContextAuthenticationStrategy} used to disable + * pooling when the DN doesn't match the userDn property. This prevents pooling for calls + * to {@link #getContext(String, String)} to authenticate as specific users. * * @author Luke Taylor * @version $Id$ @@ -53,7 +61,20 @@ public class DefaultSpringSecurityContextSource extends LdapContextSource { } } - super.setUrls(urls.toArray(new String[urls.size()])); - super.setBase(rootDn); + setUrls(urls.toArray(new String[urls.size()])); + setBase(rootDn); + setPooled(true); + setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy() { + @Override + @SuppressWarnings("unchecked") + public void setupEnvironment(Hashtable env, String dn, String password) { + super.setupEnvironment(env, dn, password); + // Remove the pooling flag unless we are authenticating as the 'manager' user. + if (!userDn.equals(dn) && env.containsKey(SUN_LDAP_POOLING_FLAG)) { + logger.debug("Removing pooling flag for user " + dn); + env.remove(SUN_LDAP_POOLING_FLAG); + } + } + }); } } diff --git a/core/src/test/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java b/core/src/test/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java index 1b437c8fc8..e010603f92 100644 --- a/core/src/test/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java +++ b/core/src/test/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java @@ -1,6 +1,12 @@ package org.springframework.security.ldap; +import static org.junit.Assert.*; + +import java.util.Hashtable; + import org.junit.Test; +import org.springframework.ldap.core.support.AbstractContextSource; + /** * @author Luke Taylor @@ -9,13 +15,46 @@ import org.junit.Test; public class DefaultSpringSecurityContextSourceTests { @Test - public void instantiationSucceeds() { - new DefaultSpringSecurityContextSource("ldap://blah:789/dc=springframework,dc=org"); + public void instantiationSucceedsWithExpectedProperties() { + DefaultSpringSecurityContextSource ctxSrc = + new DefaultSpringSecurityContextSource("ldap://blah:789/dc=springframework,dc=org"); + assertFalse(ctxSrc.isAnonymousReadOnly()); + assertTrue(ctxSrc.isPooled()); } @Test public void supportsSpacesInUrl() { - new DefaultSpringSecurityContextSource("ldap://myhost:10389/dc=spring%20framework,dc=org"); + new DefaultSpringSecurityContextSource("ldap://myhost:10389/dc=spring%20framework,dc=org"); + } + + @Test + public void poolingFlagIsSetWhenAuthenticationDnMatchesManagerUserDn() throws Exception { + EnvExposingDefaultSpringSecurityContextSource ctxSrc = + new EnvExposingDefaultSpringSecurityContextSource("ldap://blah:789/dc=springframework,dc=org"); + ctxSrc.setUserDn("manager"); + ctxSrc.setPassword("password"); + ctxSrc.afterPropertiesSet(); + assertTrue(ctxSrc.getAuthenticatedEnvForTest("manager", "password").containsKey(AbstractContextSource.SUN_LDAP_POOLING_FLAG)); + } + + @Test + public void poolingFlagIsNotSetWhenAuthenticationDnIsNotManagerUserDn() throws Exception { + EnvExposingDefaultSpringSecurityContextSource ctxSrc = + new EnvExposingDefaultSpringSecurityContextSource("ldap://blah:789/dc=springframework,dc=org"); + ctxSrc.setUserDn("manager"); + ctxSrc.setPassword("password"); + ctxSrc.afterPropertiesSet(); + assertFalse(ctxSrc.getAuthenticatedEnvForTest("user", "password").containsKey(AbstractContextSource.SUN_LDAP_POOLING_FLAG)); + } + + static class EnvExposingDefaultSpringSecurityContextSource extends DefaultSpringSecurityContextSource { + public EnvExposingDefaultSpringSecurityContextSource(String providerUrl) { + super(providerUrl); + } + + @SuppressWarnings("unchecked") + Hashtable getAuthenticatedEnvForTest(String userDn, String password) { + return getAuthenticatedEnv(userDn, password); + } } - }