LDAP-183: LdapTemplate caches password with pooled connections

Now explicitly disabling native Java LDAP connection pooling for ContextSource#getContext(String principal, String credentials)
This commit is contained in:
Mattias Hellborg Arthursson
2013-08-13 09:51:46 +02:00
parent 32d1381b39
commit 0e496203ef
4 changed files with 72 additions and 42 deletions

View File

@@ -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 <code>ContextSource</code> is responsible for configuring and creating
* <code>DirContext</code> instances. It is typically used from
@@ -54,8 +54,12 @@ public interface ContextSource {
/**
* Gets a <code>DirContext</code> instance authenticated using the supplied
* principal and credentials.
*
* principal and credentials. Typically to be used for plain authentication
* purposes. <strong>Note</strong> 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.

View File

@@ -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);
}
/**

View File

@@ -218,6 +218,11 @@
which is why Spring LDAP provides a more sophisticated approach to LDAP connection pooling,
described in <xref linkend="pooling" />. If pooling functionality is required, this is the
recommended approach.</note></para>
<para><note>
Regardless of the pooling configuration, the <literal>ContextSource#getContext(String principal, String credentials)</literal>
method will always explicitly <emphasis>not</emphasis> use native Java LDAP Pooling, in order for
reset passwords to take effect as soon as possible.
</note></para>
</sect2>
<sect2 id="context-source-advanced">
<title>Advanced ContextSource Configuration</title>

View File

@@ -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));
}