From 0e496203ef5853a475b0fe1f215dcc93b2730b40 Mon Sep 17 00:00:00 2001 From: Mattias Hellborg Arthursson Date: Tue, 13 Aug 2013 09:51:46 +0200 Subject: [PATCH] LDAP-183: LdapTemplate caches password with pooled connections Now explicitly disabling native Java LDAP connection pooling for ContextSource#getContext(String principal, String credentials) --- .../ldap/core/ContextSource.java | 12 ++-- .../core/support/AbstractContextSource.java | 68 ++++++++++++------- src/docbkx/configuration.xml | 5 ++ .../LdapContextSourceIntegrationTest.java | 29 ++++---- 4 files changed, 72 insertions(+), 42 deletions(-) diff --git a/core/src/main/java/org/springframework/ldap/core/ContextSource.java b/core/src/main/java/org/springframework/ldap/core/ContextSource.java index bbcbcc09..28099c66 100644 --- a/core/src/main/java/org/springframework/ldap/core/ContextSource.java +++ b/core/src/main/java/org/springframework/ldap/core/ContextSource.java @@ -16,10 +16,10 @@ package org.springframework.ldap.core; -import javax.naming.directory.DirContext; - import org.springframework.ldap.NamingException; +import javax.naming.directory.DirContext; + /** * A ContextSource is responsible for configuring and creating * DirContext instances. It is typically used from @@ -54,8 +54,12 @@ public interface ContextSource { /** * Gets a DirContext instance authenticated using the supplied - * principal and credentials. - * + * principal and credentials. Typically to be used for plain authentication + * purposes. Note that this method will never make use + * of native Java LDAP pooling, even though this instance is configured to do so. + * This is to force password changes in the target directory to take effect + * as soon as possible. + * * @param principal The principal (typically a distinguished name of a user * in the LDAP tree) to use for authentication. * @param credentials The credentials to use for authentication. diff --git a/core/src/main/java/org/springframework/ldap/core/support/AbstractContextSource.java b/core/src/main/java/org/springframework/ldap/core/support/AbstractContextSource.java index c6cf377d..fd5dc489 100644 --- a/core/src/main/java/org/springframework/ldap/core/support/AbstractContextSource.java +++ b/core/src/main/java/org/springframework/ldap/core/support/AbstractContextSource.java @@ -15,13 +15,6 @@ */ package org.springframework.ldap.core.support; -import java.util.Hashtable; -import java.util.Map; - -import javax.naming.Context; -import javax.naming.NamingException; -import javax.naming.directory.DirContext; - import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; @@ -33,6 +26,12 @@ import org.springframework.ldap.core.ContextSource; import org.springframework.ldap.core.DistinguishedName; import org.springframework.ldap.support.LdapUtils; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import java.util.Hashtable; +import java.util.Map; + /** * Abstract implementation of the {@link ContextSource} interface. By default, * returns an authenticated @@ -67,8 +66,10 @@ public abstract class AbstractContextSource implements BaseLdapPathContextSource private static final Class DEFAULT_CONTEXT_FACTORY = com.sun.jndi.ldap.LdapCtxFactory.class; private static final Class DEFAULT_DIR_OBJECT_FACTORY = DefaultDirObjectFactory.class; + private static final boolean DONT_DISABLE_POOLING = false; + private static final boolean EXPLICITLY_DISABLE_POOLING = true; - private Class dirObjectFactory = DEFAULT_DIR_OBJECT_FACTORY; + private Class dirObjectFactory = DEFAULT_DIR_OBJECT_FACTORY; private Class contextFactory = DEFAULT_CONTEXT_FACTORY; @@ -103,26 +104,40 @@ public abstract class AbstractContextSource implements BaseLdapPathContextSource private DirContextAuthenticationStrategy authenticationStrategy = new SimpleDirContextAuthenticationStrategy(); public DirContext getContext(String principal, String credentials) { - DirContext ctx = createContext(getAuthenticatedEnv(principal, credentials)); - - try { - authenticationStrategy.processContextAfterCreation(ctx, principal, credentials); - return ctx; - } - catch (NamingException e) { - closeContext(ctx); - throw LdapUtils.convertLdapException(e); - } + // This method is typically called for authentication purposes, which means that we + // should explicitly disable pooling in case passwords are changed (LDAP-183). + return doGetContext(principal, credentials, EXPLICITLY_DISABLE_POOLING); } - /* - * (non-Javadoc) - * - * @see org.springframework.ldap.core.ContextSource#getReadOnlyContext() - */ + private DirContext doGetContext(String principal, String credentials, boolean explicitlyDisablePooling) { + Hashtable env = getAuthenticatedEnv(principal, credentials); + if(explicitlyDisablePooling) { + env.remove(SUN_LDAP_POOLING_FLAG); + } + + DirContext ctx = createContext(env); + + try { + authenticationStrategy.processContextAfterCreation(ctx, principal, credentials); + return ctx; + } + catch (NamingException e) { + closeContext(ctx); + throw LdapUtils.convertLdapException(e); + } + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.core.ContextSource#getReadOnlyContext() + */ public DirContext getReadOnlyContext() { if (!anonymousReadOnly) { - return getContext(authenticationSource.getPrincipal(), authenticationSource.getCredentials()); + return doGetContext( + authenticationSource.getPrincipal(), + authenticationSource.getCredentials(), + DONT_DISABLE_POOLING); } else { return createContext(getAnonymousEnv()); @@ -135,7 +150,10 @@ public abstract class AbstractContextSource implements BaseLdapPathContextSource * @see org.springframework.ldap.core.ContextSource#getReadWriteContext() */ public DirContext getReadWriteContext() { - return getContext(authenticationSource.getPrincipal(), authenticationSource.getCredentials()); + return doGetContext( + authenticationSource.getPrincipal(), + authenticationSource.getCredentials(), + DONT_DISABLE_POOLING); } /** diff --git a/src/docbkx/configuration.xml b/src/docbkx/configuration.xml index 1629a453..528b1f3d 100644 --- a/src/docbkx/configuration.xml +++ b/src/docbkx/configuration.xml @@ -218,6 +218,11 @@ which is why Spring LDAP provides a more sophisticated approach to LDAP connection pooling, described in . If pooling functionality is required, this is the recommended approach. + + Regardless of the pooling configuration, the ContextSource#getContext(String principal, String credentials) + method will always explicitly not use native Java LDAP Pooling, in order for + reset passwords to take effect as soon as possible. + Advanced ContextSource Configuration diff --git a/test/integration-tests/src/test/java/org/springframework/ldap/core/support/LdapContextSourceIntegrationTest.java b/test/integration-tests/src/test/java/org/springframework/ldap/core/support/LdapContextSourceIntegrationTest.java index 080d889e..f7ff704d 100644 --- a/test/integration-tests/src/test/java/org/springframework/ldap/core/support/LdapContextSourceIntegrationTest.java +++ b/test/integration-tests/src/test/java/org/springframework/ldap/core/support/LdapContextSourceIntegrationTest.java @@ -15,18 +15,6 @@ */ package org.springframework.ldap.core.support; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertTrue; -import static junit.framework.Assert.fail; - -import java.util.Hashtable; -import java.util.List; - -import javax.naming.Context; -import javax.naming.NamingException; -import javax.naming.directory.DirContext; - import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.IncorrectResultSizeDataAccessException; @@ -39,6 +27,18 @@ import org.springframework.ldap.filter.EqualsFilter; import org.springframework.ldap.support.LdapUtils; import org.springframework.test.context.ContextConfiguration; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import java.util.Hashtable; +import java.util.List; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; + /** * Integration tests for LdapContextSource. * @@ -87,6 +87,7 @@ public class LdapContextSourceIntegrationTest extends AbstractLdapTemplateIntegr assertNotNull(ctx); // Double check to see that we are authenticated. Hashtable environment = ctx.getEnvironment(); + assertTrue(environment.containsKey(LdapContextSource.SUN_LDAP_POOLING_FLAG)); assertTrue(environment.containsKey(Context.SECURITY_PRINCIPAL)); assertTrue(environment.containsKey(Context.SECURITY_CREDENTIALS)); } @@ -111,8 +112,10 @@ public class LdapContextSourceIntegrationTest extends AbstractLdapTemplateIntegr String expectedCredentials = "password"; ctx = tested.getContext(expectedPrincipal, expectedCredentials); assertNotNull(ctx); - // Double check to see that we are authenticated. + // Double check to see that we are authenticated, and that we did not receive + // a connection eligible for connection pooling. Hashtable environment = ctx.getEnvironment(); + assertFalse(environment.containsKey(LdapContextSource.SUN_LDAP_POOLING_FLAG)); assertEquals(expectedPrincipal, environment.get(Context.SECURITY_PRINCIPAL)); assertEquals(expectedCredentials, environment.get(Context.SECURITY_CREDENTIALS)); }