소스 검색

SEC-271: work on security:autoconfig

Vishal Puri 18 년 전
부모
커밋
b2c30277f4
29개의 변경된 파일2437개의 추가작업 그리고 1704개의 파일을 삭제
  1. 165 113
      core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java
  2. 520 399
      core/src/main/java/org/acegisecurity/ui/AbstractProcessingFilter.java
  3. 273 177
      core/src/main/java/org/acegisecurity/ui/ExceptionTranslationFilter.java
  4. 154 130
      core/src/main/java/org/acegisecurity/ui/logout/LogoutFilter.java
  5. 3 1
      core/src/main/java/org/acegisecurity/ui/logout/SecurityContextLogoutHandler.java
  6. 60 4
      core/src/main/java/org/acegisecurity/ui/rememberme/RememberMeProcessingFilter.java
  7. 393 304
      core/src/main/java/org/acegisecurity/ui/rememberme/TokenBasedRememberMeServices.java
  8. 206 179
      core/src/main/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilterEntryPoint.java
  9. 316 291
      core/src/test/java/org/acegisecurity/ui/ExceptionTranslationFilterTests.java
  10. 9 9
      core/src/test/java/org/acegisecurity/ui/rememberme/TokenBasedRememberMeServicesTests.java
  11. 1 1
      samples/annotations/pom.xml
  12. 3 3
      sandbox/pom.xml
  13. 25 24
      sandbox/spring-security-config/.classpath
  14. 4 0
      sandbox/spring-security-config/.project
  15. 6 4
      sandbox/spring-security-config/.settings/org.eclipse.jdt.core.prefs
  16. 5 0
      sandbox/spring-security-config/pom.xml
  17. 5 4
      sandbox/spring-security-config/src/main/java/org/acegisecurity/config/AuthenticationMechanismBeanDefinitionParser.java
  18. 10 5
      sandbox/spring-security-config/src/main/java/org/acegisecurity/config/AuthenticationProcessingFilterBeanDefinitionParser.java
  19. 204 13
      sandbox/spring-security-config/src/main/java/org/acegisecurity/config/AutoConfigBeanDefinitionParser.java
  20. 2 2
      sandbox/spring-security-config/src/main/java/org/acegisecurity/config/ContextIntegrationBeanDefinitionParser.java
  21. 31 1
      sandbox/spring-security-config/src/main/java/org/acegisecurity/config/ExceptionTranslationFilterBeanDefinitionParser.java
  22. 2 2
      sandbox/spring-security-config/src/main/java/org/acegisecurity/config/LogoutFilterBeanDefinitionParser.java
  23. 6 4
      sandbox/spring-security-config/src/main/java/org/acegisecurity/config/RememberMeFilterBeanDefinitionParser.java
  24. 5 4
      sandbox/spring-security-config/src/main/java/org/acegisecurity/config/RememberMeServicesBeanDefinitionParser.java
  25. 21 3
      sandbox/spring-security-config/src/test/java/org/acegisecurity/config/AutoConfigBeanDefinitionParserTests.java
  26. 0 1
      sandbox/spring-security-config/src/test/java/org/acegisecurity/config/RememberMeBeanDefinitionParserTest.java
  27. 3 3
      sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/applicationContext-acegi-security.xml
  28. 4 22
      sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/auto-config.xml
  29. 1 1
      sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/user.properties

+ 165 - 113
core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java

@@ -15,140 +15,192 @@
 
 package org.acegisecurity.providers.dao;
 
+import java.util.Map;
+
 import org.acegisecurity.AuthenticationException;
 import org.acegisecurity.AuthenticationServiceException;
 import org.acegisecurity.BadCredentialsException;
-
 import org.acegisecurity.providers.AuthenticationProvider;
 import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
 import org.acegisecurity.providers.encoding.PasswordEncoder;
 import org.acegisecurity.providers.encoding.PlaintextPasswordEncoder;
-
 import org.acegisecurity.userdetails.UserDetails;
 import org.acegisecurity.userdetails.UserDetailsService;
-
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
 import org.springframework.core.Ordered;
 import org.springframework.dao.DataAccessException;
-
 import org.springframework.util.Assert;
 
-
 /**
- * An {@link AuthenticationProvider} implementation that retrieves user details from an {@link UserDetailsService}.
- *
+ * An {@link AuthenticationProvider} implementation that retrieves user details
+ * from an {@link UserDetailsService}.
+ * 
  * @author Ben Alex
- * @version $Id$
+ * @version $Id: DaoAuthenticationProvider.java 1857 2007-05-24 00:47:12Z
+ * benalex $
  */
-public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider implements Ordered {
-	
-    //~ Instance fields ================================================================================================
-
-    private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
-    private SaltSource saltSource;
-    private UserDetailsService userDetailsService;
-    private boolean includeDetailsObject = true;
-    private int order = -1; // default: same as non-Ordered
-
-    //~ Methods ========================================================================================================
-
-    protected void additionalAuthenticationChecks(UserDetails userDetails,
-        UsernamePasswordAuthenticationToken authentication)
-        throws AuthenticationException {
-        Object salt = null;
-
-        if (this.saltSource != null) {
-            salt = this.saltSource.getSalt(userDetails);
-        }
-        
-        if (authentication.getCredentials() == null) {
-            throw new BadCredentialsException(messages.getMessage(
-                    "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
-                    includeDetailsObject ? userDetails : null);
-        }
-        
-        String presentedPassword = authentication.getCredentials() == null ? "" : authentication.getCredentials().toString();
-
-        if (!passwordEncoder.isPasswordValid(
-                userDetails.getPassword(), presentedPassword, salt)) {
-            throw new BadCredentialsException(messages.getMessage(
-                    "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
-                    includeDetailsObject ? userDetails : null);
-        }
-    }
-
-    protected void doAfterPropertiesSet() throws Exception {
-        Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
-    }
-
-    public PasswordEncoder getPasswordEncoder() {
-        return passwordEncoder;
-    }
-
-    public SaltSource getSaltSource() {
-        return saltSource;
-    }
-
-    public UserDetailsService getUserDetailsService() {
-        return userDetailsService;
-    }
-
-    protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
-        throws AuthenticationException {
-        UserDetails loadedUser;
-
-        try {
-            loadedUser = this.getUserDetailsService().loadUserByUsername(username);
-        } catch (DataAccessException repositoryProblem) {
-            throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
-        }
-
-        if (loadedUser == null) {
-            throw new AuthenticationServiceException(
-                "UserDetailsService returned null, which is an interface contract violation");
-        }
-        return loadedUser;
-    }
-
-    /**
-     * Sets the PasswordEncoder instance to be used to encode and validate passwords. If not set, {@link
-     * PlaintextPasswordEncoder} will be used by default.
-     *
-     * @param passwordEncoder The passwordEncoder to use
-     */
-    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
-        this.passwordEncoder = passwordEncoder;
-    }
-
-    /**
-     * The source of salts to use when decoding passwords. <code>null</code> is a valid value, meaning the
-     * <code>DaoAuthenticationProvider</code> will present <code>null</code> to the relevant
-     * <code>PasswordEncoder</code>.
-     *
-     * @param saltSource to use when attempting to decode passwords via the <code>PasswordEncoder</code>
-     */
-    public void setSaltSource(SaltSource saltSource) {
-        this.saltSource = saltSource;
-    }
-
-    public void setUserDetailsService(UserDetailsService userDetailsService) {
-        this.userDetailsService = userDetailsService;
-    }
-
-    public boolean isIncludeDetailsObject() {
-        return includeDetailsObject;
-    }
-
-    public void setIncludeDetailsObject(boolean includeDetailsObject) {
-        this.includeDetailsObject = includeDetailsObject;
-    }
+public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider implements
+		ApplicationContextAware, Ordered {
+
+	// ~ Instance fields
+	// ================================================================================================
+
+	private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
+
+	private SaltSource saltSource;
+
+	private UserDetailsService userDetailsService;
+
+	private boolean includeDetailsObject = true;
+
+	private int DEFAULT_RDER = Integer.MAX_VALUE; // default: same as
+
+	// non-Ordered
+
+	private int order = DEFAULT_RDER;
+
+	private boolean isSetUserDetailsServiceInvoked;
+
+	private ApplicationContext applicationContext;
+
+	// ~ Methods
+	// ========================================================================================================
+
+	protected void additionalAuthenticationChecks(UserDetails userDetails,
+			UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
+		Object salt = null;
+
+		if (this.saltSource != null) {
+			salt = this.saltSource.getSalt(userDetails);
+		}
+
+		if (authentication.getCredentials() == null) {
+			throw new BadCredentialsException(messages.getMessage(
+					"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
+					includeDetailsObject ? userDetails : null);
+		}
+
+		String presentedPassword = authentication.getCredentials() == null ? "" : authentication.getCredentials()
+				.toString();
+
+		if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) {
+			throw new BadCredentialsException(messages.getMessage(
+					"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
+					includeDetailsObject ? userDetails : null);
+		}
+	}
+
+	protected void doAfterPropertiesSet() throws Exception {
+		if (!isSetUserDetailsServiceInvoked) {
+			autoDetectAnyUserDetailsServiceAndUseIt(this.applicationContext);
+		}
+		Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
+	}
+
+	/**
+	 * Introspects the <code>Applicationcontext</code> for the single instance
+	 * of {@link AccessDeniedHandler}. If found invoke
+	 * setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) method by
+	 * providing the found instance of accessDeniedHandler as a method
+	 * parameter. If more than one instance of <code>AccessDeniedHandler</code>
+	 * is found, the method throws <code>IllegalStateException</code>.
+	 * 
+	 * @param applicationContext to locate the instance
+	 */
+	private void autoDetectAnyUserDetailsServiceAndUseIt(ApplicationContext applicationContext) {
+		if (applicationContext != null) {
+			Map map = applicationContext.getBeansOfType(UserDetailsService.class);
+
+			if (map.size() > 1) {
+				throw new IllegalArgumentException(
+						"More than one UserDetailsService beans detected please refer to the one using "
+								+ " [ principalRepositoryBeanRef  ] " + "attribute");
+			}
+			else if (map.size() == 1) {
+				setUserDetailsService((UserDetailsService) map.values().iterator().next());
+			}
+		}
+	}
+
+	public PasswordEncoder getPasswordEncoder() {
+		return passwordEncoder;
+	}
+
+	public SaltSource getSaltSource() {
+		return saltSource;
+	}
+
+	public UserDetailsService getUserDetailsService() {
+		return userDetailsService;
+	}
+
+	protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
+			throws AuthenticationException {
+		UserDetails loadedUser;
+
+		try {
+			loadedUser = this.getUserDetailsService().loadUserByUsername(username);
+		}
+		catch (DataAccessException repositoryProblem) {
+			throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
+		}
+
+		if (loadedUser == null) {
+			throw new AuthenticationServiceException(
+					"UserDetailsService returned null, which is an interface contract violation");
+		}
+		return loadedUser;
+	}
+
+	/**
+	 * Sets the PasswordEncoder instance to be used to encode and validate
+	 * passwords. If not set, {@link PlaintextPasswordEncoder} will be used by
+	 * default.
+	 * 
+	 * @param passwordEncoder The passwordEncoder to use
+	 */
+	public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
+		this.passwordEncoder = passwordEncoder;
+	}
+
+	/**
+	 * The source of salts to use when decoding passwords. <code>null</code>
+	 * is a valid value, meaning the <code>DaoAuthenticationProvider</code>
+	 * will present <code>null</code> to the relevant
+	 * <code>PasswordEncoder</code>.
+	 * 
+	 * @param saltSource to use when attempting to decode passwords via the
+	 * <code>PasswordEncoder</code>
+	 */
+	public void setSaltSource(SaltSource saltSource) {
+		this.saltSource = saltSource;
+	}
+
+	public void setUserDetailsService(UserDetailsService userDetailsService) {
+		isSetUserDetailsServiceInvoked = true;
+		this.userDetailsService = userDetailsService;
+	}
+
+	public boolean isIncludeDetailsObject() {
+		return includeDetailsObject;
+	}
+
+	public void setIncludeDetailsObject(boolean includeDetailsObject) {
+		this.includeDetailsObject = includeDetailsObject;
+	}
 
 	public void setOrder(int order) {
 		this.order = order;
 	}
 
 	public int getOrder() {
-		return order ;
+		return order;
 	}
-	
-	
+
+	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+		this.applicationContext = applicationContext;
+	}
+
 }

+ 520 - 399
core/src/main/java/org/acegisecurity/ui/AbstractProcessingFilter.java

@@ -27,12 +27,16 @@ import org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEv
 import org.acegisecurity.ui.rememberme.NullRememberMeServices;
 import org.acegisecurity.ui.rememberme.RememberMeServices;
 import org.acegisecurity.ui.savedrequest.SavedRequest;
+import org.acegisecurity.userdetails.UserDetailsService;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
+import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.InitializingBean;
 
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.ApplicationEventPublisherAware;
 import org.springframework.context.MessageSource;
@@ -43,6 +47,7 @@ import org.springframework.util.Assert;
 
 import java.io.IOException;
 
+import java.util.Map;
 import java.util.Properties;
 
 import javax.servlet.Filter;
@@ -54,424 +59,535 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-
 /**
- * Abstract processor of browser-based HTTP-based authentication requests.<p>This filter is responsible for
- * processing authentication requests. If authentication is successful, the resulting {@link Authentication} object
- * will be placed into the <code>SecurityContext</code>, which is guaranteed to have already been created by an
- * earlier filter.</p>
- *  <p>If authentication fails, the <code>AuthenticationException</code> will be placed into the
- * <code>HttpSession</code> with the attribute defined by {@link #ACEGI_SECURITY_LAST_EXCEPTION_KEY}.</p>
- *  <p>To use this filter, it is necessary to specify the following properties:</p>
- *  <ul>
- *      <li><code>defaultTargetUrl</code> indicates the URL that should be used for redirection if the
- *      <code>HttpSession</code> attribute named {@link #ACEGI_SAVED_REQUEST_KEY} does not indicate the target URL once
- *      authentication is completed successfully. eg: <code>/</code>. The <code>defaultTargetUrl</code> will be treated
- *      as relative to the web-app's context path, and should include the leading <code>/</code>. Alternatively,
- *      inclusion of a scheme name (eg http:// or https://) as the prefix will denote a fully-qualified URL and this is
- *      also supported.</li>
- *      <li><code>authenticationFailureUrl</code> indicates the URL that should be used for redirection if the
- *      authentication request fails. eg: <code>/login.jsp?login_error=1</code>.</li>
- *      <li><code>filterProcessesUrl</code> indicates the URL that this filter will respond to. This parameter
- *      varies by subclass.</li>
- *      <li><code>alwaysUseDefaultTargetUrl</code> causes successful authentication to always redirect to the
- *      <code>defaultTargetUrl</code>, even if the <code>HttpSession</code> attribute named {@link
- *      #ACEGI_SAVED_REQUEST_KEY} defines the intended target URL.</li>
- *  </ul>
- *  <p>To configure this filter to redirect to specific pages as the result of specific {@link
- * AuthenticationException}s you can do the following. Configure the <code>exceptionMappings</code> property in your
- * application xml. This property is a java.util.Properties object that maps a fully-qualified exception class name to
- * a redirection url target.
- * For example:
+ * Abstract processor of browser-based HTTP-based authentication requests.
+ * <p>
+ * This filter is responsible for processing authentication requests. If
+ * authentication is successful, the resulting {@link Authentication} object
+ * will be placed into the <code>SecurityContext</code>, which is guaranteed
+ * to have already been created by an earlier filter.
+ * </p>
+ * <p>
+ * If authentication fails, the <code>AuthenticationException</code> will be
+ * placed into the <code>HttpSession</code> with the attribute defined by
+ * {@link #ACEGI_SECURITY_LAST_EXCEPTION_KEY}.
+ * </p>
+ * <p>
+ * To use this filter, it is necessary to specify the following properties:
+ * </p>
+ * <ul>
+ * <li><code>defaultTargetUrl</code> indicates the URL that should be used
+ * for redirection if the <code>HttpSession</code> attribute named
+ * {@link #ACEGI_SAVED_REQUEST_KEY} does not indicate the target URL once
+ * authentication is completed successfully. eg: <code>/</code>. The
+ * <code>defaultTargetUrl</code> will be treated as relative to the web-app's
+ * context path, and should include the leading <code>/</code>.
+ * Alternatively, inclusion of a scheme name (eg http:// or https://) as the
+ * prefix will denote a fully-qualified URL and this is also supported.</li>
+ * <li><code>authenticationFailureUrl</code> indicates the URL that should be
+ * used for redirection if the authentication request fails. eg:
+ * <code>/login.jsp?login_error=1</code>.</li>
+ * <li><code>filterProcessesUrl</code> indicates the URL that this filter
+ * will respond to. This parameter varies by subclass.</li>
+ * <li><code>alwaysUseDefaultTargetUrl</code> causes successful
+ * authentication to always redirect to the <code>defaultTargetUrl</code>,
+ * even if the <code>HttpSession</code> attribute named {@link
+ * #ACEGI_SAVED_REQUEST_KEY} defines the intended target URL.</li>
+ * </ul>
+ * <p>
+ * To configure this filter to redirect to specific pages as the result of
+ * specific {@link AuthenticationException}s you can do the following.
+ * Configure the <code>exceptionMappings</code> property in your application
+ * xml. This property is a java.util.Properties object that maps a
+ * fully-qualified exception class name to a redirection url target. For
+ * example:
+ * 
  * <pre>
- *  &lt;property name="exceptionMappings"&gt;
+ *  &lt;property name=&quot;exceptionMappings&quot;&gt;
  *    &lt;props&gt;
- *      &lt;prop&gt; key="org.acegisecurity.BadCredentialsException"&gt;/bad_credentials.jsp&lt;/prop&gt;
+ *      &lt;prop&gt; key=&quot;org.acegisecurity.BadCredentialsException&quot;&gt;/bad_credentials.jsp&lt;/prop&gt;
  *    &lt;/props&gt;
  *  &lt;/property&gt;
  * </pre>
- * The example above would redirect all {@link org.acegisecurity.BadCredentialsException}s thrown, to a page in the
- * web-application called /bad_credentials.jsp.</p>
- *  <p>Any {@link AuthenticationException} thrown that cannot be matched in the <code>exceptionMappings</code> will
- * be redirected to the <code>authenticationFailureUrl</code></p>
- *  <p>If authentication is successful, an {@link
- * org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent} will be published to the application
- * context. No events will be published if authentication was unsuccessful, because this would generally be recorded
- * via an <code>AuthenticationManager</code>-specific application event.</p>
- *
+ * 
+ * The example above would redirect all
+ * {@link org.acegisecurity.BadCredentialsException}s thrown, to a page in the
+ * web-application called /bad_credentials.jsp.
+ * </p>
+ * <p>
+ * Any {@link AuthenticationException} thrown that cannot be matched in the
+ * <code>exceptionMappings</code> will be redirected to the
+ * <code>authenticationFailureUrl</code>
+ * </p>
+ * <p>
+ * If authentication is successful, an {@link
+ * org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent}
+ * will be published to the application context. No events will be published if
+ * authentication was unsuccessful, because this would generally be recorded via
+ * an <code>AuthenticationManager</code>-specific application event.
+ * </p>
+ * 
  * @author Ben Alex
  * @version $Id$
  */
 public abstract class AbstractProcessingFilter implements Filter, InitializingBean, ApplicationEventPublisherAware,
-    MessageSourceAware {
-    //~ Static fields/initializers =====================================================================================
-
-    public static final String ACEGI_SAVED_REQUEST_KEY = "ACEGI_SAVED_REQUEST_KEY";
-    public static final String ACEGI_SECURITY_LAST_EXCEPTION_KEY = "ACEGI_SECURITY_LAST_EXCEPTION";
-
-    //~ Instance fields ================================================================================================
-
-    protected ApplicationEventPublisher eventPublisher;
-    protected AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
-    private AuthenticationManager authenticationManager;
-    protected final Log logger = LogFactory.getLog(this.getClass());
-    protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
-    private Properties exceptionMappings = new Properties();
-    private RememberMeServices rememberMeServices = new NullRememberMeServices();
-
-    /** Where to redirect the browser to if authentication fails */
-    private String authenticationFailureUrl;
-
-    /**
-     * Where to redirect the browser to if authentication is successful but ACEGI_SAVED_REQUEST_KEY is
-     * <code>null</code>
-     */
-    private String defaultTargetUrl;
-
-    /**
-     * The URL destination that this filter intercepts and processes (usually something like
-     * <code>/j_acegi_security_check</code>)
-     */
-    private String filterProcessesUrl = getDefaultFilterProcessesUrl();
-
-    /**
-     * If <code>true</code>, will always redirect to the value of {@link #getDefaultTargetUrl} upon successful
-     * authentication, irrespective of the page that caused the authentication request (defaults to <code>false</code>).
-     */
-    private boolean alwaysUseDefaultTargetUrl = false;
-
-    /**
-     * Indicates if the filter chain should be continued prior to delegation to {@link
-     * #successfulAuthentication(HttpServletRequest, HttpServletResponse, Authentication)}, which may be useful in
-     * certain environment (eg Tapestry). Defaults to <code>false</code>.
-     */
-    private boolean continueChainBeforeSuccessfulAuthentication = false;
-    
-    /**
-     * Specifies the buffer size to use in the event of a directory. A buffer size is used to ensure the
-     * response is not written back to the client immediately. This provides a way for the <code>HttpSession</code>
-     * to be updated before the browser redirect will be sent. Defaults to an 8 Kb buffer.
-     */
-    private int bufferSize = 8 * 1024;
-    
-    /**
-     * If true, causes any redirection URLs to be calculated minus the protocol and context path (defaults to false).
-     */
-    private boolean useRelativeContext = false;
-
-    //~ Methods ========================================================================================================
-
-    public void afterPropertiesSet() throws Exception {
-        Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
-        Assert.hasLength(defaultTargetUrl, "defaultTargetUrl must be specified");
-        Assert.hasLength(authenticationFailureUrl, "authenticationFailureUrl must be specified");
-        Assert.notNull(authenticationManager, "authenticationManager must be specified");
-        Assert.notNull(this.rememberMeServices);
-    }
-
-    /**
-     * Performs actual authentication.
-     *
-     * @param request from which to extract parameters and perform the authentication
-     *
-     * @return the authenticated user
-     *
-     * @throws AuthenticationException if authentication fails
-     */
-    public abstract Authentication attemptAuthentication(HttpServletRequest request)
-        throws AuthenticationException;
-
-    /**
-     * Does nothing. We use IoC container lifecycle services instead.
-     */
-    public void destroy() {}
-
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
-        throws IOException, ServletException {
-        if (!(request instanceof HttpServletRequest)) {
-            throw new ServletException("Can only process HttpServletRequest");
-        }
-
-        if (!(response instanceof HttpServletResponse)) {
-            throw new ServletException("Can only process HttpServletResponse");
-        }
-
-        HttpServletRequest httpRequest = (HttpServletRequest) request;
-        HttpServletResponse httpResponse = (HttpServletResponse) response;
-
-        if (requiresAuthentication(httpRequest, httpResponse)) {
-            if (logger.isDebugEnabled()) {
-                logger.debug("Request is to process authentication");
-            }
-
-            Authentication authResult;
-
-            try {
-                onPreAuthentication(httpRequest, httpResponse);
-                authResult = attemptAuthentication(httpRequest);
-            } catch (AuthenticationException failed) {
-                // Authentication failed
-                unsuccessfulAuthentication(httpRequest, httpResponse, failed);
-
-                return;
-            }
-
-            // Authentication success
-            if (continueChainBeforeSuccessfulAuthentication) {
-                chain.doFilter(request, response);
-            }
-
-            successfulAuthentication(httpRequest, httpResponse, authResult);
-
-            return;
-        }
-
-        chain.doFilter(request, response);
-    }
-
-    public String getAuthenticationFailureUrl() {
-        return authenticationFailureUrl;
-    }
-
-    public AuthenticationManager getAuthenticationManager() {
-        return authenticationManager;
-    }
-
-    /**
-     * Specifies the default <code>filterProcessesUrl</code> for the implementation.
-     *
-     * @return the default <code>filterProcessesUrl</code>
-     */
-    public abstract String getDefaultFilterProcessesUrl();
-
-    /**
-     * Supplies the default target Url that will be used if no saved request is found or the
-     * <tt>alwaysUseDefaultTargetUrl</tt> propert is set to true.
-     * Override this method of you want to provide a customized default Url (for example if you want different Urls
-     * depending on the authorities of the user who has just logged in).
-     *
-     * @return the defaultTargetUrl property
-     */
-    public String getDefaultTargetUrl() {
-        return defaultTargetUrl;
-    }
-
-    public Properties getExceptionMappings() {
-        return new Properties(exceptionMappings);
-    }
-
-    public String getFilterProcessesUrl() {
-        return filterProcessesUrl;
-    }
-
-    public RememberMeServices getRememberMeServices() {
-        return rememberMeServices;
-    }
-
-    /**
-     * Does nothing. We use IoC container lifecycle services instead.
-     *
-     * @param arg0 ignored
-     *
-     * @throws ServletException ignored
-     */
-    public void init(FilterConfig arg0) throws ServletException {}
-
-    public boolean isAlwaysUseDefaultTargetUrl() {
-        return alwaysUseDefaultTargetUrl;
-    }
-
-    public boolean isContinueChainBeforeSuccessfulAuthentication() {
-        return continueChainBeforeSuccessfulAuthentication;
-    }
-
-    public static String obtainFullRequestUrl(HttpServletRequest request) {
-        SavedRequest savedRequest = (SavedRequest) request.getSession()
-                                                          .getAttribute(AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY);
-
-        return (savedRequest == null) ? null : savedRequest.getFullRequestUrl();
-    }
-
-    protected void onPreAuthentication(HttpServletRequest request, HttpServletResponse response)
-        throws AuthenticationException, IOException {}
-
-    protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
-        Authentication authResult) throws IOException {}
-
-    protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
-        AuthenticationException failed) throws IOException {}
-
-    /**
-     * <p>Indicates whether this filter should attempt to process a login request for the current invocation.</p>
-     *  <p>It strips any parameters from the "path" section of the request URL (such as the jsessionid
-     * parameter in <em>http://host/myapp/index.html;jsessionid=blah</em>) before matching against the
-     * <code>filterProcessesUrl</code> property.</p>
-     *  <p>Subclasses may override for special requirements, such as Tapestry integration.</p>
-     *
-     * @param request as received from the filter chain
-     * @param response as received from the filter chain
-     *
-     * @return <code>true</code> if the filter should attempt authentication, <code>false</code> otherwise
-     */
-    protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
-        String uri = request.getRequestURI();
-        int pathParamIndex = uri.indexOf(';');
-
-        if (pathParamIndex > 0) {
-            // strip everything after the first semi-colon
-            uri = uri.substring(0, pathParamIndex);
-        }
-
-        if ("".equals(request.getContextPath())) {
-        	return uri.endsWith(filterProcessesUrl);
-        }
-        
-        return uri.endsWith(request.getContextPath() + filterProcessesUrl);
-    }
-
-    protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
-        throws IOException {
-        String finalUrl;
-        if (!url.startsWith("http://") && !url.startsWith("https://")) {
-            if (useRelativeContext) {
-                finalUrl = url;
-            } else {
-                finalUrl = request.getContextPath() + url;
-            }
-        } else if (useRelativeContext) {
-            // Calculate the relative URL from the fully qualifed URL, minus the protocol and base context.
-            int len = request.getContextPath().length();
-            int index = url.indexOf(request.getContextPath()) + len;
-            finalUrl = url.substring(index);
-            if (finalUrl.length() > 1 && finalUrl.charAt(0) == '/') {
-                finalUrl = finalUrl.substring( 1 );
-            }
-        } else {
-            finalUrl = url;
-        }
-
-        Assert.isTrue(!response.isCommitted(), "Response already committed; the authentication mechanism must be able to modify buffer size");
-        response.setBufferSize(bufferSize);
-        response.sendRedirect(response.encodeRedirectURL(finalUrl));
-    }
-
-    public void setAlwaysUseDefaultTargetUrl(boolean alwaysUseDefaultTargetUrl) {
-        this.alwaysUseDefaultTargetUrl = alwaysUseDefaultTargetUrl;
-    }
-
-    public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
-        this.eventPublisher = eventPublisher;
-    }
-
-    public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
-        Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
-        this.authenticationDetailsSource = authenticationDetailsSource;
-    }
-
-    public void setAuthenticationFailureUrl(String authenticationFailureUrl) {
-        this.authenticationFailureUrl = authenticationFailureUrl;
-    }
-
-    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
-        this.authenticationManager = authenticationManager;
-    }
-
-    public void setContinueChainBeforeSuccessfulAuthentication(boolean continueChainBeforeSuccessfulAuthentication) {
-        this.continueChainBeforeSuccessfulAuthentication = continueChainBeforeSuccessfulAuthentication;
-    }
-
-    public void setDefaultTargetUrl(String defaultTargetUrl) {
-        Assert.isTrue(defaultTargetUrl.startsWith("/") | defaultTargetUrl.startsWith("http"),
-                "defaultTarget must start with '/' or with 'http(s)'");
-        this.defaultTargetUrl = defaultTargetUrl;
-    }
-
-    public void setExceptionMappings(Properties exceptionMappings) {
-        this.exceptionMappings = exceptionMappings;
-    }
-
-    public void setFilterProcessesUrl(String filterProcessesUrl) {
-        this.filterProcessesUrl = filterProcessesUrl;
-    }
-
-    public void setMessageSource(MessageSource messageSource) {
-        this.messages = new MessageSourceAccessor(messageSource);
-    }
-
-    public void setRememberMeServices(RememberMeServices rememberMeServices) {
-        this.rememberMeServices = rememberMeServices;
-    }
-
-    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
-        Authentication authResult) throws IOException {
-        if (logger.isDebugEnabled()) {
-            logger.debug("Authentication success: " + authResult.toString());
-        }
-
-        SecurityContextHolder.getContext().setAuthentication(authResult);
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Updated SecurityContextHolder to contain the following Authentication: '" + authResult + "'");
-        }
-
-        String targetUrl = determineTargetUrl(request);
-        
-        if (logger.isDebugEnabled()) {
-            logger.debug("Redirecting to target URL from HTTP Session (or default): " + targetUrl);
-        }
-
-        onSuccessfulAuthentication(request, response, authResult);
-
-        rememberMeServices.loginSuccess(request, response, authResult);
-
-        // Fire event
-        if (this.eventPublisher != null) {
-            eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
-        }
-
-        sendRedirect(request, response, targetUrl);
-    }
-    
-    protected String determineTargetUrl(HttpServletRequest request) {
-        // Don't attempt to obtain the url from the saved request if alwaysUsedefaultTargetUrl is set
-        String targetUrl = alwaysUseDefaultTargetUrl ? null : obtainFullRequestUrl(request);
-
-        if (targetUrl == null) {
-            targetUrl = getDefaultTargetUrl();
-        }
-
-        return targetUrl;
-    }
+		MessageSourceAware, ApplicationContextAware {
+	// ~ Static fields/initializers
+	// =====================================================================================
+
+	public static final String ACEGI_SAVED_REQUEST_KEY = "ACEGI_SAVED_REQUEST_KEY";
+
+	public static final String ACEGI_SECURITY_LAST_EXCEPTION_KEY = "ACEGI_SECURITY_LAST_EXCEPTION";
+
+	// ~ Instance fields
+	// ================================================================================================
+
+	protected ApplicationEventPublisher eventPublisher;
+
+	protected AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
+
+	private AuthenticationManager authenticationManager;
+
+	protected final Log logger = LogFactory.getLog(this.getClass());
+
+	protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
+
+	private Properties exceptionMappings = new Properties();
+
+	private RememberMeServices rememberMeServices = new NullRememberMeServices();
+
+	/** Where to redirect the browser to if authentication fails */
+	private String authenticationFailureUrl;
+
+	/**
+	 * Where to redirect the browser to if authentication is successful but
+	 * ACEGI_SAVED_REQUEST_KEY is <code>null</code>
+	 */
+	private String defaultTargetUrl;
+
+	/**
+	 * The URL destination that this filter intercepts and processes (usually
+	 * something like <code>/j_acegi_security_check</code>)
+	 */
+	private String filterProcessesUrl = getDefaultFilterProcessesUrl();
+
+	/**
+	 * If <code>true</code>, will always redirect to the value of
+	 * {@link #getDefaultTargetUrl} upon successful authentication, irrespective
+	 * of the page that caused the authentication request (defaults to
+	 * <code>false</code>).
+	 */
+	private boolean alwaysUseDefaultTargetUrl = false;
+
+	/**
+	 * Indicates if the filter chain should be continued prior to delegation to
+	 * {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse,
+	 * Authentication)}, which may be useful in certain environment (eg
+	 * Tapestry). Defaults to <code>false</code>.
+	 */
+	private boolean continueChainBeforeSuccessfulAuthentication = false;
+
+	/**
+	 * Specifies the buffer size to use in the event of a directory. A buffer
+	 * size is used to ensure the response is not written back to the client
+	 * immediately. This provides a way for the <code>HttpSession</code> to be
+	 * updated before the browser redirect will be sent. Defaults to an 8 Kb
+	 * buffer.
+	 */
+	private int bufferSize = 8 * 1024;
+
+	/**
+	 * If true, causes any redirection URLs to be calculated minus the protocol
+	 * and context path (defaults to false).
+	 */
+	private boolean useRelativeContext = false;
+
+	private ApplicationContext applicationContext;
+
+	private boolean isSetAuthenticationManagerInvoked = false;
+
+	private boolean isSetRememberMeServicesInvoked = false;
+
+	// ~ Methods
+	// ========================================================================================================
+
+	public void afterPropertiesSet() throws Exception {
+		Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
+		Assert.hasLength(defaultTargetUrl, "defaultTargetUrl must be specified");
+		Assert.hasLength(authenticationFailureUrl, "authenticationFailureUrl must be specified");
+		if (!isSetAuthenticationManagerInvoked) {
+			autoDetectAuthenticationManager();
+		}
+		if (!isSetRememberMeServicesInvoked) {
+			autoDetectRememberMeServices();
+		}
+		Assert.notNull(authenticationManager, "authenticationManager must be specified");
+		Assert.notNull(this.rememberMeServices);
+	}
+
+	private void autoDetectRememberMeServices() {
+		if (applicationContext != null) {
+			Map map = applicationContext.getBeansOfType(RememberMeServices.class);
+			if (map.size() > 0) {
+				setRememberMeServices((RememberMeServices) map.values().iterator().next());
+			}      
+		}
+	}
+
+	/**
+	 * Introspects the <code>Applicationcontext</code> for the single instance
+	 * of <code>AuthenticationManager</code>. If found invoke
+	 * setAuthenticationManager method by providing the found instance of
+	 * authenticationManager as a method parameter. If more than one instance of
+	 * <code>AuthenticationManager</code> is found, the method throws
+	 * <code>IllegalStateException</code>.
+	 * 
+	 * @param applicationContext to locate the instance
+	 */
+	private void autoDetectAuthenticationManager() {
+		if (applicationContext != null) {
+			Map map = applicationContext.getBeansOfType(AuthenticationManager.class);
+			if (map.size() > 1) {
+				throw new IllegalArgumentException(
+						"More than one AuthenticationManager beans detected please refer to the one using "
+								+ " [ authenticationManager  ] " + "property");
+			}
+			else if (map.size() == 1) {
+				setAuthenticationManager((AuthenticationManager) map.values().iterator().next());
+			}
+		}
+
+	}
+
+	/**
+	 * Performs actual authentication.
+	 * 
+	 * @param request from which to extract parameters and perform the
+	 * authentication
+	 * 
+	 * @return the authenticated user
+	 * 
+	 * @throws AuthenticationException if authentication fails
+	 */
+	public abstract Authentication attemptAuthentication(HttpServletRequest request) throws AuthenticationException;
+
+	/**
+	 * Does nothing. We use IoC container lifecycle services instead.
+	 */
+	public void destroy() {
+	}
+
+	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
+			ServletException {
+		if (!(request instanceof HttpServletRequest)) {
+			throw new ServletException("Can only process HttpServletRequest");
+		}
+
+		if (!(response instanceof HttpServletResponse)) {
+			throw new ServletException("Can only process HttpServletResponse");
+		}
+
+		HttpServletRequest httpRequest = (HttpServletRequest) request;
+		HttpServletResponse httpResponse = (HttpServletResponse) response;
+
+		if (requiresAuthentication(httpRequest, httpResponse)) {
+			if (logger.isDebugEnabled()) {
+				logger.debug("Request is to process authentication");
+			}
+
+			Authentication authResult;
+
+			try {
+				onPreAuthentication(httpRequest, httpResponse);
+				authResult = attemptAuthentication(httpRequest);
+			}
+			catch (AuthenticationException failed) {
+				// Authentication failed
+				unsuccessfulAuthentication(httpRequest, httpResponse, failed);
+
+				return;
+			}
+
+			// Authentication success
+			if (continueChainBeforeSuccessfulAuthentication) {
+				chain.doFilter(request, response);
+			}
+
+			successfulAuthentication(httpRequest, httpResponse, authResult);
+
+			return;
+		}
+
+		chain.doFilter(request, response);
+	}
+
+	public String getAuthenticationFailureUrl() {
+		return authenticationFailureUrl;
+	}
+
+	public AuthenticationManager getAuthenticationManager() {
+		return authenticationManager;
+	}
+
+	/**
+	 * Specifies the default <code>filterProcessesUrl</code> for the
+	 * implementation.
+	 * 
+	 * @return the default <code>filterProcessesUrl</code>
+	 */
+	public abstract String getDefaultFilterProcessesUrl();
+
+	/**
+	 * Supplies the default target Url that will be used if no saved request is
+	 * found or the <tt>alwaysUseDefaultTargetUrl</tt> propert is set to true.
+	 * Override this method of you want to provide a customized default Url (for
+	 * example if you want different Urls depending on the authorities of the
+	 * user who has just logged in).
+	 * 
+	 * @return the defaultTargetUrl property
+	 */
+	public String getDefaultTargetUrl() {
+		return defaultTargetUrl;
+	}
+
+	public Properties getExceptionMappings() {
+		return new Properties(exceptionMappings);
+	}
+
+	public String getFilterProcessesUrl() {
+		return filterProcessesUrl;
+	}
+
+	public RememberMeServices getRememberMeServices() {
+		return rememberMeServices;
+	}
+
+	/**
+	 * Does nothing. We use IoC container lifecycle services instead.
+	 * 
+	 * @param arg0 ignored
+	 * 
+	 * @throws ServletException ignored
+	 */
+	public void init(FilterConfig arg0) throws ServletException {
+	}
+
+	public boolean isAlwaysUseDefaultTargetUrl() {
+		return alwaysUseDefaultTargetUrl;
+	}
+
+	public boolean isContinueChainBeforeSuccessfulAuthentication() {
+		return continueChainBeforeSuccessfulAuthentication;
+	}
+
+	public static String obtainFullRequestUrl(HttpServletRequest request) {
+		SavedRequest savedRequest = (SavedRequest) request.getSession().getAttribute(
+				AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY);
+
+		return (savedRequest == null) ? null : savedRequest.getFullRequestUrl();
+	}
+
+	protected void onPreAuthentication(HttpServletRequest request, HttpServletResponse response)
+			throws AuthenticationException, IOException {
+	}
+
+	protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
+			Authentication authResult) throws IOException {
+	}
 
-    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
-        AuthenticationException failed) throws IOException {
-        SecurityContextHolder.getContext().setAuthentication(null);
+	protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
+			AuthenticationException failed) throws IOException {
+	}
+
+	/**
+	 * <p>
+	 * Indicates whether this filter should attempt to process a login request
+	 * for the current invocation.
+	 * </p>
+	 * <p>
+	 * It strips any parameters from the "path" section of the request URL (such
+	 * as the jsessionid parameter in
+	 * <em>http://host/myapp/index.html;jsessionid=blah</em>) before matching
+	 * against the <code>filterProcessesUrl</code> property.
+	 * </p>
+	 * <p>
+	 * Subclasses may override for special requirements, such as Tapestry
+	 * integration.
+	 * </p>
+	 * 
+	 * @param request as received from the filter chain
+	 * @param response as received from the filter chain
+	 * 
+	 * @return <code>true</code> if the filter should attempt authentication,
+	 * <code>false</code> otherwise
+	 */
+	protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
+		String uri = request.getRequestURI();
+		int pathParamIndex = uri.indexOf(';');
+
+		if (pathParamIndex > 0) {
+			// strip everything after the first semi-colon
+			uri = uri.substring(0, pathParamIndex);
+		}
+
+		if ("".equals(request.getContextPath())) {
+			return uri.endsWith(filterProcessesUrl);
+		}
+
+		return uri.endsWith(request.getContextPath() + filterProcessesUrl);
+	}
+
+	protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
+			throws IOException {
+		String finalUrl;
+		if (!url.startsWith("http://") && !url.startsWith("https://")) {
+			if (useRelativeContext) {
+				finalUrl = url;
+			}
+			else {
+				finalUrl = request.getContextPath() + url;
+			}
+		}
+		else if (useRelativeContext) {
+			// Calculate the relative URL from the fully qualifed URL, minus the
+			// protocol and base context.
+			int len = request.getContextPath().length();
+			int index = url.indexOf(request.getContextPath()) + len;
+			finalUrl = url.substring(index);
+			if (finalUrl.length() > 1 && finalUrl.charAt(0) == '/') {
+				finalUrl = finalUrl.substring(1);
+			}
+		}
+		else {
+			finalUrl = url;
+		}
+
+		Assert.isTrue(!response.isCommitted(),
+				"Response already committed; the authentication mechanism must be able to modify buffer size");
+		response.setBufferSize(bufferSize);
+		response.sendRedirect(response.encodeRedirectURL(finalUrl));
+	}
+
+	public void setAlwaysUseDefaultTargetUrl(boolean alwaysUseDefaultTargetUrl) {
+		this.alwaysUseDefaultTargetUrl = alwaysUseDefaultTargetUrl;
+	}
+
+	public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
+		this.eventPublisher = eventPublisher;
+	}
+
+	public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
+		Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
+		this.authenticationDetailsSource = authenticationDetailsSource;
+	}
+
+	public void setAuthenticationFailureUrl(String authenticationFailureUrl) {
+		this.authenticationFailureUrl = authenticationFailureUrl;
+	}
+
+	public void setAuthenticationManager(AuthenticationManager authenticationManager) {
+		this.authenticationManager = authenticationManager;
+	}
+
+	public void setContinueChainBeforeSuccessfulAuthentication(boolean continueChainBeforeSuccessfulAuthentication) {
+		this.continueChainBeforeSuccessfulAuthentication = continueChainBeforeSuccessfulAuthentication;
+	}
+
+	public void setDefaultTargetUrl(String defaultTargetUrl) {
+		Assert.isTrue(defaultTargetUrl.startsWith("/") | defaultTargetUrl.startsWith("http"),
+				"defaultTarget must start with '/' or with 'http(s)'");
+		this.defaultTargetUrl = defaultTargetUrl;
+	}
+
+	public void setExceptionMappings(Properties exceptionMappings) {
+		this.exceptionMappings = exceptionMappings;
+	}
 
-        if (logger.isDebugEnabled()) {
-            logger.debug("Updated SecurityContextHolder to contain null Authentication");
-        }
+	public void setFilterProcessesUrl(String filterProcessesUrl) {
+		this.filterProcessesUrl = filterProcessesUrl;
+	}
 
-        String failureUrl = exceptionMappings.getProperty(failed.getClass().getName(), authenticationFailureUrl);
+	public void setMessageSource(MessageSource messageSource) {
+		this.messages = new MessageSourceAccessor(messageSource);
+	}
 
-        if (logger.isDebugEnabled()) {
-            logger.debug("Authentication request failed: " + failed.toString());
-        }
+	public void setRememberMeServices(RememberMeServices rememberMeServices) {
+		this.rememberMeServices = rememberMeServices;
+	}
 
-        try {
-            request.getSession().setAttribute(ACEGI_SECURITY_LAST_EXCEPTION_KEY, failed);
-        } catch (Exception ignored) {}
+	protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
+			Authentication authResult) throws IOException {
+		if (logger.isDebugEnabled()) {
+			logger.debug("Authentication success: " + authResult.toString());
+		}
 
-        onUnsuccessfulAuthentication(request, response, failed);
+		SecurityContextHolder.getContext().setAuthentication(authResult);
 
-        rememberMeServices.loginFail(request, response);
+		if (logger.isDebugEnabled()) {
+			logger.debug("Updated SecurityContextHolder to contain the following Authentication: '" + authResult + "'");
+		}
 
-        sendRedirect(request, response, failureUrl);
-    }
+		String targetUrl = determineTargetUrl(request);
 
-    public AuthenticationDetailsSource getAuthenticationDetailsSource() {
-        // Required due to SEC-310
-        return authenticationDetailsSource;
-    }
+		if (logger.isDebugEnabled()) {
+			logger.debug("Redirecting to target URL from HTTP Session (or default): " + targetUrl);
+		}
+
+		onSuccessfulAuthentication(request, response, authResult);
+
+		rememberMeServices.loginSuccess(request, response, authResult);
+
+		// Fire event
+		if (this.eventPublisher != null) {
+			eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
+		}
+
+		sendRedirect(request, response, targetUrl);
+	}
+
+	protected String determineTargetUrl(HttpServletRequest request) {
+		// Don't attempt to obtain the url from the saved request if
+		// alwaysUsedefaultTargetUrl is set
+		String targetUrl = alwaysUseDefaultTargetUrl ? null : obtainFullRequestUrl(request);
+
+		if (targetUrl == null) {
+			targetUrl = getDefaultTargetUrl();
+		}
+
+		return targetUrl;
+	}
+
+	protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
+			AuthenticationException failed) throws IOException {
+		SecurityContextHolder.getContext().setAuthentication(null);
+
+		if (logger.isDebugEnabled()) {
+			logger.debug("Updated SecurityContextHolder to contain null Authentication");
+		}
+
+		String failureUrl = exceptionMappings.getProperty(failed.getClass().getName(), authenticationFailureUrl);
+
+		if (logger.isDebugEnabled()) {
+			logger.debug("Authentication request failed: " + failed.toString());
+		}
+
+		try {
+			request.getSession().setAttribute(ACEGI_SECURITY_LAST_EXCEPTION_KEY, failed);
+		}
+		catch (Exception ignored) {
+		}
+
+		onUnsuccessfulAuthentication(request, response, failed);
+
+		rememberMeServices.loginFail(request, response);
+
+		sendRedirect(request, response, failureUrl);
+	}
+
+	public AuthenticationDetailsSource getAuthenticationDetailsSource() {
+		// Required due to SEC-310
+		return authenticationDetailsSource;
+	}
 
 	public void setBufferSize(int bufferSize) {
 		this.bufferSize = bufferSize;
@@ -480,4 +596,9 @@ public abstract class AbstractProcessingFilter implements Filter, InitializingBe
 	public void setUseRelativeContext(boolean useRelativeContext) {
 		this.useRelativeContext = useRelativeContext;
 	}
+
+	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+		this.applicationContext = applicationContext;
+	}
+
 }

+ 273 - 177
core/src/main/java/org/acegisecurity/ui/ExceptionTranslationFilter.java

@@ -32,11 +32,15 @@ import org.acegisecurity.util.PortResolverImpl;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
+import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
 
 import org.springframework.util.Assert;
 
 import java.io.IOException;
+import java.util.Map;
 
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
@@ -47,185 +51,277 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-
 /**
- * Handles any <code>AccessDeniedException</code> and <code>AuthenticationException</code> thrown within the filter
- * chain.<p>This filter is necessary because it provides the bridge between Java exceptions and HTTP responses. It
- * is solely concerned with maintaining the user interface. This filter does not do any actual security enforcement.</p>
- *  <p>If an {@link AuthenticationException} is detected, the filter will launch the
- * <code>authenticationEntryPoint</code>. This allows common handling of authentication failures originating from any
- * subclass of {@link org.acegisecurity.intercept.AbstractSecurityInterceptor}.</p>
- *  <p>If an {@link AccessDeniedException} is detected, the filter will determine whether or not the user is an
- * anonymous user. If they are an anonymous user, the <code>authenticationEntryPoint</code> will be launched. If they
- * are not an anonymous user, the filter will delegate to the {@link org.acegisecurity.ui.AccessDeniedHandler}. By
- * default the filter will use {@link org.acegisecurity.ui.AccessDeniedHandlerImpl}.</p>
- *  <p>To use this filter, it is necessary to specify the following properties:</p>
- *  <ul>
- *      <li><code>authenticationEntryPoint</code> indicates the handler that should commence the authentication
- *      process if an <code>AuthenticationException</code> is detected. Note that this may also switch the current
- *      protocol from http to https for an SSL login.</li>
- *      <li><code>portResolver</code> is used to determine the "real" port that a request was received on.</li>
- *  </ul>
- *  <P><B>Do not use this class directly.</B> Instead configure <code>web.xml</code> to use the {@link
- * org.acegisecurity.util.FilterToBeanProxy}.</p>
- *
+ * Handles any <code>AccessDeniedException</code> and
+ * <code>AuthenticationException</code> thrown within the filter chain.
+ * <p>
+ * This filter is necessary because it provides the bridge between Java
+ * exceptions and HTTP responses. It is solely concerned with maintaining the
+ * user interface. This filter does not do any actual security enforcement.
+ * </p>
+ * <p>
+ * If an {@link AuthenticationException} is detected, the filter will launch the
+ * <code>authenticationEntryPoint</code>. This allows common handling of
+ * authentication failures originating from any subclass of
+ * {@link org.acegisecurity.intercept.AbstractSecurityInterceptor}.
+ * </p>
+ * <p>
+ * If an {@link AccessDeniedException} is detected, the filter will determine
+ * whether or not the user is an anonymous user. If they are an anonymous user,
+ * the <code>authenticationEntryPoint</code> will be launched. If they are not
+ * an anonymous user, the filter will delegate to the
+ * {@link org.acegisecurity.ui.AccessDeniedHandler}. By default the filter will
+ * use {@link org.acegisecurity.ui.AccessDeniedHandlerImpl}.
+ * </p>
+ * <p>
+ * To use this filter, it is necessary to specify the following properties:
+ * </p>
+ * <ul>
+ * <li><code>authenticationEntryPoint</code> indicates the handler that
+ * should commence the authentication process if an
+ * <code>AuthenticationException</code> is detected. Note that this may also
+ * switch the current protocol from http to https for an SSL login.</li>
+ * <li><code>portResolver</code> is used to determine the "real" port that a
+ * request was received on.</li>
+ * </ul>
+ * <P>
+ * <B>Do not use this class directly.</B> Instead configure
+ * <code>web.xml</code> to use the {@link
+ * org.acegisecurity.util.FilterToBeanProxy}.
+ * </p>
+ * 
  * @author Ben Alex
  * @author colin sampaleanu
- * @version $Id$
+ * @version $Id: ExceptionTranslationFilter.java 1496 2006-05-23 13:38:33Z
+ * benalex $
  */
-public class ExceptionTranslationFilter implements Filter, InitializingBean {
-    //~ Static fields/initializers =====================================================================================
-
-    private static final Log logger = LogFactory.getLog(ExceptionTranslationFilter.class);
-
-    //~ Instance fields ================================================================================================
-
-    private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
-    private AuthenticationEntryPoint authenticationEntryPoint;
-    private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
-    private PortResolver portResolver = new PortResolverImpl();
-    private boolean createSessionAllowed = true;
-
-    //~ Methods ========================================================================================================
-
-    public void afterPropertiesSet() throws Exception {
-        Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint must be specified");
-        Assert.notNull(portResolver, "portResolver must be specified");
-        Assert.notNull(authenticationTrustResolver, "authenticationTrustResolver must be specified");
-    }
-
-    public void destroy() {}
-
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
-        throws IOException, ServletException {
-        if (!(request instanceof HttpServletRequest)) {
-            throw new ServletException("HttpServletRequest required");
-        }
-
-        if (!(response instanceof HttpServletResponse)) {
-            throw new ServletException("HttpServletResponse required");
-        }
-
-        try {
-            chain.doFilter(request, response);
-
-            if (logger.isDebugEnabled()) {
-                logger.debug("Chain processed normally");
-            }
-        } catch (AuthenticationException ex) {
-            handleException(request, response, chain, ex);
-        } catch (AccessDeniedException ex) {
-            handleException(request, response, chain, ex);
-        } catch (ServletException ex) {
-            if (ex.getRootCause() instanceof AuthenticationException
-                || ex.getRootCause() instanceof AccessDeniedException) {
-                handleException(request, response, chain, (AcegiSecurityException) ex.getRootCause());
-            } else {
-                throw ex;
-            }
-        } catch (IOException ex) {
-            throw ex;
-        }
-    }
-
-    public AuthenticationEntryPoint getAuthenticationEntryPoint() {
-        return authenticationEntryPoint;
-    }
-
-    public AuthenticationTrustResolver getAuthenticationTrustResolver() {
-        return authenticationTrustResolver;
-    }
-
-    public PortResolver getPortResolver() {
-        return portResolver;
-    }
-
-    private void handleException(ServletRequest request, ServletResponse response, FilterChain chain,
-        AcegiSecurityException exception) throws IOException, ServletException {
-        if (exception instanceof AuthenticationException) {
-            if (logger.isDebugEnabled()) {
-                logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);
-            }
-
-            sendStartAuthentication(request, response, chain, (AuthenticationException) exception);
-        } else if (exception instanceof AccessDeniedException) {
-            if (authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication())) {
-                if (logger.isDebugEnabled()) {
-                    logger.debug("Access is denied (user is anonymous); redirecting to authentication entry point",
-                        exception);
-                }
-
-                sendStartAuthentication(request, response, chain,
-                    new InsufficientAuthenticationException("Full authentication is required to access this resource"));
-            } else {
-                if (logger.isDebugEnabled()) {
-                    logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler",
-                        exception);
-                }
-
-                accessDeniedHandler.handle(request, response, (AccessDeniedException) exception);
-            }
-        }
-    }
-
-    public void init(FilterConfig filterConfig) throws ServletException {}
-
-    /**
-     * If <code>true</code>, indicates that <code>SecurityEnforcementFilter</code> is permitted to store the
-     * target URL and exception information in the <code>HttpSession</code> (the default). In situations where you do
-     * not wish to unnecessarily create <code>HttpSession</code>s - because the user agent will know the failed URL,
-     * such as with BASIC or Digest authentication - you may wish to set this property to <code>false</code>. Remember
-     * to also set the {@link org.acegisecurity.context.HttpSessionContextIntegrationFilter#allowSessionCreation} to
-     * <code>false</code> if you set this property to <code>false</code>.
-     *
-     * @return <code>true</code> if the <code>HttpSession</code> will be used to store information about the failed
-     *         request, <code>false</code> if the <code>HttpSession</code> will not be used
-     */
-    public boolean isCreateSessionAllowed() {
-        return createSessionAllowed;
-    }
-
-    protected void sendStartAuthentication(ServletRequest request, ServletResponse response, FilterChain chain,
-        AuthenticationException reason) throws ServletException, IOException {
-        HttpServletRequest httpRequest = (HttpServletRequest) request;
-
-        SavedRequest savedRequest = new SavedRequest(httpRequest, portResolver);
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Authentication entry point being called; SavedRequest added to Session: " + savedRequest);
-        }
-
-        if (createSessionAllowed) {
-            // Store the HTTP request itself. Used by AbstractProcessingFilter
-            // for redirection after successful authentication (SEC-29)
-            httpRequest.getSession().setAttribute(AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY, savedRequest);
-        }
-
-        // SEC-112: Clear the SecurityContextHolder's Authentication, as the
-        // existing Authentication is no longer considered valid
-        SecurityContextHolder.getContext().setAuthentication(null);
-
-        authenticationEntryPoint.commence(httpRequest, (HttpServletResponse) response, reason);
-    }
-
-    public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
-        Assert.notNull(accessDeniedHandler, "AccessDeniedHandler required");
-        this.accessDeniedHandler = accessDeniedHandler;
-    }
-
-    public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
-        this.authenticationEntryPoint = authenticationEntryPoint;
-    }
-
-    public void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) {
-        this.authenticationTrustResolver = authenticationTrustResolver;
-    }
-
-    public void setCreateSessionAllowed(boolean createSessionAllowed) {
-        this.createSessionAllowed = createSessionAllowed;
-    }
-
-    public void setPortResolver(PortResolver portResolver) {
-        this.portResolver = portResolver;
-    }
+public class ExceptionTranslationFilter implements Filter, InitializingBean, ApplicationContextAware {
+	// ~ Static fields/initializers
+	// =====================================================================================
+
+	private static final Log logger = LogFactory.getLog(ExceptionTranslationFilter.class);
+
+	// ~ Instance fields
+	// ================================================================================================
+
+	private AccessDeniedHandler accessDeniedHandler;
+
+	private AuthenticationEntryPoint authenticationEntryPoint;
+
+	private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
+
+	private PortResolver portResolver = new PortResolverImpl();
+
+	private boolean createSessionAllowed = true;
+
+	/*
+	 * applicationContext will be inject as a part of the contract of
+	 * ApplicationContextAware interface
+	 */
+	private ApplicationContext applicationContext;
+
+	/*
+	 * boolean field to track if setter for accessDeniedHandler is invoked. If
+	 * invoked the default value changes to true
+	 */
+	private boolean isSetAcessDeniedHandlerInvoked = false;
+
+	// ~ Methods
+	// ========================================================================================================
+
+	public void afterPropertiesSet() throws Exception {
+		Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint must be specified");
+		Assert.notNull(portResolver, "portResolver must be specified");
+		Assert.notNull(authenticationTrustResolver, "authenticationTrustResolver must be specified");
+
+		// autodetect AccessDeniedHandler instance in the applicationcontext if
+		// it wasn't injected.
+		if (!isSetAcessDeniedHandlerInvoked) {
+			if (applicationContext != null) {
+				autoDetectAnyAccessDeniedHandlerAndUseIt(applicationContext);
+			}
+		}
+	}
+
+	/**
+	 * Introspects the <code>Applicationcontext</code> for the single instance
+	 * of {@link AccessDeniedHandler}. If found invoke
+	 * setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) method by
+	 * providing the found instance of accessDeniedHandler as a method
+	 * parameter. If more than one instance of <code>AccessDeniedHandler</code>
+	 * is found, the method throws <code>IllegalStateException</code>.
+	 * 
+	 * @param applicationContext to locate the instance
+	 */
+	private void autoDetectAnyAccessDeniedHandlerAndUseIt(ApplicationContext applicationContext) {
+		Map map = applicationContext.getBeansOfType(AccessDeniedHandler.class);
+		if (map.size() > 1) {
+			throw new IllegalArgumentException(
+					"More than one AccessDeniedHandler beans detected please refer to the one using "
+							+ " [ accessDeniedBeanRef  ] " + "attribute");
+		}
+		else if (map.size() == 1) {
+			AccessDeniedHandler handler = (AccessDeniedHandlerImpl) map.values().iterator().next();
+			setAccessDeniedHandler(handler);
+		}
+		else {
+			// create and use the default one specified as an instance variable.
+			accessDeniedHandler = new AccessDeniedHandlerImpl();
+		}
+
+	}
+
+	public void destroy() {
+	}
+
+	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
+			ServletException {
+		if (!(request instanceof HttpServletRequest)) {
+			throw new ServletException("HttpServletRequest required");
+		}
+
+		if (!(response instanceof HttpServletResponse)) {
+			throw new ServletException("HttpServletResponse required");
+		}
+
+		try {
+			chain.doFilter(request, response);
+
+			if (logger.isDebugEnabled()) {
+				logger.debug("Chain processed normally");
+			}
+		}
+		catch (AuthenticationException ex) {
+			handleException(request, response, chain, ex);
+		}
+		catch (AccessDeniedException ex) {
+			handleException(request, response, chain, ex);
+		}
+		catch (ServletException ex) {
+			if (ex.getRootCause() instanceof AuthenticationException
+					|| ex.getRootCause() instanceof AccessDeniedException) {
+				handleException(request, response, chain, (AcegiSecurityException) ex.getRootCause());
+			}
+			else {
+				throw ex;
+			}
+		}
+		catch (IOException ex) {
+			throw ex;
+		}
+	}
+
+	public AuthenticationEntryPoint getAuthenticationEntryPoint() {
+		return authenticationEntryPoint;
+	}
+
+	public AuthenticationTrustResolver getAuthenticationTrustResolver() {
+		return authenticationTrustResolver;
+	}
+
+	public PortResolver getPortResolver() {
+		return portResolver;
+	}
+
+	private void handleException(ServletRequest request, ServletResponse response, FilterChain chain,
+			AcegiSecurityException exception) throws IOException, ServletException {
+		if (exception instanceof AuthenticationException) {
+			if (logger.isDebugEnabled()) {
+				logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);
+			}
+
+			sendStartAuthentication(request, response, chain, (AuthenticationException) exception);
+		}
+		else if (exception instanceof AccessDeniedException) {
+			if (authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication())) {
+				if (logger.isDebugEnabled()) {
+					logger.debug("Access is denied (user is anonymous); redirecting to authentication entry point",
+							exception);
+				}
+
+				sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException(
+						"Full authentication is required to access this resource"));
+			}
+			else {
+				if (logger.isDebugEnabled()) {
+					logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler",
+							exception);
+				}
+
+				accessDeniedHandler.handle(request, response, (AccessDeniedException) exception);
+			}
+		}
+	}
+
+	public void init(FilterConfig filterConfig) throws ServletException {
+	}
+
+	/**
+	 * If <code>true</code>, indicates that
+	 * <code>SecurityEnforcementFilter</code> is permitted to store the target
+	 * URL and exception information in the <code>HttpSession</code> (the
+	 * default). In situations where you do not wish to unnecessarily create
+	 * <code>HttpSession</code>s - because the user agent will know the
+	 * failed URL, such as with BASIC or Digest authentication - you may wish to
+	 * set this property to <code>false</code>. Remember to also set the
+	 * {@link org.acegisecurity.context.HttpSessionContextIntegrationFilter#allowSessionCreation}
+	 * to <code>false</code> if you set this property to <code>false</code>.
+	 * 
+	 * @return <code>true</code> if the <code>HttpSession</code> will be
+	 * used to store information about the failed request, <code>false</code>
+	 * if the <code>HttpSession</code> will not be used
+	 */
+	public boolean isCreateSessionAllowed() {
+		return createSessionAllowed;
+	}
+
+	protected void sendStartAuthentication(ServletRequest request, ServletResponse response, FilterChain chain,
+			AuthenticationException reason) throws ServletException, IOException {
+		HttpServletRequest httpRequest = (HttpServletRequest) request;
+
+		SavedRequest savedRequest = new SavedRequest(httpRequest, portResolver);
+
+		if (logger.isDebugEnabled()) {
+			logger.debug("Authentication entry point being called; SavedRequest added to Session: " + savedRequest);
+		}
+
+		if (createSessionAllowed) {
+			// Store the HTTP request itself. Used by AbstractProcessingFilter
+			// for redirection after successful authentication (SEC-29)
+			httpRequest.getSession().setAttribute(AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY, savedRequest);
+		}
+
+		// SEC-112: Clear the SecurityContextHolder's Authentication, as the
+		// existing Authentication is no longer considered valid
+		SecurityContextHolder.getContext().setAuthentication(null);
+
+		authenticationEntryPoint.commence(httpRequest, (HttpServletResponse) response, reason);
+	}
+
+	public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
+		Assert.notNull(accessDeniedHandler, "AccessDeniedHandler required");
+		this.accessDeniedHandler = accessDeniedHandler;
+		this.isSetAcessDeniedHandlerInvoked = true;
+	}
+
+	public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
+		this.authenticationEntryPoint = authenticationEntryPoint;
+	}
+
+	public void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) {
+		this.authenticationTrustResolver = authenticationTrustResolver;
+	}
+
+	public void setCreateSessionAllowed(boolean createSessionAllowed) {
+		this.createSessionAllowed = createSessionAllowed;
+	}
+
+	public void setPortResolver(PortResolver portResolver) {
+		this.portResolver = portResolver;
+	}
+
+	public void setApplicationContext(ApplicationContext applicationContext) {
+		this.applicationContext = applicationContext;
+	}
+
 }

+ 154 - 130
core/src/main/java/org/acegisecurity/ui/logout/LogoutFilter.java

@@ -15,15 +15,6 @@
 
 package org.acegisecurity.ui.logout;
 
-import org.acegisecurity.Authentication;
-
-import org.acegisecurity.context.SecurityContextHolder;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.springframework.util.Assert;
-
 import java.io.IOException;
 
 import javax.servlet.Filter;
@@ -35,130 +26,163 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.acegisecurity.Authentication;
+import org.acegisecurity.context.SecurityContextHolder;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.util.Assert;
 
 /**
- * Logs a principal out.<p>Polls a series of {@link LogoutHandler}s. The handlers should be specified in the order
- * they are required. Generally you will want to call logout handlers <code>TokenBasedRememberMeServices</code> and
- * <code>SecurityContextLogoutHandler</code> (in that order).</p>
- *  <p>After logout, the URL specified by {@link #logoutSuccessUrl} will be shown.</p>
- *  <p><b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the {@link
- * org.acegisecurity.util.FilterToBeanProxy}.</p>
- *
+ * Logs a principal out.
+ * <p>
+ * Polls a series of {@link LogoutHandler}s. The handlers should be specified
+ * in the order they are required. Generally you will want to call logout
+ * handlers <code>TokenBasedRememberMeServices</code> and
+ * <code>SecurityContextLogoutHandler</code> (in that order).
+ * </p>
+ * <p>
+ * After logout, the URL specified by {@link #logoutSuccessUrl} will be shown.
+ * </p>
+ * <p>
+ * <b>Do not use this class directly.</b> Instead configure
+ * <code>web.xml</code> to use the {@link
+ * org.acegisecurity.util.FilterToBeanProxy}.
+ * </p>
+ * 
  * @author Ben Alex
  * @version $Id$
  */
-public class LogoutFilter implements Filter {
-    //~ Static fields/initializers =====================================================================================
-
-    private static final Log logger = LogFactory.getLog(LogoutFilter.class);
-
-    //~ Instance fields ================================================================================================
-
-    private String filterProcessesUrl = "/j_acegi_logout";
-    private String logoutSuccessUrl;
-    private LogoutHandler[] handlers;
-
-    //~ Constructors ===================================================================================================
-
-    public LogoutFilter(String logoutSuccessUrl, LogoutHandler[] handlers) {
-        Assert.hasText(logoutSuccessUrl, "LogoutSuccessUrl required");
-        Assert.notEmpty(handlers, "LogoutHandlers are required");
-        this.logoutSuccessUrl = logoutSuccessUrl;
-        this.handlers = handlers;
-    }
-
-    //~ Methods ========================================================================================================
-
-    /**
-     * Not used. Use IoC container lifecycle methods instead.
-     */
-    public void destroy() {}
-
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
-        throws IOException, ServletException {
-        if (!(request instanceof HttpServletRequest)) {
-            throw new ServletException("Can only process HttpServletRequest");
-        }
-
-        if (!(response instanceof HttpServletResponse)) {
-            throw new ServletException("Can only process HttpServletResponse");
-        }
-
-        HttpServletRequest httpRequest = (HttpServletRequest) request;
-        HttpServletResponse httpResponse = (HttpServletResponse) response;
-
-        if (requiresLogout(httpRequest, httpResponse)) {
-            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
-
-            if (logger.isDebugEnabled()) {
-                logger.debug("Logging out user '" + auth + "' and redirecting to logout page");
-            }
-
-            for (int i = 0; i < handlers.length; i++) {
-                handlers[i].logout(httpRequest, httpResponse, auth);
-            }
-
-            sendRedirect(httpRequest, httpResponse, logoutSuccessUrl);
-
-            return;
-        }
-
-        chain.doFilter(request, response);
-    }
-
-    /**
-     * Not used. Use IoC container lifecycle methods instead.
-     *
-     * @param arg0 ignored
-     *
-     * @throws ServletException ignored
-     */
-    public void init(FilterConfig arg0) throws ServletException {}
-
-    /**
-     * Allow subclasses to modify when a logout should tak eplace.
-     *
-     * @param request the request
-     * @param response the response
-     *
-     * @return <code>true</code> if logout should occur, <code>false</code> otherwise
-     */
-    protected boolean requiresLogout(HttpServletRequest request, HttpServletResponse response) {
-        String uri = request.getRequestURI();
-        int pathParamIndex = uri.indexOf(';');
-
-        if (pathParamIndex > 0) {
-            // strip everything after the first semi-colon
-            uri = uri.substring(0, pathParamIndex);
-        }
-
-        if ("".equals(request.getContextPath())) {
-        	return uri.endsWith(filterProcessesUrl);
-        }
-        
-        return uri.endsWith(request.getContextPath() + filterProcessesUrl);
-    }
-
-    /**
-     * Allow subclasses to modify the redirection message.
-     *
-     * @param request the request
-     * @param response the response
-     * @param url the URL to redirect to
-     *
-     * @throws IOException in the event of any failure
-     */
-    protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
-        throws IOException {
-        if (!url.startsWith("http://") && !url.startsWith("https://")) {
-            url = request.getContextPath() + url;
-        }
-
-        response.sendRedirect(response.encodeRedirectURL(url));
-    }
-
-    public void setFilterProcessesUrl(String filterProcessesUrl) {
-        Assert.hasText(filterProcessesUrl, "FilterProcessesUrl required");
-        this.filterProcessesUrl = filterProcessesUrl;
-    }
+public class LogoutFilter implements Filter, ApplicationContextAware {
+	// ~ Static fields/initializers
+	// =====================================================================================
+
+	private static final Log logger = LogFactory.getLog(LogoutFilter.class);
+
+	// ~ Instance fields
+	// ================================================================================================
+
+	private String filterProcessesUrl = "/j_acegi_logout";
+
+	private String logoutSuccessUrl;
+
+	private LogoutHandler[] handlers;
+
+	private ApplicationContext applicationContext;
+
+	// ~ Constructors
+	// ===================================================================================================
+
+	public LogoutFilter(String logoutSuccessUrl, LogoutHandler[] handlers) {
+		Assert.hasText(logoutSuccessUrl, "LogoutSuccessUrl required");
+		Assert.notEmpty(handlers, "LogoutHandlers are required");
+		this.logoutSuccessUrl = logoutSuccessUrl;
+		this.handlers = handlers;
+	}
+
+	// ~ Methods
+	// ========================================================================================================
+
+	/**
+	 * Not used. Use IoC container lifecycle methods instead.
+	 */
+	public void destroy() {
+	}
+
+	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
+			ServletException {
+		if (!(request instanceof HttpServletRequest)) {
+			throw new ServletException("Can only process HttpServletRequest");
+		}
+
+		if (!(response instanceof HttpServletResponse)) {
+			throw new ServletException("Can only process HttpServletResponse");
+		}
+
+		HttpServletRequest httpRequest = (HttpServletRequest) request;
+		HttpServletResponse httpResponse = (HttpServletResponse) response;
+
+		if (requiresLogout(httpRequest, httpResponse)) {
+			Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+
+			if (logger.isDebugEnabled()) {
+				logger.debug("Logging out user '" + auth + "' and redirecting to logout page");
+			}
+
+			for (int i = 0; i < handlers.length; i++) {
+				handlers[i].logout(httpRequest, httpResponse, auth);
+			}
+
+			sendRedirect(httpRequest, httpResponse, logoutSuccessUrl);
+
+			return;
+		}
+
+		chain.doFilter(request, response);
+	}
+
+	/**
+	 * Not used. Use IoC container lifecycle methods instead.
+	 * 
+	 * @param arg0 ignored
+	 * 
+	 * @throws ServletException ignored
+	 */
+	public void init(FilterConfig arg0) throws ServletException {
+	}
+
+	/**
+	 * Allow subclasses to modify when a logout should tak eplace.
+	 * 
+	 * @param request the request
+	 * @param response the response
+	 * 
+	 * @return <code>true</code> if logout should occur, <code>false</code>
+	 * otherwise
+	 */
+	protected boolean requiresLogout(HttpServletRequest request, HttpServletResponse response) {
+		String uri = request.getRequestURI();
+		int pathParamIndex = uri.indexOf(';');
+
+		if (pathParamIndex > 0) {
+			// strip everything after the first semi-colon
+			uri = uri.substring(0, pathParamIndex);
+		}
+
+		if ("".equals(request.getContextPath())) {
+			return uri.endsWith(filterProcessesUrl);
+		}
+
+		return uri.endsWith(request.getContextPath() + filterProcessesUrl);
+	}
+
+	/**
+	 * Allow subclasses to modify the redirection message.
+	 * 
+	 * @param request the request
+	 * @param response the response
+	 * @param url the URL to redirect to
+	 * 
+	 * @throws IOException in the event of any failure
+	 */
+	protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
+			throws IOException {
+		if (!url.startsWith("http://") && !url.startsWith("https://")) {
+			url = request.getContextPath() + url;
+		}
+
+		response.sendRedirect(response.encodeRedirectURL(url));
+	}
+
+	public void setFilterProcessesUrl(String filterProcessesUrl) {
+		Assert.hasText(filterProcessesUrl, "FilterProcessesUrl required");
+		this.filterProcessesUrl = filterProcessesUrl;
+	}
+
+	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+		this.applicationContext = applicationContext;
+	}
+
 }

+ 3 - 1
core/src/main/java/org/acegisecurity/ui/logout/SecurityContextLogoutHandler.java

@@ -44,7 +44,9 @@ public class SecurityContextLogoutHandler implements LogoutHandler, Ordered {
 
 	private boolean invalidateHttpSession = true;
 
-	private int order = Integer.MAX_VALUE; //~ default
+	private int DEFAULT_ORDER = Integer.MAX_VALUE; // ~ default
+
+	private int order = DEFAULT_ORDER;
 
 	/**
 	 * Requires the request to be passed in.

+ 60 - 4
core/src/main/java/org/acegisecurity/ui/rememberme/RememberMeProcessingFilter.java

@@ -26,14 +26,18 @@ import org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEv
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
+import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.InitializingBean;
 
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.ApplicationEventPublisherAware;
 
 import org.springframework.util.Assert;
 
 import java.io.IOException;
+import java.util.Map;
 
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
@@ -64,7 +68,7 @@ import javax.servlet.http.HttpServletResponse;
  * @author Ben Alex
  * @version $Id$
  */
-public class RememberMeProcessingFilter implements Filter, InitializingBean, ApplicationEventPublisherAware {
+public class RememberMeProcessingFilter implements Filter, InitializingBean, ApplicationEventPublisherAware, ApplicationContextAware {
     //~ Static fields/initializers =====================================================================================
 
     private static final Log logger = LogFactory.getLog(RememberMeProcessingFilter.class);
@@ -75,12 +79,59 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean, App
     private AuthenticationManager authenticationManager;
     private RememberMeServices rememberMeServices = new NullRememberMeServices();
 
+	private ApplicationContext applicationContext;
+
+	private boolean isSetAuthenticationManagerInvoked = false;
+
+	private boolean isSetRememberMeServicesInvoked = false;
+
     //~ Methods ========================================================================================================
 
     public void afterPropertiesSet() throws Exception {
-        Assert.notNull(rememberMeServices, "RememberMeServices required");
-        Assert.notNull(authenticationManager, "AuthenticationManager required");
-    }
+    	if (!isSetAuthenticationManagerInvoked) {
+			autoDetectAuthenticationManager();
+		}
+		if (!isSetRememberMeServicesInvoked ) {
+			autoDetectRememberMeServices();
+		}
+		Assert.notNull(authenticationManager, "authenticationManager must be specified");
+		Assert.notNull(this.rememberMeServices);
+	}
+
+	private void autoDetectRememberMeServices() {
+		if (applicationContext != null) {
+			Map map = applicationContext.getBeansOfType(RememberMeServices.class);
+			if (map.size() > 0) {
+				setRememberMeServices((RememberMeServices) map.values().iterator().next());
+			}      
+		}
+	}
+
+	/**
+	 * Introspects the <code>Applicationcontext</code> for the single instance
+	 * of <code>AuthenticationManager</code>. If found invoke
+	 * setAuthenticationManager method by providing the found instance of
+	 * authenticationManager as a method parameter. If more than one instance of
+	 * <code>AuthenticationManager</code> is found, the method throws
+	 * <code>IllegalStateException</code>.
+	 * 
+	 * @param applicationContext to locate the instance
+	 */
+	private void autoDetectAuthenticationManager() {
+		if (applicationContext != null) {
+			Map map = applicationContext.getBeansOfType(AuthenticationManager.class);
+			if (map.size() > 1) {
+				throw new IllegalArgumentException(
+						"More than one AuthenticationManager beans detected please refer to the one using "
+								+ " [ authenticationManager  ] " + "property");
+			}
+			else if (map.size() == 1) {
+				setAuthenticationManager((AuthenticationManager) map.values().iterator().next());
+			}
+		}
+
+	}
+    
 
     /**
      * Does nothing - we rely on IoC lifecycle services instead.
@@ -167,4 +218,9 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean, App
     public void setRememberMeServices(RememberMeServices rememberMeServices) {
         this.rememberMeServices = rememberMeServices;
     }
+
+	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+		this.applicationContext=applicationContext;
+	}
+    
 }

+ 393 - 304
core/src/main/java/org/acegisecurity/ui/rememberme/TokenBasedRememberMeServices.java

@@ -16,6 +16,7 @@
 package org.acegisecurity.ui.rememberme;
 
 import java.util.Date;
+import java.util.Map;
 
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
@@ -23,6 +24,8 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.acegisecurity.Authentication;
 import org.acegisecurity.providers.rememberme.RememberMeAuthenticationToken;
+import org.acegisecurity.ui.AccessDeniedHandler;
+import org.acegisecurity.ui.AccessDeniedHandlerImpl;
 import org.acegisecurity.ui.AuthenticationDetailsSource;
 import org.acegisecurity.ui.AuthenticationDetailsSourceImpl;
 import org.acegisecurity.ui.logout.LogoutHandler;
@@ -33,337 +36,419 @@ import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
 import org.springframework.core.Ordered;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 import org.springframework.web.bind.RequestUtils;
 
-
 /**
  * Identifies previously remembered users by a Base-64 encoded cookie.
- *
+ * 
  * <p>
- * This implementation does not rely on an external database, so is attractive for simple applications.
- * The cookie will be valid for a specific period from the date of the last
- * {@link #loginSuccess(HttpServletRequest, HttpServletResponse, Authentication)}. As per the
- * interface contract, this method will only be called when the principal completes a successful interactive
- * authentication. As such the time period commences from the last authentication attempt where they furnished
- * credentials - not the time period they last logged in via remember-me. The implementation will only send a
- * remember-me token if the parameter defined by {@link #setParameter(String)} is present.</p>
- *
- * <p>An {@link org.acegisecurity.userdetails.UserDetailsService} is required by this implementation, so that it
- * can construct a valid <code>Authentication</code> from the returned {@link
- * org.acegisecurity.userdetails.UserDetails}. This is also necessary so that the user's password is available and can
- * be checked as part of the encoded cookie.</p>
- *
+ * This implementation does not rely on an external database, so is attractive
+ * for simple applications. The cookie will be valid for a specific period from
+ * the date of the last
+ * {@link #loginSuccess(HttpServletRequest, HttpServletResponse, Authentication)}.
+ * As per the interface contract, this method will only be called when the
+ * principal completes a successful interactive authentication. As such the time
+ * period commences from the last authentication attempt where they furnished
+ * credentials - not the time period they last logged in via remember-me. The
+ * implementation will only send a remember-me token if the parameter defined by
+ * {@link #setParameter(String)} is present.
+ * </p>
+ * 
+ * <p>
+ * An {@link org.acegisecurity.userdetails.UserDetailsService} is required by
+ * this implementation, so that it can construct a valid
+ * <code>Authentication</code> from the returned {@link
+ * org.acegisecurity.userdetails.UserDetails}. This is also necessary so that
+ * the user's password is available and can be checked as part of the encoded
+ * cookie.
+ * </p>
+ * 
  * <p>
  * The cookie encoded by this implementation adopts the following form:
- * <pre>username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)</pre>
+ * 
+ * <pre>
+ * username + &quot;:&quot; + expiryTime + &quot;:&quot; + Md5Hex(username + &quot;:&quot; + expiryTime + &quot;:&quot; + password + &quot;:&quot; + key)
+ * </pre>
+ * 
  * </p>
  * <p>
- * As such, if the user changes their password any remember-me token will be invalidated. Equally, the system
- * administrator may invalidate every remember-me token on issue by changing the key. This provides some reasonable
- * approaches to recovering from a remember-me token being left on a public machine (eg kiosk system, Internet cafe
- * etc). Most importantly, at no time is the user's password ever sent to the user agent, providing an important
- * security safeguard. Unfortunately the username is necessary in this implementation (as we do not want to rely on a
- * database for remember-me services) and as such high security applications should be aware of this occasionally
- * undesired disclosure of a valid username.
+ * As such, if the user changes their password any remember-me token will be
+ * invalidated. Equally, the system administrator may invalidate every
+ * remember-me token on issue by changing the key. This provides some reasonable
+ * approaches to recovering from a remember-me token being left on a public
+ * machine (eg kiosk system, Internet cafe etc). Most importantly, at no time is
+ * the user's password ever sent to the user agent, providing an important
+ * security safeguard. Unfortunately the username is necessary in this
+ * implementation (as we do not want to rely on a database for remember-me
+ * services) and as such high security applications should be aware of this
+ * occasionally undesired disclosure of a valid username.
  * </p>
  * <p>
- * This is a basic remember-me implementation which is suitable for many applications. However, we recommend a
- * database-based implementation if you require a more secure remember-me approach.
+ * This is a basic remember-me implementation which is suitable for many
+ * applications. However, we recommend a database-based implementation if you
+ * require a more secure remember-me approach.
  * </p>
  * <p>
- * By default the tokens will be valid for 14 days from the last successful authentication attempt. This can be
- * changed using {@link #setTokenValiditySeconds(long)}.
+ * By default the tokens will be valid for 14 days from the last successful
+ * authentication attempt. This can be changed using
+ * {@link #setTokenValiditySeconds(long)}.
  * </p>
- *
+ * 
  * @author Ben Alex
- * @version $Id$
+ * @version $Id: TokenBasedRememberMeServices.java 1871 2007-05-25 03:12:49Z
+ * benalex $
  */
-public class TokenBasedRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler, Ordered {
-    //~ Static fields/initializers =====================================================================================
-
-    public static final String ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY = "ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE";
-    public static final String DEFAULT_PARAMETER = "_acegi_security_remember_me";
-    protected static final Log logger = LogFactory.getLog(TokenBasedRememberMeServices.class);
-
-    //~ Instance fields ================================================================================================
-
-    private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
-    private String key;
-    private String parameter = DEFAULT_PARAMETER;
-    private UserDetailsService userDetailsService;
-    private long tokenValiditySeconds = 1209600; // 14 days
-    private boolean alwaysRemember = false;
-    private int order = Integer.MAX_VALUE; //~ default
-    private String cookieName = ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY;
-
-    //~ Methods ========================================================================================================
-
-    public void afterPropertiesSet() throws Exception {
-        Assert.hasLength(key);
-        Assert.hasLength(parameter);
-        Assert.hasLength(cookieName);
-        Assert.notNull(userDetailsService);
-    }
-
-    public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
-        Cookie[] cookies = request.getCookies();
-
-        if ((cookies == null) || (cookies.length == 0)) {
-            return null;
-        }
-
-        for (int i = 0; i < cookies.length; i++) {
-            if (cookieName.equals(cookies[i].getName())) {
-                String cookieValue = cookies[i].getValue();
-
-                for (int j = 0; j < cookieValue.length() % 4; j++) {
-                    cookieValue = cookieValue + "=";
-                } 
-                
-                if (Base64.isArrayByteBase64(cookieValue.getBytes())) {
-                    if (logger.isDebugEnabled()) {
-                        logger.debug("Remember-me cookie detected");
-                    }
-
-                    // Decode token from Base64
-                    // format of token is:
-                    // username + ":" + expiryTime + ":" +
-                    //      Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)
-                    String cookieAsPlainText = new String(Base64.decodeBase64(cookieValue.getBytes()));
-                    String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":");
-
-                    if (cookieTokens.length == 3) {
-                        long tokenExpiryTime;
-
-                        try {
-                            tokenExpiryTime = new Long(cookieTokens[1]).longValue();
-                        } catch (NumberFormatException nfe) {
-                            cancelCookie(request, response,
-                                "Cookie token[1] did not contain a valid number (contained '" + cookieTokens[1] + "')");
-
-                            return null;
-                        }
-
-                        // Check it has not expired
-                        if (tokenExpiryTime < System.currentTimeMillis()) {
-                            cancelCookie(request, response,
-                                "Cookie token[1] has expired (expired on '" + new Date(tokenExpiryTime)
-                                + "'; current time is '" + new Date() + "')");
-
-                            return null;
-                        }
-
-                        // Check the user exists
-                        // Defer lookup until after expiry time checked, to possibly avoid expensive lookup
-                        UserDetails userDetails;
-
-                        try {
-                            userDetails = this.userDetailsService.loadUserByUsername(cookieTokens[0]);
-                        } catch (UsernameNotFoundException notFound) {
-                            cancelCookie(request, response,
-                                "Cookie token[0] contained username '" + cookieTokens[0] + "' but was not found");
-
-                            return null;
-                        }
-
-                        // Immediately reject if the user is not allowed to login
-                        if (!userDetails.isAccountNonExpired() || !userDetails.isCredentialsNonExpired()
-                            || !userDetails.isEnabled()) {
-                            cancelCookie(request, response,
-                                "Cookie token[0] contained username '" + cookieTokens[0]
-                                + "' but account has expired, credentials have expired, or user is disabled");
-
-                            return null;
-                        }
-
-                        // Check signature of token matches remaining details
-                        // Must do this after user lookup, as we need the DAO-derived password
-                        // If efficiency was a major issue, just add in a UserCache implementation,
-                        // but recall this method is usually only called one per HttpSession
-                        // (as if the token is valid, it will cause SecurityContextHolder population, whilst
-                        // if invalid, will cause the cookie to be cancelled)
-                        String expectedTokenSignature = DigestUtils.md5Hex(userDetails.getUsername() + ":"
-                                + tokenExpiryTime + ":" + userDetails.getPassword() + ":" + this.key);
-
-                        if (!expectedTokenSignature.equals(cookieTokens[2])) {
-                            cancelCookie(request, response,
-                                "Cookie token[2] contained signature '" + cookieTokens[2] + "' but expected '"
-                                + expectedTokenSignature + "'");
-
-                            return null;
-                        }
-
-                        // By this stage we have a valid token
-                        if (logger.isDebugEnabled()) {
-                            logger.debug("Remember-me cookie accepted");
-                        }
-
-                        RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.key, userDetails,
-                                userDetails.getAuthorities());
-                        auth.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
-
-                        return auth;
-                    } else {
-                        cancelCookie(request, response,
-                            "Cookie token did not contain 3 tokens; decoded value was '" + cookieAsPlainText + "'");
-
-                        return null;
-                    }
-                } else {
-                    cancelCookie(request, response,
-                        "Cookie token was not Base64 encoded; value was '" + cookieValue + "'");
-
-                    return null;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    private void cancelCookie(HttpServletRequest request, HttpServletResponse response, String reasonForLog) {
-        if ((reasonForLog != null) && logger.isDebugEnabled()) {
-            logger.debug("Cancelling cookie for reason: " + reasonForLog);
-        }
-
-        response.addCookie(makeCancelCookie(request));
-    }
-
-    public String getKey() {
-        return key;
-    }
-
-    public String getParameter() {
-        return parameter;
-    }
-
-    public long getTokenValiditySeconds() {
-        return tokenValiditySeconds;
-    }
-
-    public UserDetailsService getUserDetailsService() {
-        return userDetailsService;
-    }
-
-    public void loginFail(HttpServletRequest request, HttpServletResponse response) {
-        cancelCookie(request, response, "Interactive authentication attempt was unsuccessful");
-    }
-
-    protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
-        if (alwaysRemember) {
-            return true;
-        }
-
-        return RequestUtils.getBooleanParameter(request, parameter, false);
-    }
-
-    public void loginSuccess(HttpServletRequest request, HttpServletResponse response,
-        Authentication successfulAuthentication) {
-        // Exit if the principal hasn't asked to be remembered
-        if (!rememberMeRequested(request, parameter)) {
-            if (logger.isDebugEnabled()) {
-                logger.debug("Did not send remember-me cookie (principal did not set parameter '" + this.parameter
-                    + "')");
-            }
-
-            return;
-        }
-
-        // Determine username and password, ensuring empty strings
-        Assert.notNull(successfulAuthentication.getPrincipal());
-        Assert.notNull(successfulAuthentication.getCredentials());
-
-        String username;
-        String password;
-
-        if (successfulAuthentication.getPrincipal() instanceof UserDetails) {
-            username = ((UserDetails) successfulAuthentication.getPrincipal()).getUsername();
-            password = ((UserDetails) successfulAuthentication.getPrincipal()).getPassword();
-        } else {
-            username = successfulAuthentication.getPrincipal().toString();
-            password = successfulAuthentication.getCredentials().toString();
-        }
-        
-        // If unable to find a username and password, just abort as TokenBasedRememberMeServices unable to construct a valid token in this case
-        if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
-        	return;
-        }
-
-        Assert.hasLength(username);
-        Assert.hasLength(password);
-
-        long expiryTime = System.currentTimeMillis() + (tokenValiditySeconds * 1000);
-
-        // construct token to put in cookie; format is:
-        //     username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)
-        String signatureValue = DigestUtils.md5Hex(username + ":" + expiryTime + ":" + password + ":" + key);
-        String tokenValue = username + ":" + expiryTime + ":" + signatureValue;
-        String tokenValueBase64 = new String(Base64.encodeBase64(tokenValue.getBytes()));
-        response.addCookie(makeValidCookie(tokenValueBase64, request, tokenValiditySeconds));
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Added remember-me cookie for user '" + username
-                    + "', expiry: '" + new Date(expiryTime) + "'");
-        }
-    }
-
-    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
-        cancelCookie(request, response, "Logout of user "
-                + (authentication == null ? "Unknown" : authentication.getName()));
-    }
-
-    protected Cookie makeCancelCookie(HttpServletRequest request) {
-        Cookie cookie = new Cookie(cookieName, null);
-        cookie.setMaxAge(0);
-        cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
-
-        return cookie;
-    }
-
-    protected Cookie makeValidCookie(String tokenValueBase64, HttpServletRequest request, long maxAge) {
-        Cookie cookie = new Cookie(cookieName, tokenValueBase64);
-        cookie.setMaxAge(new Long(maxAge).intValue());
-        cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
-
-        return cookie;
-    }
-
-    public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
-        Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
-        this.authenticationDetailsSource = authenticationDetailsSource;
-    }
-
-    public void setKey(String key) {
-        this.key = key;
-    }
-
-    public void setParameter(String parameter) {
-        this.parameter = parameter;
-    }
-
-    public void setCookieName(String cookieName) {
+public class TokenBasedRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler, Ordered, ApplicationContextAware {
+	// ~ Static fields/initializers
+	// =====================================================================================
+
+	
+
+	public static final String ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY = "ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE";
+
+	public static final String DEFAULT_PARAMETER = "_acegi_security_remember_me";
+
+	protected static final Log logger = LogFactory.getLog(TokenBasedRememberMeServices.class);
+
+	// ~ Instance fields
+	// ================================================================================================
+
+	private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
+
+	private String key;
+
+	private String parameter = DEFAULT_PARAMETER;
+
+	private UserDetailsService userDetailsService;
+
+	private long tokenValiditySeconds = 1209600; // 14 days
+
+	private boolean alwaysRemember = false;
+
+	private static final int DEFAULT_ORDER = Integer.MAX_VALUE; // ~ default
+
+	private int order = DEFAULT_ORDER;
+
+	private String cookieName = ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY;
+
+	private boolean isSetUserDetailsServiceInvoked = false;
+
+	private ApplicationContext applicationContext;
+
+	// ~ Methods
+	// ========================================================================================================
+
+	public void afterPropertiesSet() throws Exception {
+		Assert.hasLength(key);
+		Assert.hasLength(parameter);
+		Assert.hasLength(cookieName);
+		Assert.notNull(applicationContext, "ApplicationContext required");
+		if (!isSetUserDetailsServiceInvoked) {
+			autoDetectAnyUserDetailsServiceAndUseIt(applicationContext);
+		}
+		Assert.notNull(userDetailsService);
+	}
+	
+	/**
+	 * Introspects the <code>Applicationcontext</code> for the single instance
+	 * of {@link AccessDeniedHandler}. If found invoke
+	 * setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) method by
+	 * providing the found instance of accessDeniedHandler as a method
+	 * parameter. If more than one instance of <code>AccessDeniedHandler</code>
+	 * is found, the method throws <code>IllegalStateException</code>.
+	 * 
+	 * @param applicationContext to locate the instance
+	 */
+	private void autoDetectAnyUserDetailsServiceAndUseIt(ApplicationContext applicationContext) {
+		Map map = applicationContext.getBeansOfType(UserDetailsService.class);
+		if (map.size() > 1) {
+			throw new IllegalArgumentException(
+					"More than one UserDetailsService beans detected please refer to the one using "
+							+ " [ principalRepositoryBeanRef  ] " + "attribute");
+		}
+		else if (map.size() == 1) {
+			setUserDetailsService((UserDetailsService) map.values().iterator().next());
+		}
+	}
+		
+
+	public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
+		Cookie[] cookies = request.getCookies();
+
+		if ((cookies == null) || (cookies.length == 0)) {
+			return null;
+		}
+
+		for (int i = 0; i < cookies.length; i++) {
+			if (cookieName.equals(cookies[i].getName())) {
+				String cookieValue = cookies[i].getValue();
+
+				for (int j = 0; j < cookieValue.length() % 4; j++) {
+					cookieValue = cookieValue + "=";
+				}
+
+				if (Base64.isArrayByteBase64(cookieValue.getBytes())) {
+					if (logger.isDebugEnabled()) {
+						logger.debug("Remember-me cookie detected");
+					}
+
+					// Decode token from Base64
+					// format of token is:
+					// username + ":" + expiryTime + ":" +
+					// Md5Hex(username + ":" + expiryTime + ":" + password + ":"
+					// + key)
+					String cookieAsPlainText = new String(Base64.decodeBase64(cookieValue.getBytes()));
+					String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":");
+
+					if (cookieTokens.length == 3) {
+						long tokenExpiryTime;
+
+						try {
+							tokenExpiryTime = new Long(cookieTokens[1]).longValue();
+						}
+						catch (NumberFormatException nfe) {
+							cancelCookie(request, response,
+									"Cookie token[1] did not contain a valid number (contained '" + cookieTokens[1]
+											+ "')");
+
+							return null;
+						}
+
+						// Check it has not expired
+						if (tokenExpiryTime < System.currentTimeMillis()) {
+							cancelCookie(request, response, "Cookie token[1] has expired (expired on '"
+									+ new Date(tokenExpiryTime) + "'; current time is '" + new Date() + "')");
+
+							return null;
+						}
+
+						// Check the user exists
+						// Defer lookup until after expiry time checked, to
+						// possibly avoid expensive lookup
+						UserDetails userDetails;
+
+						try {
+							userDetails = this.userDetailsService.loadUserByUsername(cookieTokens[0]);
+						}
+						catch (UsernameNotFoundException notFound) {
+							cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]
+									+ "' but was not found");
+
+							return null;
+						}
+
+						// Immediately reject if the user is not allowed to
+						// login
+						if (!userDetails.isAccountNonExpired() || !userDetails.isCredentialsNonExpired()
+								|| !userDetails.isEnabled()) {
+							cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]
+									+ "' but account has expired, credentials have expired, or user is disabled");
+
+							return null;
+						}
+
+						// Check signature of token matches remaining details
+						// Must do this after user lookup, as we need the
+						// DAO-derived password
+						// If efficiency was a major issue, just add in a
+						// UserCache implementation,
+						// but recall this method is usually only called one per
+						// HttpSession
+						// (as if the token is valid, it will cause
+						// SecurityContextHolder population, whilst
+						// if invalid, will cause the cookie to be cancelled)
+						String expectedTokenSignature = DigestUtils.md5Hex(userDetails.getUsername() + ":"
+								+ tokenExpiryTime + ":" + userDetails.getPassword() + ":" + this.key);
+
+						if (!expectedTokenSignature.equals(cookieTokens[2])) {
+							cancelCookie(request, response, "Cookie token[2] contained signature '" + cookieTokens[2]
+									+ "' but expected '" + expectedTokenSignature + "'");
+
+							return null;
+						}
+
+						// By this stage we have a valid token
+						if (logger.isDebugEnabled()) {
+							logger.debug("Remember-me cookie accepted");
+						}
+
+						RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.key, userDetails,
+								userDetails.getAuthorities());
+						auth.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
+
+						return auth;
+					}
+					else {
+						cancelCookie(request, response, "Cookie token did not contain 3 tokens; decoded value was '"
+								+ cookieAsPlainText + "'");
+
+						return null;
+					}
+				}
+				else {
+					cancelCookie(request, response, "Cookie token was not Base64 encoded; value was '" + cookieValue
+							+ "'");
+
+					return null;
+				}
+			}
+		}
+
+		return null;
+	}
+
+	private void cancelCookie(HttpServletRequest request, HttpServletResponse response, String reasonForLog) {
+		if ((reasonForLog != null) && logger.isDebugEnabled()) {
+			logger.debug("Cancelling cookie for reason: " + reasonForLog);
+		}
+
+		response.addCookie(makeCancelCookie(request));
+	}
+
+	public String getKey() {
+		return key;
+	}
+
+	public String getParameter() {
+		return parameter;
+	}
+
+	public long getTokenValiditySeconds() {
+		return tokenValiditySeconds;
+	}
+
+	public UserDetailsService getUserDetailsService() {
+		return userDetailsService;
+	}
+
+	public void loginFail(HttpServletRequest request, HttpServletResponse response) {
+		cancelCookie(request, response, "Interactive authentication attempt was unsuccessful");
+	}
+
+	protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
+		if (alwaysRemember) {
+			return true;
+		}
+
+		return RequestUtils.getBooleanParameter(request, parameter, false);
+	}
+
+	public void loginSuccess(HttpServletRequest request, HttpServletResponse response,
+			Authentication successfulAuthentication) {
+		// Exit if the principal hasn't asked to be remembered
+		if (!rememberMeRequested(request, parameter)) {
+			if (logger.isDebugEnabled()) {
+				logger.debug("Did not send remember-me cookie (principal did not set parameter '" + this.parameter
+						+ "')");
+			}
+
+			return;
+		}
+
+		// Determine username and password, ensuring empty strings
+		Assert.notNull(successfulAuthentication.getPrincipal());
+		Assert.notNull(successfulAuthentication.getCredentials());
+
+		String username;
+		String password;
+
+		if (successfulAuthentication.getPrincipal() instanceof UserDetails) {
+			username = ((UserDetails) successfulAuthentication.getPrincipal()).getUsername();
+			password = ((UserDetails) successfulAuthentication.getPrincipal()).getPassword();
+		}
+		else {
+			username = successfulAuthentication.getPrincipal().toString();
+			password = successfulAuthentication.getCredentials().toString();
+		}
+
+		// If unable to find a username and password, just abort as
+		// TokenBasedRememberMeServices unable to construct a valid token in
+		// this case
+		if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
+			return;
+		}
+
+		Assert.hasLength(username);
+		Assert.hasLength(password);
+
+		long expiryTime = System.currentTimeMillis() + (tokenValiditySeconds * 1000);
+
+		// construct token to put in cookie; format is:
+		// username + ":" + expiryTime + ":" + Md5Hex(username + ":" +
+		// expiryTime + ":" + password + ":" + key)
+		String signatureValue = DigestUtils.md5Hex(username + ":" + expiryTime + ":" + password + ":" + key);
+		String tokenValue = username + ":" + expiryTime + ":" + signatureValue;
+		String tokenValueBase64 = new String(Base64.encodeBase64(tokenValue.getBytes()));
+		response.addCookie(makeValidCookie(tokenValueBase64, request, tokenValiditySeconds));
+
+		if (logger.isDebugEnabled()) {
+			logger
+					.debug("Added remember-me cookie for user '" + username + "', expiry: '" + new Date(expiryTime)
+							+ "'");
+		}
+	}
+
+	public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
+		cancelCookie(request, response, "Logout of user "
+				+ (authentication == null ? "Unknown" : authentication.getName()));
+	}
+
+	protected Cookie makeCancelCookie(HttpServletRequest request) {
+		Cookie cookie = new Cookie(cookieName, null);
+		cookie.setMaxAge(0);
+		cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
+
+		return cookie;
+	}
+
+	protected Cookie makeValidCookie(String tokenValueBase64, HttpServletRequest request, long maxAge) {
+		Cookie cookie = new Cookie(cookieName, tokenValueBase64);
+		cookie.setMaxAge(new Long(maxAge).intValue());
+		cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
+
+		return cookie;
+	}
+
+	public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
+		Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
+		this.authenticationDetailsSource = authenticationDetailsSource;
+	}
+
+	public void setKey(String key) {
+		this.key = key;
+	}
+
+	public void setParameter(String parameter) {
+		this.parameter = parameter;
+	}
+
+	public void setCookieName(String cookieName) {
 		this.cookieName = cookieName;
 	}
 
 	public void setTokenValiditySeconds(long tokenValiditySeconds) {
-        this.tokenValiditySeconds = tokenValiditySeconds;
-    }
+		this.tokenValiditySeconds = tokenValiditySeconds;
+	}
 
-    public void setUserDetailsService(UserDetailsService userDetailsService) {
-        this.userDetailsService = userDetailsService;
-    }
+	public void setUserDetailsService(UserDetailsService userDetailsService) {
+		this.userDetailsService = userDetailsService;
+		this.isSetUserDetailsServiceInvoked = true;
+	}
 
-    public boolean isAlwaysRemember() {
-        return alwaysRemember;
-    }
+	public boolean isAlwaysRemember() {
+		return alwaysRemember;
+	}
 
-    public void setAlwaysRemember(boolean alwaysRemember) {
-        this.alwaysRemember = alwaysRemember;
-    }
+	public void setAlwaysRemember(boolean alwaysRemember) {
+		this.alwaysRemember = alwaysRemember;
+	}
 
 	public int getOrder() {
 		return order;
@@ -373,4 +458,8 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial
 		this.order = order;
 	}
 
+	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+		this.applicationContext=applicationContext;
+	}
+
 }

+ 206 - 179
core/src/main/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilterEntryPoint.java

@@ -41,206 +41,233 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-
 /**
- * <p>Used by the <code>SecurityEnforcementFilter</code> to commence authentication via the {@link
- * AuthenticationProcessingFilter}. This object holds the location of the login form, relative to the web app context
- * path, and is used to commence a redirect to that form.</p>
- *  <p>By setting the <em>forceHttps</em> property to true, you may configure the class to force the protocol used
- * for the login form to be <code>HTTPS</code>, even if the original intercepted request for a resource used the
- * <code>HTTP</code> protocol. When this happens, after a successful login (via HTTPS), the original resource will
- * still be accessed as HTTP, via the original request URL. For the forced HTTPS feature to work, the {@link
- * PortMapper} is consulted to determine the HTTP:HTTPS pairs.</p>
- *
+ * <p>
+ * Used by the <code>SecurityEnforcementFilter</code> to commence
+ * authentication via the {@link AuthenticationProcessingFilter}. This object
+ * holds the location of the login form, relative to the web app context path,
+ * and is used to commence a redirect to that form.
+ * </p>
+ * <p>
+ * By setting the <em>forceHttps</em> property to true, you may configure the
+ * class to force the protocol used for the login form to be <code>HTTPS</code>,
+ * even if the original intercepted request for a resource used the
+ * <code>HTTP</code> protocol. When this happens, after a successful login
+ * (via HTTPS), the original resource will still be accessed as HTTP, via the
+ * original request URL. For the forced HTTPS feature to work, the {@link
+ * PortMapper} is consulted to determine the HTTP:HTTPS pairs.
+ * </p>
+ * 
  * @author Ben Alex
  * @author colin sampaleanu
  * @author Omri Spector
- * @version $Id$
+ * @version $Id: AuthenticationProcessingFilterEntryPoint.java 1873 2007-05-25
+ * 03:21:17Z benalex $
  */
 public class AuthenticationProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean, Ordered {
-    //~ Static fields/initializers =====================================================================================
+	// ~ Static fields/initializers
+	// =====================================================================================
+
+	private static final Log logger = LogFactory.getLog(AuthenticationProcessingFilterEntryPoint.class);
+
+	// ~ Instance fields
+	// ================================================================================================
+
+	private PortMapper portMapper = new PortMapperImpl();
 
-    private static final Log logger = LogFactory.getLog(AuthenticationProcessingFilterEntryPoint.class);
+	private PortResolver portResolver = new PortResolverImpl();
 
-    //~ Instance fields ================================================================================================
+	private String loginFormUrl;
 
-    private PortMapper portMapper = new PortMapperImpl();
-    private PortResolver portResolver = new PortResolverImpl();
-    private String loginFormUrl;
-    private boolean forceHttps = false;
-    private boolean serverSideRedirect = false;
-    private int order = Integer.MAX_VALUE; // ~ default
+	private boolean forceHttps = false;
 
-    //~ Methods ========================================================================================================
+	private boolean serverSideRedirect = false;
+
+	private int DEFAULT_ORDER = Integer.MAX_VALUE;// ~ default
+
+	private int order = DEFAULT_ORDER;
+
+	// ~ Methods
+	// ========================================================================================================
 
 	public void afterPropertiesSet() throws Exception {
-        Assert.hasLength(loginFormUrl, "loginFormUrl must be specified");
-        Assert.notNull(portMapper, "portMapper must be specified");
-        Assert.notNull(portResolver, "portResolver must be specified");
-    }
+		Assert.hasLength(loginFormUrl, "loginFormUrl must be specified");
+		Assert.notNull(portMapper, "portMapper must be specified");
+		Assert.notNull(portResolver, "portResolver must be specified");
+	}
 
 	/**
-	 * Allows subclasses to modify the login form URL that should be applicable for a given request.
+	 * Allows subclasses to modify the login form URL that should be applicable
+	 * for a given request.
 	 * 
 	 * @param request the request
 	 * @param response the response
 	 * @param exception the exception
-	 * @return the URL (cannot be null or empty; defaults to {@link #getLoginFormUrl()})
+	 * @return the URL (cannot be null or empty; defaults to
+	 * {@link #getLoginFormUrl()})
 	 */
-	protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) {
+	protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response,
+			AuthenticationException exception) {
 		return getLoginFormUrl();
 	}
-	
-    public void commence(ServletRequest request, ServletResponse response, AuthenticationException authException)
-        throws IOException, ServletException {
-        HttpServletRequest req = (HttpServletRequest) request;
-        HttpServletResponse resp = (HttpServletResponse) response;
-        String scheme = request.getScheme();
-        String serverName = request.getServerName();
-        int serverPort = portResolver.getServerPort(request);
-        String contextPath = req.getContextPath();
-
-        boolean inHttp = "http".equals(scheme.toLowerCase());
-        boolean inHttps = "https".equals(scheme.toLowerCase());
-
-        boolean includePort = true;
-
-        String redirectUrl = null;
-        boolean doForceHttps = false;
-        Integer httpsPort = null;
-
-        if (inHttp && (serverPort == 80)) {
-            includePort = false;
-        } else if (inHttps && (serverPort == 443)) {
-            includePort = false;
-        }
-
-        if (forceHttps && inHttp) {
-            httpsPort = (Integer) portMapper.lookupHttpsPort(new Integer(serverPort));
-        
-            if (httpsPort != null) {
-                doForceHttps = true;
-                if (httpsPort.intValue() == 443) {
-                    includePort = false;
-                } else {
-                    includePort = true;
-                }
-            }
-            
-        }
-  
-    	String loginForm = determineUrlToUseForThisRequest(req, resp, authException);
-    	
-        if ( serverSideRedirect ) {
-
-            if ( doForceHttps ) {
-          
-                // before doing server side redirect, we need to do client redirect to https.
-              
-                String servletPath = req.getServletPath();
-                String pathInfo = req.getPathInfo();
-                String query = req.getQueryString();
-
-                redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath
-                  + servletPath + (pathInfo == null ? "" : pathInfo ) + (query == null ? "" : "?"+query );
-
-            } else {
-
-                if (logger.isDebugEnabled()) {
-                  logger.debug("Server side forward to: " + loginForm);
-                }
-
-                RequestDispatcher dispatcher = req.getRequestDispatcher(loginForm);
-
-                dispatcher.forward(request, response);
-                
-                return;
-
-            }
-
-        } else {
-
-            if ( doForceHttps ) {
-
-                redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath
-                    + loginForm;
-
-            } else {
-
-                redirectUrl = scheme + "://" + serverName + ((includePort) ? (":" + serverPort) : "") + contextPath
-                  + loginForm;
-
-            }
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("Redirecting to: " + redirectUrl);
-        }
-
-        ((HttpServletResponse) response).sendRedirect(((HttpServletResponse) response).encodeRedirectURL(redirectUrl));
-    }
-
-    public boolean getForceHttps() {
-        return forceHttps;
-    }
-
-    public String getLoginFormUrl() {
-        return loginFormUrl;
-    }
-
-    public PortMapper getPortMapper() {
-        return portMapper;
-    }
-
-    public PortResolver getPortResolver() {
-        return portResolver;
-    }
-
-    public boolean isServerSideRedirect() {
-       return serverSideRedirect;
-    }
-
-    /**
-     * Set to true to force login form access to be via https. If this value is ture (the default is false),
-     * and the incoming request for the protected resource which triggered the interceptor was not already
-     * <code>https</code>, then
-     *
-     * @param forceHttps
-     */
-    public void setForceHttps(boolean forceHttps) {
-        this.forceHttps = forceHttps;
-    }
-
-    /**
-     * The URL where the <code>AuthenticationProcessingFilter</code> login page can be found. Should be
-     * relative to the web-app context path, and include a leading <code>/</code>
-     *
-     * @param loginFormUrl
-     */
-    public void setLoginFormUrl(String loginFormUrl) {
-        this.loginFormUrl = loginFormUrl;
-    }
-
-    public void setPortMapper(PortMapper portMapper) {
-        this.portMapper = portMapper;
-    }
-
-    public void setPortResolver(PortResolver portResolver) {
-        this.portResolver = portResolver;
-    }
- 
-    /**
-     * Tells if we are to do a server side include of the <code>loginFormUrl</code> instead of a 302
-     * redirect.
-     * 
-     * @param serverSideRedirect
-     */
-    public void setServerSideRedirect(boolean serverSideRedirect) {
-        this.serverSideRedirect = serverSideRedirect;
-    }
-    
-
-    public int getOrder() {
+
+	public void commence(ServletRequest request, ServletResponse response, AuthenticationException authException)
+			throws IOException, ServletException {
+		HttpServletRequest req = (HttpServletRequest) request;
+		HttpServletResponse resp = (HttpServletResponse) response;
+		String scheme = request.getScheme();
+		String serverName = request.getServerName();
+		int serverPort = portResolver.getServerPort(request);
+		String contextPath = req.getContextPath();
+
+		boolean inHttp = "http".equals(scheme.toLowerCase());
+		boolean inHttps = "https".equals(scheme.toLowerCase());
+
+		boolean includePort = true;
+
+		String redirectUrl = null;
+		boolean doForceHttps = false;
+		Integer httpsPort = null;
+
+		if (inHttp && (serverPort == 80)) {
+			includePort = false;
+		}
+		else if (inHttps && (serverPort == 443)) {
+			includePort = false;
+		}
+
+		if (forceHttps && inHttp) {
+			httpsPort = (Integer) portMapper.lookupHttpsPort(new Integer(serverPort));
+
+			if (httpsPort != null) {
+				doForceHttps = true;
+				if (httpsPort.intValue() == 443) {
+					includePort = false;
+				}
+				else {
+					includePort = true;
+				}
+			}
+
+		}
+
+		String loginForm = determineUrlToUseForThisRequest(req, resp, authException);
+
+		if (serverSideRedirect) {
+
+			if (doForceHttps) {
+
+				// before doing server side redirect, we need to do client
+				// redirect to https.
+
+				String servletPath = req.getServletPath();
+				String pathInfo = req.getPathInfo();
+				String query = req.getQueryString();
+
+				redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath
+						+ servletPath + (pathInfo == null ? "" : pathInfo) + (query == null ? "" : "?" + query);
+
+			}
+			else {
+
+				if (logger.isDebugEnabled()) {
+					logger.debug("Server side forward to: " + loginForm);
+				}
+
+				RequestDispatcher dispatcher = req.getRequestDispatcher(loginForm);
+
+				dispatcher.forward(request, response);
+
+				return;
+
+			}
+
+		}
+		else {
+
+			if (doForceHttps) {
+
+				redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath
+						+ loginForm;
+
+			}
+			else {
+
+				redirectUrl = scheme + "://" + serverName + ((includePort) ? (":" + serverPort) : "") + contextPath
+						+ loginForm;
+
+			}
+		}
+
+		if (logger.isDebugEnabled()) {
+			logger.debug("Redirecting to: " + redirectUrl);
+		}
+
+		((HttpServletResponse) response).sendRedirect(((HttpServletResponse) response).encodeRedirectURL(redirectUrl));
+	}
+
+	public boolean getForceHttps() {
+		return forceHttps;
+	}
+
+	public String getLoginFormUrl() {
+		return loginFormUrl;
+	}
+
+	public PortMapper getPortMapper() {
+		return portMapper;
+	}
+
+	public PortResolver getPortResolver() {
+		return portResolver;
+	}
+
+	public boolean isServerSideRedirect() {
+		return serverSideRedirect;
+	}
+
+	/**
+	 * Set to true to force login form access to be via https. If this value is
+	 * ture (the default is false), and the incoming request for the protected
+	 * resource which triggered the interceptor was not already
+	 * <code>https</code>, then
+	 * 
+	 * @param forceHttps
+	 */
+	public void setForceHttps(boolean forceHttps) {
+		this.forceHttps = forceHttps;
+	}
+
+	/**
+	 * The URL where the <code>AuthenticationProcessingFilter</code> login
+	 * page can be found. Should be relative to the web-app context path, and
+	 * include a leading <code>/</code>
+	 * 
+	 * @param loginFormUrl
+	 */
+	public void setLoginFormUrl(String loginFormUrl) {
+		this.loginFormUrl = loginFormUrl;
+	}
+
+	public void setPortMapper(PortMapper portMapper) {
+		this.portMapper = portMapper;
+	}
+
+	public void setPortResolver(PortResolver portResolver) {
+		this.portResolver = portResolver;
+	}
+
+	/**
+	 * Tells if we are to do a server side include of the
+	 * <code>loginFormUrl</code> instead of a 302 redirect.
+	 * 
+	 * @param serverSideRedirect
+	 */
+	public void setServerSideRedirect(boolean serverSideRedirect) {
+		this.serverSideRedirect = serverSideRedirect;
+	}
+
+	public int getOrder() {
 		return order;
 	}
 

+ 316 - 291
core/src/test/java/org/acegisecurity/ui/ExceptionTranslationFilterTests.java

@@ -38,300 +38,325 @@ import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 
-
 /**
  * Tests {@link ExceptionTranslationFilter}.
- *
+ * 
  * @author Ben Alex
- * @version $Id$
+ * @version $Id: ExceptionTranslationFilterTests.java 1496 2006-05-23 13:38:33Z
+ * benalex $
  */
 public class ExceptionTranslationFilterTests extends TestCase {
-    //~ Constructors ===================================================================================================
-
-    public ExceptionTranslationFilterTests() {
-        super();
-    }
-
-    public ExceptionTranslationFilterTests(String arg0) {
-        super(arg0);
-    }
-
-    //~ Methods ========================================================================================================
-
-    public static void main(String[] args) {
-        junit.textui.TestRunner.run(ExceptionTranslationFilterTests.class);
-    }
-
-    public final void setUp() throws Exception {
-        super.setUp();
-    }
-
-    protected void tearDown() throws Exception {
-        super.tearDown();
-        SecurityContextHolder.clearContext();
-    }
-
-    public void testAccessDeniedWhenAnonymous() throws Exception {
-        // Setup our HTTP request
-        MockHttpServletRequest request = new MockHttpServletRequest();
-        request.setServletPath("/secure/page.html");
-        request.setServerPort(80);
-        request.setScheme("http");
-        request.setServerName("www.example.com");
-        request.setContextPath("/mycontext");
-        request.setRequestURI("/mycontext/secure/page.html");
-
-        // Setup the FilterChain to thrown an access denied exception
-        MockFilterChain chain = new MockFilterChain(true, false, false, false);
-
-        // Setup SecurityContextHolder, as filter needs to check if user is anonymous
-        SecurityContextHolder.getContext()
-                             .setAuthentication(new AnonymousAuthenticationToken("ignored", "ignored",
-                new GrantedAuthority[] {new GrantedAuthorityImpl("IGNORED")}));
-
-        // Test
-        ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
-        filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
-
-        MockHttpServletResponse response = new MockHttpServletResponse();
-        filter.doFilter(request, response, chain);
-        assertEquals("/mycontext/login.jsp", response.getRedirectedUrl());
-        assertEquals("http://www.example.com/mycontext/secure/page.html",
-            AbstractProcessingFilter.obtainFullRequestUrl(request));
-    }
-
-    public void testAccessDeniedWhenNonAnonymous() throws Exception {
-        // Setup our HTTP request
-        MockHttpServletRequest request = new MockHttpServletRequest();
-        request.setServletPath("/secure/page.html");
-
-        // Setup the FilterChain to thrown an access denied exception
-        MockFilterChain chain = new MockFilterChain(true, false, false, false);
-
-        // Setup SecurityContextHolder, as filter needs to check if user is anonymous
-        SecurityContextHolder.getContext().setAuthentication(null);
-
-        // Setup a new AccessDeniedHandlerImpl that will do a "forward"
-        AccessDeniedHandlerImpl adh = new AccessDeniedHandlerImpl();
-        adh.setErrorPage("/error.jsp");
-
-        // Test
-        ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
-        filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
-        filter.setAccessDeniedHandler(adh);
-
-        MockHttpServletResponse response = new MockHttpServletResponse();
-        filter.doFilter(request, response, chain);
-        assertEquals(403, response.getStatus());
-        assertEquals(AccessDeniedException.class,
-            request.getAttribute(AccessDeniedHandlerImpl.ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY).getClass());
-    }
-
-    public void testDoFilterWithNonHttpServletRequestDetected()
-        throws Exception {
-        ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
-
-        try {
-            filter.doFilter(null, new MockHttpServletResponse(), new MockFilterChain(false, false, false, false));
-            fail("Should have thrown ServletException");
-        } catch (ServletException expected) {
-            assertEquals("HttpServletRequest required", expected.getMessage());
-        }
-    }
-
-    public void testDoFilterWithNonHttpServletResponseDetected()
-        throws Exception {
-        ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
-
-        try {
-            filter.doFilter(new MockHttpServletRequest(null, null), null,
-                new MockFilterChain(false, false, false, false));
-            fail("Should have thrown ServletException");
-        } catch (ServletException expected) {
-            assertEquals("HttpServletResponse required", expected.getMessage());
-        }
-    }
-
-    public void testGettersSetters() {
-        ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
-
-        filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
-        assertTrue(filter.getAuthenticationEntryPoint() != null);
-
-        filter.setPortResolver(new MockPortResolver(80, 443));
-        assertTrue(filter.getPortResolver() != null);
-    }
-
-    public void testRedirectedToLoginFormAndSessionShowsOriginalTargetWhenAuthenticationException()
-        throws Exception {
-        // Setup our HTTP request
-        MockHttpServletRequest request = new MockHttpServletRequest();
-        request.setServletPath("/secure/page.html");
-        request.setServerPort(80);
-        request.setScheme("http");
-        request.setServerName("www.example.com");
-        request.setContextPath("/mycontext");
-        request.setRequestURI("/mycontext/secure/page.html");
-
-        // Setup the FilterChain to thrown an authentication failure exception
-        MockFilterChain chain = new MockFilterChain(false, true, false, false);
-
-        // Test
-        ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
-        filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
-        filter.setPortResolver(new MockPortResolver(80, 443));
-        filter.afterPropertiesSet();
-
-        MockHttpServletResponse response = new MockHttpServletResponse();
-        filter.doFilter(request, response, chain);
-        assertEquals("/mycontext/login.jsp", response.getRedirectedUrl());
-        assertEquals("http://www.example.com/mycontext/secure/page.html",
-            AbstractProcessingFilter.obtainFullRequestUrl(request));
-    }
-
-    public void testRedirectedToLoginFormAndSessionShowsOriginalTargetWithExoticPortWhenAuthenticationException()
-        throws Exception {
-        // Setup our HTTP request
-        MockHttpServletRequest request = new MockHttpServletRequest();
-        request.setServletPath("/secure/page.html");
-        request.setServerPort(8080);
-        request.setScheme("http");
-        request.setServerName("www.example.com");
-        request.setContextPath("/mycontext");
-        request.setRequestURI("/mycontext/secure/page.html");
-
-        // Setup the FilterChain to thrown an authentication failure exception
-        MockFilterChain chain = new MockFilterChain(false, true, false, false);
-
-        // Test
-        ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
-        filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
-        filter.setPortResolver(new MockPortResolver(8080, 8443));
-        filter.afterPropertiesSet();
-
-        MockHttpServletResponse response = new MockHttpServletResponse();
-        filter.doFilter(request, response, chain);
-        assertEquals("/mycontext/login.jsp", response.getRedirectedUrl());
-        assertEquals("http://www.example.com:8080/mycontext/secure/page.html",
-            AbstractProcessingFilter.obtainFullRequestUrl(request));
-    }
-
-    public void testStartupDetectsMissingAuthenticationEntryPoint()
-        throws Exception {
-        ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
-
-        try {
-            filter.afterPropertiesSet();
-            fail("Should have thrown IllegalArgumentException");
-        } catch (IllegalArgumentException expected) {
-            assertEquals("authenticationEntryPoint must be specified", expected.getMessage());
-        }
-    }
-
-    public void testStartupDetectsMissingPortResolver()
-        throws Exception {
-        ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
-        filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
-        filter.setPortResolver(null);
-
-        try {
-            filter.afterPropertiesSet();
-            fail("Should have thrown IllegalArgumentException");
-        } catch (IllegalArgumentException expected) {
-            assertEquals("portResolver must be specified", expected.getMessage());
-        }
-    }
-
-    public void testSuccessfulAccessGrant() throws Exception {
-        // Setup our HTTP request
-        MockHttpServletRequest request = new MockHttpServletRequest();
-        request.setServletPath("/secure/page.html");
-
-        // Setup the FilterChain to thrown no exceptions
-        MockFilterChain chain = new MockFilterChain(false, false, false, false);
-
-        // Test
-        ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
-        filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
-
-        MockHttpServletResponse response = new MockHttpServletResponse();
-        filter.doFilter(request, response, chain);
-    }
-
-    public void testSuccessfulStartupAndShutdownDown()
-        throws Exception {
-        ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
-
-        filter.init(null);
-        filter.destroy();
-        assertTrue(true);
-    }
-
-    public void testThrowIOException() throws Exception {
-        ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
-
-        filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(""));
-
-        filter.afterPropertiesSet();
-
-        try {
-            filter.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(),
-                new MockFilterChain(false, false, false, true));
-            fail("Should have thrown IOException");
-        } catch (IOException e) {
-            assertNull("The IOException thrown should not have been wrapped", e.getCause());
-        }
-    }
-
-    public void testThrowServletException() throws Exception {
-        ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
-
-        filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(""));
-
-        filter.afterPropertiesSet();
-
-        try {
-            filter.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(),
-                new MockFilterChain(false, false, true, false));
-            fail("Should have thrown ServletException");
-        } catch (ServletException e) {
-            assertNull("The ServletException thrown should not have been wrapped", e.getCause());
-        }
-    }
-
-    //~ Inner Classes ==================================================================================================
-
-    private class MockFilterChain implements FilterChain {
-        private boolean throwAccessDenied;
-        private boolean throwAuthenticationFailure;
-        private boolean throwIOException;
-        private boolean throwServletException;
-
-        public MockFilterChain(boolean throwAccessDenied, boolean throwAuthenticationFailure,
-            boolean throwServletException, boolean throwIOException) {
-            this.throwAccessDenied = throwAccessDenied;
-            this.throwAuthenticationFailure = throwAuthenticationFailure;
-            this.throwServletException = throwServletException;
-            this.throwIOException = throwIOException;
-        }
-
-        public void doFilter(ServletRequest request, ServletResponse response)
-            throws IOException, ServletException {
-            if (throwAccessDenied) {
-                throw new AccessDeniedException("As requested");
-            }
-
-            if (throwAuthenticationFailure) {
-                throw new BadCredentialsException("As requested");
-            }
-
-            if (throwServletException) {
-                throw new ServletException("As requested");
-            }
-
-            if (throwIOException) {
-                throw new IOException("As requested");
-            }
-        }
-    }
+	// ~ Constructors
+	// ===================================================================================================
+
+	public ExceptionTranslationFilterTests() {
+		super();
+	}
+
+	public ExceptionTranslationFilterTests(String arg0) {
+		super(arg0);
+	}
+
+	// ~ Methods
+	// ========================================================================================================
+
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(ExceptionTranslationFilterTests.class);
+	}
+
+	public final void setUp() throws Exception {
+		super.setUp();
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+		SecurityContextHolder.clearContext();
+	}
+
+	public void testAccessDeniedWhenAnonymous() throws Exception {
+		// Setup our HTTP request
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.setServletPath("/secure/page.html");
+		request.setServerPort(80);
+		request.setScheme("http");
+		request.setServerName("www.example.com");
+		request.setContextPath("/mycontext");
+		request.setRequestURI("/mycontext/secure/page.html");
+
+		// Setup the FilterChain to thrown an access denied exception
+		MockFilterChain chain = new MockFilterChain(true, false, false, false);
+
+		// Setup SecurityContextHolder, as filter needs to check if user is
+		// anonymous
+		SecurityContextHolder.getContext().setAuthentication(
+				new AnonymousAuthenticationToken("ignored", "ignored",
+						new GrantedAuthority[] { new GrantedAuthorityImpl("IGNORED") }));
+
+		// Test
+		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
+		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
+
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		filter.doFilter(request, response, chain);
+		assertEquals("/mycontext/login.jsp", response.getRedirectedUrl());
+		assertEquals("http://www.example.com/mycontext/secure/page.html", AbstractProcessingFilter
+				.obtainFullRequestUrl(request));
+	}
+
+	public void testAccessDeniedWhenNonAnonymous() throws Exception {
+		// Setup our HTTP request
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.setServletPath("/secure/page.html");
+
+		// Setup the FilterChain to thrown an access denied exception
+		MockFilterChain chain = new MockFilterChain(true, false, false, false);
+
+		// Setup SecurityContextHolder, as filter needs to check if user is
+		// anonymous
+		SecurityContextHolder.getContext().setAuthentication(null);
+
+		// Setup a new AccessDeniedHandlerImpl that will do a "forward"
+		AccessDeniedHandlerImpl adh = new AccessDeniedHandlerImpl();
+		adh.setErrorPage("/error.jsp");
+
+		// Test
+		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
+		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
+		filter.setAccessDeniedHandler(adh);
+
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		filter.doFilter(request, response, chain);
+		assertEquals(403, response.getStatus());
+		assertEquals(AccessDeniedException.class, request.getAttribute(
+				AccessDeniedHandlerImpl.ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY).getClass());
+	}
+
+	public void testDoFilterWithNonHttpServletRequestDetected() throws Exception {
+		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
+
+		try {
+			filter.doFilter(null, new MockHttpServletResponse(), new MockFilterChain(false, false, false, false));
+			fail("Should have thrown ServletException");
+		}
+		catch (ServletException expected) {
+			assertEquals("HttpServletRequest required", expected.getMessage());
+		}
+	}
+
+	public void testDoFilterWithNonHttpServletResponseDetected() throws Exception {
+		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
+
+		try {
+			filter.doFilter(new MockHttpServletRequest(null, null), null, new MockFilterChain(false, false, false,
+					false));
+			fail("Should have thrown ServletException");
+		}
+		catch (ServletException expected) {
+			assertEquals("HttpServletResponse required", expected.getMessage());
+		}
+	}
+
+	public void testGettersSetters() {
+		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
+
+		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
+		assertTrue(filter.getAuthenticationEntryPoint() != null);
+
+		filter.setPortResolver(new MockPortResolver(80, 443));
+		assertTrue(filter.getPortResolver() != null);
+	}
+
+	public void testRedirectedToLoginFormAndSessionShowsOriginalTargetWhenAuthenticationException() throws Exception {
+		// Setup our HTTP request
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.setServletPath("/secure/page.html");
+		request.setServerPort(80);
+		request.setScheme("http");
+		request.setServerName("www.example.com");
+		request.setContextPath("/mycontext");
+		request.setRequestURI("/mycontext/secure/page.html");
+
+		// Setup the FilterChain to thrown an authentication failure exception
+		MockFilterChain chain = new MockFilterChain(false, true, false, false);
+
+		// Test
+		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
+		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
+		filter.setPortResolver(new MockPortResolver(80, 443));
+		/*
+		 * Disabled the call to afterPropertiesSet as it requires
+		 * applicationContext to be injected before it is invoked. We do not
+		 * have this filter configured in IOC for this test hence no
+		 * ApplicationContext
+		 */
+		// filter.afterPropertiesSet();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		filter.doFilter(request, response, chain);
+		assertEquals("/mycontext/login.jsp", response.getRedirectedUrl());
+		assertEquals("http://www.example.com/mycontext/secure/page.html", AbstractProcessingFilter
+				.obtainFullRequestUrl(request));
+	}
+
+	public void testRedirectedToLoginFormAndSessionShowsOriginalTargetWithExoticPortWhenAuthenticationException()
+			throws Exception {
+		// Setup our HTTP request
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.setServletPath("/secure/page.html");
+		request.setServerPort(8080);
+		request.setScheme("http");
+		request.setServerName("www.example.com");
+		request.setContextPath("/mycontext");
+		request.setRequestURI("/mycontext/secure/page.html");
+
+		// Setup the FilterChain to thrown an authentication failure exception
+		MockFilterChain chain = new MockFilterChain(false, true, false, false);
+
+		// Test
+		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
+		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
+		filter.setPortResolver(new MockPortResolver(8080, 8443));
+		/*
+		 * Disabled the call to afterPropertiesSet as it requires
+		 * applicationContext to be injected before it is invoked. We do not
+		 * have this filter configured in IOC for this test hence no
+		 * ApplicationContext
+		 */
+		// filter.afterPropertiesSet();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		filter.doFilter(request, response, chain);
+		assertEquals("/mycontext/login.jsp", response.getRedirectedUrl());
+		assertEquals("http://www.example.com:8080/mycontext/secure/page.html", AbstractProcessingFilter
+				.obtainFullRequestUrl(request));
+	}
+
+	public void testStartupDetectsMissingAuthenticationEntryPoint() throws Exception {
+		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
+
+		try {
+			filter.afterPropertiesSet();
+			fail("Should have thrown IllegalArgumentException");
+		}
+		catch (IllegalArgumentException expected) {
+			assertEquals("authenticationEntryPoint must be specified", expected.getMessage());
+		}
+	}
+
+	public void testStartupDetectsMissingPortResolver() throws Exception {
+		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
+		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
+		filter.setPortResolver(null);
+
+		try {
+			filter.afterPropertiesSet();
+			fail("Should have thrown IllegalArgumentException");
+		}
+		catch (IllegalArgumentException expected) {
+			assertEquals("portResolver must be specified", expected.getMessage());
+		}
+	}
+
+	public void testSuccessfulAccessGrant() throws Exception {
+		// Setup our HTTP request
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.setServletPath("/secure/page.html");
+
+		// Setup the FilterChain to thrown no exceptions
+		MockFilterChain chain = new MockFilterChain(false, false, false, false);
+
+		// Test
+		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
+		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
+
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		filter.doFilter(request, response, chain);
+	}
+
+	public void testSuccessfulStartupAndShutdownDown() throws Exception {
+		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
+
+		filter.init(null);
+		filter.destroy();
+		assertTrue(true);
+	}
+
+	public void testThrowIOException() throws Exception {
+		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
+
+		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(""));
+		/*
+		 * Disabled the call to afterPropertiesSet as it requires
+		 * applicationContext to be injected before it is invoked. We do not
+		 * have this filter configured in IOC for this test hence no
+		 * ApplicationContext
+		 */
+		// filter.afterPropertiesSet();
+		try {
+			filter.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(), new MockFilterChain(false,
+					false, false, true));
+			fail("Should have thrown IOException");
+		}
+		catch (IOException e) {
+			assertNull("The IOException thrown should not have been wrapped", e.getCause());
+		}
+	}
+
+	public void testThrowServletException() throws Exception {
+		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
+
+		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(""));
+		/*
+		 * Disabled the call to afterPropertiesSet as it requires
+		 * applicationContext to be injected before it is invoked. We do not
+		 * have this filter configured in IOC for this test hence no
+		 * ApplicationContext
+		 */
+		// filter.afterPropertiesSet();
+		try {
+			filter.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(), new MockFilterChain(false,
+					false, true, false));
+			fail("Should have thrown ServletException");
+		}
+		catch (ServletException e) {
+			assertNull("The ServletException thrown should not have been wrapped", e.getCause());
+		}
+	}
+
+	// ~ Inner Classes
+	// ==================================================================================================
+
+	private class MockFilterChain implements FilterChain {
+		private boolean throwAccessDenied;
+
+		private boolean throwAuthenticationFailure;
+
+		private boolean throwIOException;
+
+		private boolean throwServletException;
+
+		public MockFilterChain(boolean throwAccessDenied, boolean throwAuthenticationFailure,
+				boolean throwServletException, boolean throwIOException) {
+			this.throwAccessDenied = throwAccessDenied;
+			this.throwAuthenticationFailure = throwAuthenticationFailure;
+			this.throwServletException = throwServletException;
+			this.throwIOException = throwIOException;
+		}
+
+		public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
+			if (throwAccessDenied) {
+				throw new AccessDeniedException("As requested");
+			}
+
+			if (throwAuthenticationFailure) {
+				throw new BadCredentialsException("As requested");
+			}
+
+			if (throwServletException) {
+				throw new ServletException("As requested");
+			}
+
+			if (throwIOException) {
+				throw new IOException("As requested");
+			}
+		}
+	}
 }

+ 9 - 9
core/src/test/java/org/acegisecurity/ui/rememberme/TokenBasedRememberMeServicesTests.java

@@ -94,7 +94,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
         TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
         services.setKey("key");
         services.setUserDetailsService(new MockAuthenticationDao(null, true));
-        services.afterPropertiesSet();
+        //services.afterPropertiesSet();
 
         MockHttpServletRequest request = new MockHttpServletRequest();
         request.setRequestURI("dc");
@@ -114,7 +114,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
         TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
         services.setKey("key");
         services.setUserDetailsService(new MockAuthenticationDao(null, true));
-        services.afterPropertiesSet();
+        //services.afterPropertiesSet();
 
         Cookie cookie = new Cookie("unrelated_cookie", "foobar");
         MockHttpServletRequest request = new MockHttpServletRequest();
@@ -137,7 +137,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
         TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
         services.setKey("key");
         services.setUserDetailsService(new MockAuthenticationDao(user, false));
-        services.afterPropertiesSet();
+       // services.afterPropertiesSet();
 
         Cookie cookie = new Cookie(TokenBasedRememberMeServices.ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,
                 generateCorrectCookieContentForToken(System.currentTimeMillis() - 1000000, "someone", "password", "key"));
@@ -163,7 +163,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
         TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
         services.setKey("key");
         services.setUserDetailsService(new MockAuthenticationDao(user, false));
-        services.afterPropertiesSet();
+        //services.afterPropertiesSet();
 
         Cookie cookie = new Cookie(TokenBasedRememberMeServices.ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,
                 new String(Base64.encodeBase64("x".getBytes())));
@@ -188,7 +188,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
         TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
         services.setKey("key");
         services.setUserDetailsService(new MockAuthenticationDao(user, false));
-        services.afterPropertiesSet();
+       //services.afterPropertiesSet();
 
         Cookie cookie = new Cookie(TokenBasedRememberMeServices.ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,
                 "NOT_BASE_64_ENCODED");
@@ -214,7 +214,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
         TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
         services.setKey("key");
         services.setUserDetailsService(new MockAuthenticationDao(user, false));
-        services.afterPropertiesSet();
+        //services.afterPropertiesSet();
 
         Cookie cookie = new Cookie(TokenBasedRememberMeServices.ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,
                 generateCorrectCookieContentForToken(System.currentTimeMillis() + 1000000, "someone", "password",
@@ -241,7 +241,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
         TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
         services.setKey("key");
         services.setUserDetailsService(new MockAuthenticationDao(user, false));
-        services.afterPropertiesSet();
+        //services.afterPropertiesSet();
 
         Cookie cookie = new Cookie(TokenBasedRememberMeServices.ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,
                 new String(Base64.encodeBase64("username:NOT_A_NUMBER:signature".getBytes())));
@@ -263,7 +263,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
         TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
         services.setKey("key");
         services.setUserDetailsService(new MockAuthenticationDao(null, true));
-        services.afterPropertiesSet();
+        //services.afterPropertiesSet();
 
         Cookie cookie = new Cookie(TokenBasedRememberMeServices.ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,
                 generateCorrectCookieContentForToken(System.currentTimeMillis() + 1000000, "someone", "password", "key"));
@@ -288,7 +288,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
         TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
         services.setKey("key");
         services.setUserDetailsService(new MockAuthenticationDao(user, false));
-        services.afterPropertiesSet();
+       // services.afterPropertiesSet();
 
         Cookie cookie = new Cookie(TokenBasedRememberMeServices.ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,
                 generateCorrectCookieContentForToken(System.currentTimeMillis() + 1000000, "someone", "password", "key"));

+ 1 - 1
samples/annotations/pom.xml

@@ -6,7 +6,7 @@
   <parent>
     <groupId>org.acegisecurity</groupId>
     <artifactId>acegi-security-samples</artifactId>
-    <version>1.0.4-SNAPSHOT</version>
+    <version>1.0.5-SNAPSHOT</version>
   </parent>
   <artifactId>acegi-security-sample-annotations</artifactId>
   <name>Acegi Security System for Spring - Annotations sample</name>

+ 3 - 3
sandbox/pom.xml

@@ -4,7 +4,7 @@
   <parent>
     <groupId>org.acegisecurity</groupId>
     <artifactId>acegi-security-parent</artifactId>
-    <version>1.0.4-SNAPSHOT</version>
+    <version>1.0.5-SNAPSHOT</version>
   </parent>
   <artifactId>acegi-security-sandbox</artifactId>
   <name>Acegi Security System for Spring - Sandbox</name>
@@ -27,7 +27,7 @@
     <dependency>
       <groupId>org.acegisecurity</groupId>
       <artifactId>acegi-security</artifactId>
-      <version>1.1-SNAPSHOT</version>
+      <version>1.0.5-SNAPSHOT</version>
     </dependency>
   </dependencies>
-</project>
+</project>

+ 25 - 24
sandbox/spring-security-config/.classpath

@@ -5,39 +5,40 @@
 	<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
 	<classpathentry excluding="**/*.java" kind="src" output="target/test-classes" path="src/test/resources"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="var" path="M2_REPO/antlr/antlr/2.7.6/antlr-2.7.6.jar" sourcepath="M2_REPO/antlr/antlr/2.7.6/antlr-2.7.6-sources.jar"/>
-	<classpathentry kind="var" path="M2_REPO/log4j/log4j/1.2.9/log4j-1.2.9.jar" sourcepath="M2_REPO/log4j/log4j/1.2.9/log4j-1.2.9-sources.jar"/>
-	<classpathentry kind="var" path="M2_REPO/hsqldb/hsqldb/1.8.0.4/hsqldb-1.8.0.4.jar"/>
-	<classpathentry kind="var" path="M2_REPO/org/apache/directory/shared/shared-ldap/0.9.5.3/shared-ldap-0.9.5.3.jar"/>
-	<classpathentry kind="var" path="M2_REPO/org/acegisecurity/acegi-security/1.0.5-SNAPSHOT/acegi-security-1.0.5-SNAPSHOT.jar" sourcepath="M2_REPO/org/acegisecurity/acegi-security/1.0.5-SNAPSHOT/acegi-security-1.0.5-SNAPSHOT-sources.jar"/>
-	<classpathentry kind="var" path="M2_REPO/org/apache/directory/shared/shared-asn1/0.9.5.3/shared-asn1-0.9.5.3.jar"/>
-	<classpathentry kind="var" path="M2_REPO/net/sf/ehcache/ehcache/1.2.4/ehcache-1.2.4.jar" sourcepath="M2_REPO/net/sf/ehcache/ehcache/1.2.4/ehcache-1.2.4-sources.jar"/>
-	<classpathentry kind="var" path="M2_REPO/org/apache/directory/server/apacheds-core/1.0.0/apacheds-core-1.0.0.jar"/>
 	<classpathentry kind="var" path="M2_REPO/commons-collections/commons-collections/3.1/commons-collections-3.1.jar" sourcepath="M2_REPO/commons-collections/commons-collections/3.1/commons-collections-3.1-sources.jar"/>
+	<classpathentry kind="var" path="M2_REPO/cas/casclient/2.0.11/casclient-2.0.11.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-beans/2.0.4/spring-beans-2.0.4.jar"/>
 	<classpathentry kind="var" path="M2_REPO/jdbm/jdbm/1.0/jdbm-1.0.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/apache/directory/shared/shared-asn1/0.9.5.3/shared-asn1-0.9.5.3.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-support/2.0.4/spring-support-2.0.4.jar"/>
+	<classpathentry kind="var" path="M2_REPO/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar" sourcepath="M2_REPO/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4-sources.jar"/>
 	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-mock/2.0.4/spring-mock-2.0.4.jar"/>
 	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-dao/2.0.4/spring-dao-2.0.4.jar"/>
-	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-remoting/2.0.4/spring-remoting-2.0.4.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/apache/directory/shared/shared-ldap/0.9.5.3/shared-ldap-0.9.5.3.jar"/>
+	<classpathentry kind="var" path="M2_REPO/javax/servlet/jsp-api/2.0/jsp-api-2.0.jar"/>
+	<classpathentry kind="var" path="M2_REPO/commons-codec/commons-codec/1.3/commons-codec-1.3.jar" sourcepath="M2_REPO/commons-codec/commons-codec/1.3/commons-codec-1.3-sources.jar"/>
 	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-jdbc/2.0.4/spring-jdbc-2.0.4.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/slf4j/slf4j-log4j12/1.0.1/slf4j-log4j12-1.0.1.jar" sourcepath="M2_REPO/org/slf4j/slf4j-log4j12/1.0.1/slf4j-log4j12-1.0.1-sources.jar"/>
 	<classpathentry kind="var" path="M2_REPO/aspectj/aspectjrt/1.2/aspectjrt-1.2.jar"/>
-	<classpathentry kind="var" path="M2_REPO/junit/junit/3.8.1/junit-3.8.1.jar" sourcepath="M2_REPO/junit/junit/3.8.1/junit-3.8.1-sources.jar"/>
-	<classpathentry kind="var" path="M2_REPO/taglibs/standard/1.0.6/standard-1.0.6.jar" sourcepath="M2_REPO/taglibs/standard/1.0.6/standard-1.0.6-sources.jar"/>
-	<classpathentry kind="var" path="M2_REPO/org/apache/directory/server/apacheds-core-shared/1.0.0/apacheds-core-shared-1.0.0.jar"/>
-	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-web/2.0.4/spring-web-2.0.4.jar"/>
-	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-context/2.0.4/spring-context-2.0.4.jar"/>
+	<classpathentry kind="var" path="M2_REPO/antlr/antlr/2.7.6/antlr-2.7.6.jar" sourcepath="M2_REPO/antlr/antlr/2.7.6/antlr-2.7.6-sources.jar"/>
 	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-aop/2.0.4/spring-aop-2.0.4.jar"/>
-	<classpathentry kind="var" path="M2_REPO/javax/servlet/jsp-api/2.0/jsp-api-2.0.jar"/>
+	<classpathentry kind="var" path="M2_REPO/commons-lang/commons-lang/2.1/commons-lang-2.1.jar" sourcepath="M2_REPO/commons-lang/commons-lang/2.1/commons-lang-2.1-sources.jar"/>
 	<classpathentry kind="var" path="M2_REPO/javax/servlet/servlet-api/2.4/servlet-api-2.4.jar" sourcepath="M2_REPO/javax/servlet/servlet-api/2.4/servlet-api-2.4-sources.jar"/>
-	<classpathentry kind="var" path="M2_REPO/org/slf4j/slf4j-log4j12/1.0.1/slf4j-log4j12-1.0.1.jar" sourcepath="M2_REPO/org/slf4j/slf4j-log4j12/1.0.1/slf4j-log4j12-1.0.1-sources.jar"/>
-	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-core/2.0.4/spring-core-2.0.4.jar" sourcepath="/spring"/>
-	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-support/2.0.4/spring-support-2.0.4.jar"/>
-	<classpathentry kind="var" path="M2_REPO/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar" sourcepath="M2_REPO/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4-sources.jar"/>
-	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-beans/2.0.4/spring-beans-2.0.4.jar" sourcepath="/spring"/>
+	<classpathentry kind="var" path="M2_REPO/log4j/log4j/1.2.9/log4j-1.2.9.jar" sourcepath="M2_REPO/log4j/log4j/1.2.9/log4j-1.2.9-sources.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-web/2.0.4/spring-web-2.0.4.jar"/>
+	<classpathentry kind="var" path="M2_REPO/oro/oro/2.0.8/oro-2.0.8.jar" sourcepath="M2_REPO/oro/oro/2.0.8/oro-2.0.8-sources.jar"/>
+	<classpathentry kind="var" path="M2_REPO/junit/junit/3.8.1/junit-3.8.1.jar" sourcepath="M2_REPO/junit/junit/3.8.1/junit-3.8.1-sources.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/acegisecurity/acegi-security-tiger/1.0.5-SNAPSHOT/acegi-security-tiger-1.0.5-SNAPSHOT.jar" sourcepath="M2_REPO/org/acegisecurity/acegi-security-tiger/1.0.5-SNAPSHOT/acegi-security-tiger-1.0.5-SNAPSHOT-sources.jar"/>
+	<classpathentry kind="var" path="M2_REPO/hsqldb/hsqldb/1.8.0.4/hsqldb-1.8.0.4.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-context/2.0.4/spring-context-2.0.4.jar"/>
+	<classpathentry kind="var" path="M2_REPO/taglibs/standard/1.0.6/standard-1.0.6.jar" sourcepath="M2_REPO/taglibs/standard/1.0.6/standard-1.0.6-sources.jar"/>
 	<classpathentry kind="var" path="M2_REPO/jmock/jmock/1.0.1/jmock-1.0.1.jar" sourcepath="M2_REPO/jmock/jmock/1.0.1/jmock-1.0.1-sources.jar"/>
 	<classpathentry kind="var" path="M2_REPO/aopalliance/aopalliance/1.0/aopalliance-1.0.jar" sourcepath="M2_REPO/aopalliance/aopalliance/1.0/aopalliance-1.0-sources.jar"/>
-	<classpathentry kind="var" path="M2_REPO/commons-codec/commons-codec/1.3/commons-codec-1.3.jar" sourcepath="M2_REPO/commons-codec/commons-codec/1.3/commons-codec-1.3-sources.jar"/>
-	<classpathentry kind="var" path="M2_REPO/cas/casclient/2.0.11/casclient-2.0.11.jar"/>
-	<classpathentry kind="var" path="M2_REPO/commons-lang/commons-lang/2.1/commons-lang-2.1.jar" sourcepath="M2_REPO/commons-lang/commons-lang/2.1/commons-lang-2.1-sources.jar"/>
-	<classpathentry kind="var" path="M2_REPO/oro/oro/2.0.8/oro-2.0.8.jar" sourcepath="M2_REPO/oro/oro/2.0.8/oro-2.0.8-sources.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/apache/directory/server/apacheds-core/1.0.0/apacheds-core-1.0.0.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-remoting/2.0.4/spring-remoting-2.0.4.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/acegisecurity/acegi-security/1.0.5-SNAPSHOT/acegi-security-1.0.5-SNAPSHOT.jar" sourcepath="M2_REPO/org/acegisecurity/acegi-security/1.0.5-SNAPSHOT/acegi-security-1.0.5-SNAPSHOT-sources.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/apache/directory/server/apacheds-core-shared/1.0.0/apacheds-core-shared-1.0.0.jar"/>
+	<classpathentry kind="var" path="M2_REPO/net/sf/ehcache/ehcache/1.2.4/ehcache-1.2.4.jar" sourcepath="M2_REPO/net/sf/ehcache/ehcache/1.2.4/ehcache-1.2.4-sources.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/springframework/spring-core/2.0.4/spring-core-2.0.4.jar" sourcepath="/spring"/>
 	<classpathentry kind="output" path="target/classes"/>
 </classpath>

+ 4 - 0
sandbox/spring-security-config/.project

@@ -9,8 +9,12 @@
     <buildCommand>
       <name>org.eclipse.wst.validation.validationbuilder</name>
     </buildCommand>
+    <buildCommand>
+      <name>org.springframework.ide.eclipse.core.springbuilder</name>
+    </buildCommand>
   </buildSpec>
   <natures>
+    <nature>org.springframework.ide.eclipse.core.springnature</nature>
     <nature>org.eclipse.wst.common.project.facet.core.nature</nature>
     <nature>org.eclipse.jdt.core.javanature</nature>
     <nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>

+ 6 - 4
sandbox/spring-security-config/.settings/org.eclipse.jdt.core.prefs

@@ -1,5 +1,7 @@
-#Wed Jun 06 14:00:16 EST 2007
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.3
+#Sun Jun 17 10:48:58 EST 2007
 eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.source=1.3
-org.eclipse.jdt.core.compiler.compliance=1.3
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5

+ 5 - 0
sandbox/spring-security-config/pom.xml

@@ -11,6 +11,11 @@
   <version>2.0.0-M1</version>
   <url>http://maven.apache.org</url>
  <dependencies>
+		<dependency>
+			<groupId>org.acegisecurity</groupId>
+			<artifactId>acegi-security-tiger</artifactId>
+			<version>1.0.5-SNAPSHOT</version>
+		</dependency>
 		<dependency>
 			<groupId>org.springframework</groupId>
 			<artifactId>spring-remoting</artifactId>

+ 5 - 4
sandbox/spring-security-config/src/main/java/org/acegisecurity/config/AuthenticationMechanismBeanDefinitionParser.java

@@ -80,14 +80,15 @@ public class AuthenticationMechanismBeanDefinitionParser extends AbstractBeanDef
 	 * Creates a default bean definition.
 	 * @return
 	 */
-	protected static RootBeanDefinition createBeanDefinitionWithDefaults() {
-		RootBeanDefinition authMechanismBeanDef = new RootBeanDefinition(ProviderManager.class);
+	protected static RootBeanDefinition createAndRegisterBeanDefinitionWithDefaults(ParserContext parserContext) {
+		RootBeanDefinition beanDefinition = new RootBeanDefinition(ProviderManager.class);
 		ManagedList providers = new ManagedList();
 		// create authentication-repository (DaoAuthenticationProvider) and add that to list
 		RootBeanDefinition authRepo = AuthenticationRepositoryBeanDefinitionParser.createBeanDefinitionWithDefaults();
 		providers.add(authRepo);
-		authMechanismBeanDef.getPropertyValues().addPropertyValue("providers", providers);
-		return authMechanismBeanDef;
+		beanDefinition.getPropertyValues().addPropertyValue("providers", providers);
+		parserContext.getReaderContext().registerWithGeneratedName(beanDefinition);
+		return beanDefinition;
 	}
 	
 }

+ 10 - 5
sandbox/spring-security-config/src/main/java/org/acegisecurity/config/AuthenticationProcessingFilterBeanDefinitionParser.java

@@ -5,6 +5,7 @@ package org.acegisecurity.config;
 
 import org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices;
 import org.acegisecurity.ui.webapp.AuthenticationProcessingFilter;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
 import org.springframework.beans.factory.support.RootBeanDefinition;
 import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
@@ -51,15 +52,19 @@ public class AuthenticationProcessingFilterBeanDefinitionParser extends Abstract
 		}
 	}
 
-	protected static RootBeanDefinition createBeandefinitionWithDefaults() {
+	protected static RootBeanDefinition createBeandefinitionWithDefaults(ParserContext parserContext, RootBeanDefinition authenticationManager, RootBeanDefinition rememberMeServices) {
 		RootBeanDefinition definition = new RootBeanDefinition(AuthenticationProcessingFilter.class);
-		definition.getPropertyValues().addPropertyValue("authenticationManager",
-				AuthenticationMechanismBeanDefinitionParser.createBeanDefinitionWithDefaults());
-		definition.getPropertyValues().addPropertyValue("rememberMeServices",
-				RememberMeServicesBeanDefinitionParser.doCreateBeanDefintionWithDefaults());
+		definition.getPropertyValues().addPropertyValue("authenticationManager",authenticationManager);
+		definition.getPropertyValues().addPropertyValue("rememberMeServices",rememberMeServices);
+//		RootBeanDefinition beanDefinition = AuthenticationMechanismBeanDefinitionParser.createAndRegisterBeanDefinitionWithDefaults(parserContext);
+//		definition.getPropertyValues().addPropertyValue("authenticationManager",
+//				parserContext.getReaderContext().getRegistry().getBeanDefinition(beanDefinition.getBeanClassName()));
+//		definition.getPropertyValues().addPropertyValue("rememberMeServices",
+//				RememberMeServicesBeanDefinitionParser.createAndRegisterBeanDefintionWithDefaults(parserContext));
 		/* TODO: There should not be any defaults for these urls ?!?! */
 		definition.getPropertyValues().addPropertyValue("authenticationFailureUrl", "/acegilogin.jsp?login_error=1");
 		definition.getPropertyValues().addPropertyValue("defaultTargetUrl", "/");
+		
 		return definition;
 	}
 

+ 204 - 13
sandbox/spring-security-config/src/main/java/org/acegisecurity/config/AutoConfigBeanDefinitionParser.java

@@ -3,12 +3,28 @@
  */
 package org.acegisecurity.config;
 
-import org.acegisecurity.context.HttpSessionContextIntegrationFilter;
-import org.acegisecurity.ui.logout.LogoutFilter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.acegisecurity.AuthenticationManager;
+import org.acegisecurity.annotation.SecurityAnnotationAttributes;
+import org.acegisecurity.intercept.method.MethodDefinitionAttributes;
+import org.acegisecurity.intercept.method.aopalliance.MethodDefinitionSourceAdvisor;
+import org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor;
+import org.acegisecurity.intercept.web.FilterInvocationDefinitionDecorator;
+import org.acegisecurity.intercept.web.FilterInvocationDefinitionSourceMapping;
+import org.acegisecurity.intercept.web.FilterSecurityInterceptor;
+import org.acegisecurity.intercept.web.PathBasedFilterInvocationDefinitionMap;
+import org.acegisecurity.runas.RunAsManagerImpl;
+import org.acegisecurity.vote.AffirmativeBased;
+import org.acegisecurity.vote.AuthenticatedVoter;
+import org.acegisecurity.vote.RoleVoter;
+import org.acegisecurity.vote.UnanimousBased;
+import org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter;
+import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
 import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.ManagedList;
 import org.springframework.beans.factory.support.RootBeanDefinition;
-import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
 import org.springframework.beans.factory.xml.BeanDefinitionParser;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.w3c.dom.Element;
@@ -21,28 +37,181 @@ import org.w3c.dom.Element;
  * @author Vishal Puri
  * 
  */
-public class AutoConfigBeanDefinitionParser  implements BeanDefinitionParser {
+public class AutoConfigBeanDefinitionParser implements BeanDefinitionParser {
+
+	private RootBeanDefinition authenticationManager;
+
+	private RootBeanDefinition rememberMeServices;
+
+	private ManagedList decisionVoters = new ManagedList();
 
 	public BeanDefinition parse(Element element, ParserContext parserContext) {
+		// authentication manager
+		this.authenticationManager = AuthenticationMechanismBeanDefinitionParser
+				.createAndRegisterBeanDefinitionWithDefaults(parserContext);
+		// remembermeServices
+		this.rememberMeServices = RememberMeServicesBeanDefinitionParser
+				.createAndRegisterBeanDefintionWithDefaults(parserContext);
+		// flters
 		createAndRegisterBeanDefinitionForHttpSessionContextIntegrationFilter(parserContext);
-		createAndRegisterBeanDefinitionForLogoutFilter(parserContext);
-		createAndRegisterBeanDefinitionForAuthenticationProcessingFilter(parserContext);
+		createAndRegisterBeanDefinitionForLogoutFilter(parserContext, rememberMeServices);
+		createAndRegisterBeanDefinitionForAuthenticationProcessingFilter(parserContext, authenticationManager,
+				rememberMeServices);
+		createAndRegisterBeanDefinitionForRememberMeProcessingFilter(parserContext, authenticationManager);
+		createAndRegisterBeanDefinitionForExceptionTranslationFilter(parserContext);
+		createAndRegisterBeanDefintionForSecurityContextHolderAwareRequestFilter(parserContext);
+
+		// method interceptor
+		createAndRegisterBeanDefinitinoForMethodDefinitionSourceAdvisor(parserContext, authenticationManager);
+		createAndRegisterDefaultAdvisorAutoProxyCreator(parserContext);
+
+		// filter security interceptor
+		createAndRegisterBeanDefinitionForFilterSecurityInterceptor(parserContext, authenticationManager);
 		return null;
 	}
 
-	private void createAndRegisterBeanDefinitionForAuthenticationProcessingFilter(ParserContext parserContext) {
-		RootBeanDefinition defintion = AuthenticationProcessingFilterBeanDefinitionParser.createBeandefinitionWithDefaults();
+	private void createAndRegisterBeanDefintionForSecurityContextHolderAwareRequestFilter(ParserContext parserContext) {
+		RootBeanDefinition beanDefinition = new RootBeanDefinition(SecurityContextHolderAwareRequestFilter.class);
+		registerBeanDefinition(parserContext, beanDefinition);
+	}
+
+	/**
+	 * Creates <code>FilterSecurityInterceptor</code> bean definition and
+	 * register it with the <code>ParserContext</code>
+	 * 
+	 * @param parserContext To register the bean definition with
+	 * @param authenticationManager The <code>AuthenticationManager</code> to
+	 * set as a property in the bean definition
+	 */
+	private void createAndRegisterBeanDefinitionForFilterSecurityInterceptor(ParserContext parserContext,
+			RootBeanDefinition authenticationManager) {
+		RootBeanDefinition filterInvocationInterceptor = new RootBeanDefinition(FilterSecurityInterceptor.class);
+		filterInvocationInterceptor.getPropertyValues()
+				.addPropertyValue("authenticationManager", authenticationManager);
+		RootBeanDefinition accessDecisionManager = createAccessDecisionManagerAffirmativeBased();
+		filterInvocationInterceptor.getPropertyValues()
+				.addPropertyValue("accessDecisionManager", accessDecisionManager);
+
+		FilterInvocationDefinitionDecorator source = new FilterInvocationDefinitionDecorator();
+		source.setDecorated(new PathBasedFilterInvocationDefinitionMap());
+
+		FilterInvocationDefinitionSourceMapping mapping = new FilterInvocationDefinitionSourceMapping();
+
+		String url1 = "/acegilogin.jsp";
+		String value1 = "IS_AUTHENTICATED_ANONYMOUSLY";
+
+		String url2 = "/**";
+		String value2 = "IS_AUTHENTICATED_REMEMBERED";
+
+		mapping.setUrl(url1);
+		mapping.addConfigAttribute(value1);
+
+		mapping.setUrl(url2);
+		mapping.addConfigAttribute(value2);
+
+		List mappings = new ArrayList();
+		mappings.add(mapping);
+		source.setMappings(mappings);
+		filterInvocationInterceptor.getPropertyValues().addPropertyValue("objectDefinitionSource",
+				source.getDecorated());
+		registerBeanDefinition(parserContext, filterInvocationInterceptor);
+	}
+
+	private RootBeanDefinition createAccessDecisionManagerAffirmativeBased() {
+		RootBeanDefinition accessDecisionManager = new RootBeanDefinition(AffirmativeBased.class);
+		accessDecisionManager.getPropertyValues().addPropertyValue("allowIfAllAbstainDecisions", Boolean.FALSE);
+		RootBeanDefinition authenticatedVoter = new RootBeanDefinition(AuthenticatedVoter.class);
+		this.decisionVoters.add(authenticatedVoter);
+		accessDecisionManager.getPropertyValues().addPropertyValue("decisionVoters", decisionVoters);
+		return accessDecisionManager;
+	}
+
+	private void createAndRegisterDefaultAdvisorAutoProxyCreator(ParserContext parserContext) {
+		registerBeanDefinition(parserContext, new RootBeanDefinition(DefaultAdvisorAutoProxyCreator.class));
+	}
+
+	private void createAndRegisterBeanDefinitinoForMethodDefinitionSourceAdvisor(ParserContext parserContext,
+			RootBeanDefinition authenticationManager) {
+		RootBeanDefinition methodSecurityAdvisor = new RootBeanDefinition(MethodDefinitionSourceAdvisor.class);
+
+		RootBeanDefinition securityInterceptor = createMethodSecurityInterceptor(authenticationManager);
+		methodSecurityAdvisor.getConstructorArgumentValues().addIndexedArgumentValue(0, securityInterceptor);
+		registerBeanDefinition(parserContext, methodSecurityAdvisor);
+
+	}
+
+	private RootBeanDefinition createAccessDecisionManagerUnanimousBased() {
+		RootBeanDefinition accessDecisionManager = new RootBeanDefinition(UnanimousBased.class);
+		accessDecisionManager.getPropertyValues().addPropertyValue("allowIfAllAbstainDecisions", Boolean.FALSE);
+		RootBeanDefinition roleVoter = createRoleVoter();
+		decisionVoters.add(roleVoter);
+		accessDecisionManager.getPropertyValues().addPropertyValue("decisionVoters", decisionVoters);
+		return accessDecisionManager;
+	}
+
+	private RootBeanDefinition createRoleVoter() {
+		return new RootBeanDefinition(RoleVoter.class);
+	}
+
+	private RootBeanDefinition createMethodSecurityInterceptor(RootBeanDefinition authenticationManager) {
+		RootBeanDefinition securityInterceptor = new RootBeanDefinition(MethodSecurityInterceptor.class);
+		securityInterceptor.getPropertyValues().addPropertyValue("authenticationManager", authenticationManager);
+		RootBeanDefinition accessDecisionManager = createAccessDecisionManagerUnanimousBased();
+		securityInterceptor.getPropertyValues().addPropertyValue("accessDecisionManager", accessDecisionManager);
+		securityInterceptor.getPropertyValues().addPropertyValue("validateConfigAttributes", Boolean.FALSE);
+		RootBeanDefinition runAsManager = createRunAsManager();
+		securityInterceptor.getPropertyValues().addPropertyValue("runAsManager", runAsManager);
+		RootBeanDefinition objectDefinitionSource = createMethodDefinitionAttributes();
+		securityInterceptor.getPropertyValues().addPropertyValue("objectDefinitionSource", objectDefinitionSource);
+		return securityInterceptor;
+	}
+
+	private RootBeanDefinition createMethodDefinitionAttributes() {
+		RootBeanDefinition objectDefinitionSource = new RootBeanDefinition(MethodDefinitionAttributes.class);
+		RootBeanDefinition attributes = createSecurityAnnotationAttributes();
+		objectDefinitionSource.getPropertyValues().addPropertyValue("attributes", attributes);
+		return objectDefinitionSource;
+	}
+
+	private RootBeanDefinition createSecurityAnnotationAttributes() {
+		return new RootBeanDefinition(SecurityAnnotationAttributes.class);
+	}
+
+	private RootBeanDefinition createRunAsManager() {
+		RootBeanDefinition runAsManager = new RootBeanDefinition(RunAsManagerImpl.class);
+		runAsManager.getPropertyValues().addPropertyValue("key", "my_run_as_password");
+		return runAsManager;
+	}
+
+	private void createAndRegisterBeanDefinitionForExceptionTranslationFilter(ParserContext parserContext) {
+		registerBeanDefinition(parserContext, ExceptionTranslationFilterBeanDefinitionParser
+				.createBeanDefinitionWithDefaults());
+	}
+
+	private void createAndRegisterBeanDefinitionForRememberMeProcessingFilter(ParserContext parserContext,
+			RootBeanDefinition authenticationManager) {
+		registerBeanDefinition(parserContext, RememberMeFilterBeanDefinitionParser.createBeanDefinitionWithDefaults(
+				parserContext, authenticationManager));
+	}
+
+	private void createAndRegisterBeanDefinitionForAuthenticationProcessingFilter(ParserContext parserContext,
+			RootBeanDefinition authenticationManager, RootBeanDefinition rememberMeServices) {
+		RootBeanDefinition defintion = AuthenticationProcessingFilterBeanDefinitionParser
+				.createBeandefinitionWithDefaults(parserContext, authenticationManager, rememberMeServices);
 		registerBeanDefinition(parserContext, defintion);
 	}
 
-	private void createAndRegisterBeanDefinitionForLogoutFilter(ParserContext parserContext) {
-		RootBeanDefinition defintion =LogoutFilterBeanDefinitionParser.doCreateBeanDefinitionWithDefaults();
+	private void createAndRegisterBeanDefinitionForLogoutFilter(ParserContext parserContext,
+			RootBeanDefinition rememberMeServices) {
+		RootBeanDefinition defintion = LogoutFilterBeanDefinitionParser
+				.createBeanDefinitionWithDefaults(rememberMeServices);
 		registerBeanDefinition(parserContext, defintion);
 	}
 
 	private void createAndRegisterBeanDefinitionForHttpSessionContextIntegrationFilter(ParserContext parserContext) {
-		RootBeanDefinition defintion = ContextIntegrationBeanDefinitionParser.doCreateBeanDefinitionWithDefaults();
+		RootBeanDefinition defintion = ContextIntegrationBeanDefinitionParser.createBeanDefinitionWithDefaults();
 		registerBeanDefinition(parserContext, defintion);
+		// retrieveBeanDefinition(parserContext, o)
 	}
 
 	/**
@@ -50,7 +219,29 @@ public class AutoConfigBeanDefinitionParser  implements BeanDefinitionParser {
 	 * @param defintion
 	 */
 	private void registerBeanDefinition(ParserContext parserContext, RootBeanDefinition defintion) {
-		parserContext.getRegistry().registerBeanDefinition(parserContext.getReaderContext().generateBeanName(defintion), defintion);
+		parserContext.getRegistry().registerBeanDefinition(
+				parserContext.getReaderContext().generateBeanName(defintion), defintion);
+	}
+
+	/**
+	 * Returns a <code>BeanDefinition</code> of the specified type.
+	 * 
+	 * @param parserContext
+	 * @param type
+	 * @return
+	 */
+	private RootBeanDefinition retrieveBeanDefinition(ParserContext parserContext, Class type) {
+		String[] names = parserContext.getRegistry().getBeanDefinitionNames();
+		for (String name : names) {
+			BeanDefinition beanDefinition = parserContext.getRegistry().getBeanDefinition(name);
+			if (type.isInstance(beanDefinition)) {
+				return (RootBeanDefinition) beanDefinition;
+			}
+		}
+		return null;
 	}
 
+	private Class ss(Object o) {
+		return o.getClass();
+	}
 }

+ 2 - 2
sandbox/spring-security-config/src/main/java/org/acegisecurity/config/ContextIntegrationBeanDefinitionParser.java

@@ -75,11 +75,11 @@ public class ContextIntegrationBeanDefinitionParser extends AbstractSingleBeanDe
 			builder.addPropertyValue(ALLOW_SESSION_CREATION, Boolean.FALSE);
 		}
 		else {
-			doCreateBeanDefinitionWithDefaults();
+			createBeanDefinitionWithDefaults();
 		}
 	}
 
-	protected static RootBeanDefinition doCreateBeanDefinitionWithDefaults() {
+	protected static RootBeanDefinition createBeanDefinitionWithDefaults() {
 		RootBeanDefinition definition = new RootBeanDefinition(HttpSessionContextIntegrationFilter.class);
 		definition.getPropertyValues().addPropertyValue(ALLOW_SESSION_CREATION, Boolean.TRUE);
 		return definition;

+ 31 - 1
sandbox/spring-security-config/src/main/java/org/acegisecurity/config/ExceptionTranslationFilterBeanDefinitionParser.java

@@ -5,6 +5,7 @@ package org.acegisecurity.config;
 
 import org.acegisecurity.ui.AccessDeniedHandlerImpl;
 import org.acegisecurity.ui.ExceptionTranslationFilter;
+import org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint;
 import org.springframework.beans.factory.config.RuntimeBeanReference;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
 import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -46,7 +47,7 @@ import org.w3c.dom.Element;
  * </p>
  * 
  * @author Vishal Puri
- * @version 
+ * @version
  * @see {@link org.acegisecurity.ui.ExceptionTranslationFilter}
  * @see {@link org.acegisecurity.ui.AccessDeniedHandler}
  */
@@ -62,6 +63,10 @@ public class ExceptionTranslationFilterBeanDefinitionParser extends AbstractBean
 
 	private static final String ENTRY_POINT_REF = "entryPointBeanRef";
 
+	private static final String LOGIN_FORM_URL = "loginFormUrl";
+
+	private static final String LOGIN_FORM_URL_VALUE = "/acegilogin.jsp";
+
 	protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
 
 		RootBeanDefinition exceptionFilterDef = new RootBeanDefinition(ExceptionTranslationFilter.class);
@@ -144,4 +149,29 @@ public class ExceptionTranslationFilterBeanDefinitionParser extends AbstractBean
 			definition.getPropertyValues().addPropertyValue(property, propertyValue);
 		}
 	}
+
+	/**
+	 * Creates <code>BeanDefintion</code> for
+	 * <code>ExceptionTranslationFilter</code> with it's default properties.
+	 * @return beanDefinition The bean defintion configured with default
+	 * properties
+	 */
+	protected static RootBeanDefinition createBeanDefinitionWithDefaults() {
+		RootBeanDefinition beanDefinition = new RootBeanDefinition(ExceptionTranslationFilter.class);
+		beanDefinition.getPropertyValues().addPropertyValue("authenticationEntryPoint",
+				createBeanDefintionForAuthenticationProcessingFilterEntryPoint());
+		return beanDefinition;
+	}
+
+	/**
+	 * Creates <code>BeanDefintion</code> for
+	 * <code>AuthenticationProcessingFilterEntryPoint</code> with it's default
+	 * properties.
+	 * @return beanDefinition The bean defintion configured with default
+	 */
+	protected static RootBeanDefinition createBeanDefintionForAuthenticationProcessingFilterEntryPoint() {
+		RootBeanDefinition beanDefinition = new RootBeanDefinition(AuthenticationProcessingFilterEntryPoint.class);
+		beanDefinition.getPropertyValues().addPropertyValue(LOGIN_FORM_URL, LOGIN_FORM_URL_VALUE);
+		return beanDefinition;
+	}
 }

+ 2 - 2
sandbox/spring-security-config/src/main/java/org/acegisecurity/config/LogoutFilterBeanDefinitionParser.java

@@ -92,13 +92,13 @@ public class LogoutFilterBeanDefinitionParser extends AbstractBeanDefinitionPars
 	 * @param isAutoconfig
 	 * @return definition
 	 */
-	protected static RootBeanDefinition doCreateBeanDefinitionWithDefaults() {
+	protected static RootBeanDefinition createBeanDefinitionWithDefaults(RootBeanDefinition rememberMeServices) {
 		RootBeanDefinition definition = new RootBeanDefinition(LogoutFilter.class);
 		definition.getConstructorArgumentValues().addIndexedArgumentValue(0, REDIRECT_AFTER_LOGOUT_URL_VALUE);
 		// create BeanDefinitions for LogoutHandlers
 		// (TokenBasedRememberMeServices) and (SecuritycontextLogoutHandler)
 		ManagedList handlers = new ManagedList();
-		RootBeanDefinition rememberMeServices = RememberMeServicesBeanDefinitionParser.doCreateBeanDefintionWithDefaults();
+		//RootBeanDefinition rememberMeServices = RememberMeServicesBeanDefinitionParser.doCreateBeanDefintionWithDefaults();
 		handlers.add(rememberMeServices);
 		handlers.add(new RootBeanDefinition(SecurityContextLogoutHandler.class));
 		definition.getConstructorArgumentValues().addIndexedArgumentValue(1, handlers);

+ 6 - 4
sandbox/spring-security-config/src/main/java/org/acegisecurity/config/RememberMeFilterBeanDefinitionParser.java

@@ -29,10 +29,6 @@ public class RememberMeFilterBeanDefinitionParser extends AbstractBeanDefinition
 		Assert.notNull(parserContext, "ParserContext must not be null");
 		
 		RootBeanDefinition rememberMeFilterBeanDef = new RootBeanDefinition(RememberMeProcessingFilter.class);
-		
-		// detect all the required dependencies and autowire them by type
-		rememberMeFilterBeanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_AUTODETECT);
-		
 		// check if rememberMeServicesBeanRef is defined and if it's specified use its referred bean
 		String rememberMeServicesRef = element.getAttribute(REMEMBER_ME_SERVICES_REF);
 		if (StringUtils.hasLength(rememberMeServicesRef)) {
@@ -41,4 +37,10 @@ public class RememberMeFilterBeanDefinitionParser extends AbstractBeanDefinition
 		} 
 		return rememberMeFilterBeanDef;
 	}
+	
+	protected static RootBeanDefinition createBeanDefinitionWithDefaults(ParserContext parserContext, RootBeanDefinition authenticationManager) {
+		RootBeanDefinition definition= new RootBeanDefinition(RememberMeProcessingFilter.class);
+		definition.getPropertyValues().addPropertyValue("authenticationManager",authenticationManager);
+		return definition;
+	}
 }

+ 5 - 4
sandbox/spring-security-config/src/main/java/org/acegisecurity/config/RememberMeServicesBeanDefinitionParser.java

@@ -72,10 +72,11 @@ public class RememberMeServicesBeanDefinitionParser extends AbstractBeanDefiniti
 		return rememberMeServicesBeanDef;
 	}
 	
-	protected static RootBeanDefinition doCreateBeanDefintionWithDefaults(){
-		RootBeanDefinition definition = new RootBeanDefinition(TokenBasedRememberMeServices.class);
-		definition.getPropertyValues().addPropertyValue(KEY, "key");
-		return definition;
+	protected static RootBeanDefinition createAndRegisterBeanDefintionWithDefaults(ParserContext parserContext){
+		RootBeanDefinition beanDefinition = new RootBeanDefinition(TokenBasedRememberMeServices.class);
+		beanDefinition.getPropertyValues().addPropertyValue(KEY, "key");
+		parserContext.getReaderContext().registerWithGeneratedName(beanDefinition);
+		return beanDefinition;
 	}
 
 }

+ 21 - 3
sandbox/spring-security-config/src/test/java/org/acegisecurity/config/AutoConfigBeanDefinitionParserTests.java

@@ -10,8 +10,11 @@ import junit.framework.TestCase;
 
 import org.acegisecurity.AuthenticationManager;
 import org.acegisecurity.context.HttpSessionContextIntegrationFilter;
+import org.acegisecurity.intercept.method.MethodDefinitionSource;
+import org.acegisecurity.intercept.method.aopalliance.MethodDefinitionSourceAdvisor;
 import org.acegisecurity.ui.logout.LogoutFilter;
 import org.acegisecurity.ui.logout.LogoutHandler;
+import org.acegisecurity.ui.rememberme.RememberMeProcessingFilter;
 import org.acegisecurity.ui.rememberme.RememberMeServices;
 import org.acegisecurity.ui.webapp.AuthenticationProcessingFilter;
 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -47,7 +50,6 @@ public class AutoConfigBeanDefinitionParserTests extends TestCase {
 		assertFalse(filter.isForceEagerSessionCreation());
 		assertFalse(filter.isCloneFromHttpSession());
 	}
-	
 
 	public void testLogoutFilterDefinitionCreatedWithDefaults() throws Exception {
 		String[] names = bf.getBeanNamesForType(LogoutFilter.class);
@@ -71,8 +73,23 @@ public class AutoConfigBeanDefinitionParserTests extends TestCase {
 		assertNotNull(authMgr);
 		RememberMeServices remMeServices = filter.getRememberMeServices();
 		assertNotNull(remMeServices);
-		assertEquals("/acegilogin.jsp?login_error=1",filter.getAuthenticationFailureUrl());
-		assertEquals( "/",filter.getDefaultTargetUrl());
+		assertEquals("/acegilogin.jsp?login_error=1", filter.getAuthenticationFailureUrl());
+		assertEquals("/", filter.getDefaultTargetUrl());
+	}
+
+	public void testRememberMePRocessingFilterCreatedWithDefaults() {
+		Map map = bf.getBeansOfType(RememberMeProcessingFilter.class);
+		RememberMeProcessingFilter filter = (RememberMeProcessingFilter) map.values().iterator().next();
+	}
+
+	public void testMethodDefinitionSourceAdvisorCreatedWithDefaults() throws Exception {
+		Map map = bf.getBeansOfType(MethodDefinitionSourceAdvisor.class);
+		assertEquals(1, map.size());
+		MethodDefinitionSourceAdvisor advisor = (MethodDefinitionSourceAdvisor) map.values().iterator().next();
+		Field transactionAttributeSource = makeAccessibleAndGetFieldByName(advisor.getClass().getDeclaredFields(), "transactionAttributeSource");
+		assertNotNull(transactionAttributeSource);
+		assertTrue(transactionAttributeSource.get(advisor) instanceof MethodDefinitionSource);
+
 	}
 
 	private Field makeAccessibleAndGetFieldByName(Field[] declaredFields, String name) {
@@ -85,4 +102,5 @@ public class AutoConfigBeanDefinitionParserTests extends TestCase {
 		}
 		return field;
 	}
+
 }

+ 0 - 1
sandbox/spring-security-config/src/test/java/org/acegisecurity/config/RememberMeBeanDefinitionParserTest.java

@@ -16,5 +16,4 @@ public class RememberMeBeanDefinitionParserTest extends TestCase {
 		assertEquals(1, mgr.getProviders().size());
 		assertTrue(mgr.getProviders().get(0) instanceof DaoAuthenticationProvider);
 	}
-
 }

+ 3 - 3
sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/applicationContext-acegi-security.xml

@@ -128,9 +128,9 @@
 			<value>
 				CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
 				PATTERN_TYPE_APACHE_ANT
-				/secure/extreme/**=ROLE_SUPERVISOR
-				/secure/**=IS_AUTHENTICATED_REMEMBERED
-				/**=IS_AUTHENTICATED_ANONYMOUSLY
+				/acegilogin.jsp=IS_AUTHENTICATED_ANONYMOUSLY
+				/**=IS_AUTHENTICATED_REMEMBERED
+
 			</value>
 		</property>
 	</bean>

+ 4 - 22
sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/auto-config.xml

@@ -25,26 +25,8 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc
 		<security:properties
 			resource="classpath:org/acegisecurity/config/user.properties" />
 	</security:principal-repository>
-
-	<!--<bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
-		<property name="authenticationManager"><ref bean="authenticationManager"/></property>
-		<property name="accessDecisionManager">
-		<bean class="org.acegisecurity.vote.AffirmativeBased">
-		<property name="allowIfAllAbstainDecisions" value="false"/>
-		<property name="decisionVoters">
-		<list>
-		<bean class="org.acegisecurity.vote.RoleVoter"/>
-		<bean class="org.acegisecurity.vote.AuthenticatedVoter"/>
-		</list>
-		</property>
-		</bean>
-		</property>
-		<property name="objectDefinitionSource">
-		<value>
-		org.springframework.samples.petclinic.Clinic.*=IS_AUTHENTICATED_REMEMBERED
-		org.springframework.samples.petclinic.Clinic.storeVisit=ROLE_SUPERVISOR
-		</value>
-		</property>
-		</bean>
-	-->
+	
+	<bean id="bankService" class="org.acegisecurity.BankServiceImpl"/>
+	
+	
 </beans>

+ 1 - 1
sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/user.properties

@@ -1,2 +1,2 @@
 vishal=ity,ROLE_ADMIN
-ity=vishal,ROLE_TELLER
+ity=vishal,ROLE_TELLER,ROLE_PERMISSION_LIST