Fix concurrent session interaction bug where UserDetails.getUsername() may have been override to be a different value than the original login request, as per email from Herryanto Siatono on acegisecurity-developer 5 November 2005.

This commit is contained in:
Ben Alex 2005-11-05 03:50:22 +00:00
parent a807b8d539
commit aa4fd8586c
1 changed files with 90 additions and 62 deletions

View File

@ -15,12 +15,6 @@
package net.sf.acegisecurity.providers; package net.sf.acegisecurity.providers;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import net.sf.acegisecurity.AbstractAuthenticationManager; import net.sf.acegisecurity.AbstractAuthenticationManager;
import net.sf.acegisecurity.AccountExpiredException; import net.sf.acegisecurity.AccountExpiredException;
import net.sf.acegisecurity.Authentication; import net.sf.acegisecurity.Authentication;
@ -48,11 +42,21 @@ import net.sf.acegisecurity.providers.cas.ProxyUntrustedException;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
/** /**
* Iterates an {@link Authentication} request through a list of {@link * Iterates an {@link Authentication} request through a list of {@link
@ -76,17 +80,22 @@ import org.springframework.util.Assert;
* <code>ProviderNotFoundException</code>. * <code>ProviderNotFoundException</code>.
* </p> * </p>
* *
* <p>If a valid <code>Authentication</code> is returned by an <code>AuthenticationProvider</code>, * <p>
* the <code>ProviderManager</code> will publish an * If a valid <code>Authentication</code> is returned by an
* {@link net.sf.acegisecurity.event.authentication.AuthenticationSuccessEvent}. If an * <code>AuthenticationProvider</code>, the <code>ProviderManager</code> will
* <code>AuthenticationException</code> is detected, the final <code>AuthenticationException</code> thrown * publish an {@link
* will be used to publish an appropriate failure event. By default <code>ProviderManager</code> * net.sf.acegisecurity.event.authentication.AuthenticationSuccessEvent}. If
* maps common exceptions to events, but this can be fine-tuned by providing a new * an <code>AuthenticationException</code> is detected, the final
* <code>exceptionMappings</code> <code>java.util.Properties</code> object. In the * <code>AuthenticationException</code> thrown will be used to publish an
* properties object, each of the keys represent the fully qualified classname of * appropriate failure event. By default <code>ProviderManager</code> maps
* the exception, and each of the values represent the name of an event class which subclasses * common exceptions to events, but this can be fine-tuned by providing a new
* {@link net.sf.acegisecurity.event.authentication.AbstractAuthenticationFailureEvent} and * <code>exceptionMappings</code><code>java.util.Properties</code> object. In
* provides its constructor. * the properties object, each of the keys represent the fully qualified
* classname of the exception, and each of the values represent the name of an
* event class which subclasses {@link
* net.sf.acegisecurity.event.authentication.AbstractAuthenticationFailureEvent}
* and provides its constructor.
* </p>
* *
* @author Ben Alex * @author Ben Alex
* @author Wesley Hall * @author Wesley Hall
@ -103,13 +112,18 @@ public class ProviderManager extends AbstractAuthenticationManager
//~ Instance fields ======================================================== //~ Instance fields ========================================================
private ApplicationEventPublisher applicationEventPublisher;
private ConcurrentSessionController sessionController = new NullConcurrentSessionController(); private ConcurrentSessionController sessionController = new NullConcurrentSessionController();
private List providers; private List providers;
private Properties exceptionMappings; private Properties exceptionMappings;
private ApplicationEventPublisher applicationEventPublisher;
//~ Methods ================================================================ //~ Methods ================================================================
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
/** /**
* Sets the {@link AuthenticationProvider} objects to be used for * Sets the {@link AuthenticationProvider} objects to be used for
* authentication. * authentication.
@ -169,28 +183,30 @@ public class ProviderManager extends AbstractAuthenticationManager
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
checkIfValidList(this.providers); checkIfValidList(this.providers);
if (exceptionMappings == null) { if (exceptionMappings == null) {
exceptionMappings = new Properties(); exceptionMappings = new Properties();
exceptionMappings.put(AccountExpiredException.class.getName(), AuthenticationFailureExpiredEvent.class.getName()); exceptionMappings.put(AccountExpiredException.class.getName(),
exceptionMappings.put(AuthenticationServiceException.class.getName(), AuthenticationFailureServiceExceptionEvent.class.getName()); AuthenticationFailureExpiredEvent.class.getName());
exceptionMappings.put(LockedException.class.getName(), AuthenticationFailureLockedEvent.class.getName()); exceptionMappings.put(AuthenticationServiceException.class.getName(),
exceptionMappings.put(CredentialsExpiredException.class.getName(), AuthenticationFailureCredentialsExpiredEvent.class.getName()); AuthenticationFailureServiceExceptionEvent.class.getName());
exceptionMappings.put(DisabledException.class.getName(), AuthenticationFailureDisabledEvent.class.getName()); exceptionMappings.put(LockedException.class.getName(),
exceptionMappings.put(BadCredentialsException.class.getName(), AuthenticationFailureBadCredentialsEvent.class.getName()); AuthenticationFailureLockedEvent.class.getName());
exceptionMappings.put(ConcurrentLoginException.class.getName(), AuthenticationFailureConcurrentLoginEvent.class.getName()); exceptionMappings.put(CredentialsExpiredException.class.getName(),
exceptionMappings.put(ProviderNotFoundException.class.getName(), AuthenticationFailureProviderNotFoundEvent.class.getName()); AuthenticationFailureCredentialsExpiredEvent.class.getName());
exceptionMappings.put(ProxyUntrustedException.class.getName(), AuthenticationFailureProxyUntrustedEvent.class.getName()); exceptionMappings.put(DisabledException.class.getName(),
doAddExtraDefaultExceptionMappings(exceptionMappings); AuthenticationFailureDisabledEvent.class.getName());
exceptionMappings.put(BadCredentialsException.class.getName(),
AuthenticationFailureBadCredentialsEvent.class.getName());
exceptionMappings.put(ConcurrentLoginException.class.getName(),
AuthenticationFailureConcurrentLoginEvent.class.getName());
exceptionMappings.put(ProviderNotFoundException.class.getName(),
AuthenticationFailureProviderNotFoundEvent.class.getName());
exceptionMappings.put(ProxyUntrustedException.class.getName(),
AuthenticationFailureProxyUntrustedEvent.class.getName());
doAddExtraDefaultExceptionMappings(exceptionMappings);
} }
} }
/**
* Provided so subclasses can add extra exception mappings during startup if no
* exception mappings are injected by the IoC container.
*
* @param exceptionMappings the properties object, which already has entries in it
*/
protected void doAddExtraDefaultExceptionMappings(Properties exceptionMappings) {}
/** /**
* Attempts to authenticate the passed {@link Authentication} object. * Attempts to authenticate the passed {@link Authentication} object.
@ -215,7 +231,6 @@ public class ProviderManager extends AbstractAuthenticationManager
* @return a fully authenticated object including credentials. * @return a fully authenticated object including credentials.
* *
* @throws AuthenticationException if authentication fails. * @throws AuthenticationException if authentication fails.
* @throws ProviderNotFoundException DOCUMENT ME!
*/ */
public Authentication doAuthentication(Authentication authentication) public Authentication doAuthentication(Authentication authentication)
throws AuthenticationException { throws AuthenticationException {
@ -223,8 +238,6 @@ public class ProviderManager extends AbstractAuthenticationManager
Class toTest = authentication.getClass(); Class toTest = authentication.getClass();
sessionController.checkAuthenticationAllowed(authentication);
AuthenticationException lastException = null; AuthenticationException lastException = null;
while (iter.hasNext()) { while (iter.hasNext()) {
@ -239,54 +252,69 @@ public class ProviderManager extends AbstractAuthenticationManager
try { try {
result = provider.authenticate(authentication); result = provider.authenticate(authentication);
sessionController.checkAuthenticationAllowed(result);
} catch (AuthenticationException ae) { } catch (AuthenticationException ae) {
lastException = ae; lastException = ae;
} }
if (result != null) { if (result != null) {
sessionController.registerSuccessfulAuthentication(result); sessionController.registerSuccessfulAuthentication(result);
applicationEventPublisher.publishEvent(new AuthenticationSuccessEvent(result)); applicationEventPublisher.publishEvent(new AuthenticationSuccessEvent(
result));
return result; return result;
} }
} }
} }
if (lastException == null) { if (lastException == null) {
lastException = new ProviderNotFoundException("No authentication provider for " + toTest.getName()); lastException = new ProviderNotFoundException(
"No authentication provider for " + toTest.getName());
} }
// Publish the event // Publish the event
String className = exceptionMappings.getProperty(lastException.getClass().getName()); String className = exceptionMappings.getProperty(lastException.getClass()
.getName());
AbstractAuthenticationEvent event = null; AbstractAuthenticationEvent event = null;
if (className != null) { if (className != null) {
try { try {
Class clazz = getClass().getClassLoader().loadClass(className); Class clazz = getClass().getClassLoader().loadClass(className);
Constructor constructor = clazz.getConstructor(new Class[] {Authentication.class, AuthenticationException.class}); Constructor constructor = clazz.getConstructor(new Class[] {Authentication.class, AuthenticationException.class});
Object obj = constructor.newInstance(new Object[] {authentication, lastException}); Object obj = constructor.newInstance(new Object[] {authentication, lastException});
Assert.isInstanceOf(AbstractAuthenticationEvent.class, obj, "Must be an AbstractAuthenticationEvent"); Assert.isInstanceOf(AbstractAuthenticationEvent.class, obj,
event = (AbstractAuthenticationEvent) obj; "Must be an AbstractAuthenticationEvent");
} catch (ClassNotFoundException ignored) { event = (AbstractAuthenticationEvent) obj;
} catch (NoSuchMethodException ignored) { } catch (ClassNotFoundException ignored) {}
} catch (IllegalAccessException ignored) { catch (NoSuchMethodException ignored) {}
} catch (InstantiationException ignored) { catch (IllegalAccessException ignored) {}
} catch (InvocationTargetException ignored) { catch (InstantiationException ignored) {}
} catch (InvocationTargetException ignored) {}
} }
Assert.notNull(event, "A valid event must be available for the exception " + lastException.getClass().getName());
Assert.notNull(event,
"A valid event must be available for the exception "
+ lastException.getClass().getName());
applicationEventPublisher.publishEvent(event); applicationEventPublisher.publishEvent(event);
// Throw the exception // Throw the exception
throw lastException; throw lastException;
} }
/**
* Provided so subclasses can add extra exception mappings during startup
* if no exception mappings are injected by the IoC container.
*
* @param exceptionMappings the properties object, which already has
* entries in it
*/
protected void doAddExtraDefaultExceptionMappings(
Properties exceptionMappings) {}
private void checkIfValidList(List listToCheck) { private void checkIfValidList(List listToCheck) {
if ((listToCheck == null) || (listToCheck.size() == 0)) { if ((listToCheck == null) || (listToCheck.size() == 0)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"A list of AuthenticationManagers is required"); "A list of AuthenticationManagers is required");
} }
} }
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
} }