From b2c30277f42a952a7c4208759ad992489c9427c6 Mon Sep 17 00:00:00 2001 From: Vishal Puri Date: Tue, 19 Jun 2007 04:08:19 +0000 Subject: [PATCH] SEC-271: work on security:autoconfig --- .../dao/DaoAuthenticationProvider.java | 244 +++-- .../ui/AbstractProcessingFilter.java | 921 ++++++++++-------- .../ui/ExceptionTranslationFilter.java | 390 +++++--- .../acegisecurity/ui/logout/LogoutFilter.java | 238 +++-- .../logout/SecurityContextLogoutHandler.java | 4 +- .../RememberMeProcessingFilter.java | 64 +- .../TokenBasedRememberMeServices.java | 585 ++++++----- ...henticationProcessingFilterEntryPoint.java | 325 +++--- .../ui/ExceptionTranslationFilterTests.java | 499 +++++----- .../TokenBasedRememberMeServicesTests.java | 18 +- samples/annotations/pom.xml | 2 +- sandbox/pom.xml | 6 +- sandbox/spring-security-config/.classpath | 57 +- sandbox/spring-security-config/.project | 4 + .../.settings/org.eclipse.jdt.core.prefs | 10 +- sandbox/spring-security-config/pom.xml | 5 + ...ticationMechanismBeanDefinitionParser.java | 9 +- ...nProcessingFilterBeanDefinitionParser.java | 15 +- .../AutoConfigBeanDefinitionParser.java | 217 ++++- ...ontextIntegrationBeanDefinitionParser.java | 4 +- ...TranslationFilterBeanDefinitionParser.java | 32 +- .../LogoutFilterBeanDefinitionParser.java | 4 +- .../RememberMeFilterBeanDefinitionParser.java | 10 +- ...ememberMeServicesBeanDefinitionParser.java | 9 +- .../AutoConfigBeanDefinitionParserTests.java | 24 +- .../RememberMeBeanDefinitionParserTest.java | 1 - .../applicationContext-acegi-security.xml | 6 +- .../org/acegisecurity/config/auto-config.xml | 26 +- .../org/acegisecurity/config/user.properties | 2 +- 29 files changed, 2232 insertions(+), 1499 deletions(-) diff --git a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java index f8fa4d02a3..1c24a1787b 100644 --- a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java +++ b/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 ================================================================================================ +public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider implements + ApplicationContextAware, Ordered { - private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder(); - private SaltSource saltSource; - private UserDetailsService userDetailsService; - private boolean includeDetailsObject = true; - private int order = -1; // default: same as non-Ordered + // ~ Instance fields + // ================================================================================================ - //~ Methods ======================================================================================================== + private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder(); - protected void additionalAuthenticationChecks(UserDetails userDetails, - UsernamePasswordAuthenticationToken authentication) - throws AuthenticationException { - Object salt = null; + private SaltSource saltSource; - 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(); + private UserDetailsService userDetailsService; - if (!passwordEncoder.isPasswordValid( - userDetails.getPassword(), presentedPassword, salt)) { - throw new BadCredentialsException(messages.getMessage( - "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), - includeDetailsObject ? userDetails : null); - } - } + private boolean includeDetailsObject = true; - protected void doAfterPropertiesSet() throws Exception { - Assert.notNull(this.userDetailsService, "A UserDetailsService must be set"); - } + private int DEFAULT_RDER = Integer.MAX_VALUE; // default: same as - public PasswordEncoder getPasswordEncoder() { - return passwordEncoder; - } + // non-Ordered - public SaltSource getSaltSource() { - return saltSource; - } + private int order = DEFAULT_RDER; - public UserDetailsService getUserDetailsService() { - return userDetailsService; - } + private boolean isSetUserDetailsServiceInvoked; - protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) - throws AuthenticationException { - UserDetails loadedUser; + private ApplicationContext applicationContext; - try { - loadedUser = this.getUserDetailsService().loadUserByUsername(username); - } catch (DataAccessException repositoryProblem) { - throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem); - } + // ~ Methods + // ======================================================================================================== - if (loadedUser == null) { - throw new AuthenticationServiceException( - "UserDetailsService returned null, which is an interface contract violation"); - } - return loadedUser; - } + protected void additionalAuthenticationChecks(UserDetails userDetails, + UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { + Object salt = null; - /** - * 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; - } + if (this.saltSource != null) { + salt = this.saltSource.getSalt(userDetails); + } - /** - * The source of salts to use when decoding passwords. null is a valid value, meaning the - * DaoAuthenticationProvider will present null to the relevant - * PasswordEncoder. - * - * @param saltSource to use when attempting to decode passwords via the PasswordEncoder - */ - public void setSaltSource(SaltSource saltSource) { - this.saltSource = saltSource; - } + if (authentication.getCredentials() == null) { + throw new BadCredentialsException(messages.getMessage( + "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), + includeDetailsObject ? userDetails : null); + } - public void setUserDetailsService(UserDetailsService userDetailsService) { - this.userDetailsService = userDetailsService; - } + String presentedPassword = authentication.getCredentials() == null ? "" : authentication.getCredentials() + .toString(); - public boolean isIncludeDetailsObject() { - return includeDetailsObject; - } + if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) { + throw new BadCredentialsException(messages.getMessage( + "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), + includeDetailsObject ? userDetails : null); + } + } - public void setIncludeDetailsObject(boolean includeDetailsObject) { - this.includeDetailsObject = includeDetailsObject; - } + protected void doAfterPropertiesSet() throws Exception { + if (!isSetUserDetailsServiceInvoked) { + autoDetectAnyUserDetailsServiceAndUseIt(this.applicationContext); + } + Assert.notNull(this.userDetailsService, "A UserDetailsService must be set"); + } + + /** + * Introspects the Applicationcontext 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 AccessDeniedHandler + * is found, the method throws IllegalStateException. + * + * @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. null + * is a valid value, meaning the DaoAuthenticationProvider + * will present null to the relevant + * PasswordEncoder. + * + * @param saltSource to use when attempting to decode passwords via the + * PasswordEncoder + */ + 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; + } + } diff --git a/core/src/main/java/org/acegisecurity/ui/AbstractProcessingFilter.java b/core/src/main/java/org/acegisecurity/ui/AbstractProcessingFilter.java index aa9e26159e..f10ab92396 100644 --- a/core/src/main/java/org/acegisecurity/ui/AbstractProcessingFilter.java +++ b/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.

This filter is responsible for - * processing authentication requests. If authentication is successful, the resulting {@link Authentication} object - * will be placed into the SecurityContext, which is guaranteed to have already been created by an - * earlier filter.

- *

If authentication fails, the AuthenticationException will be placed into the - * HttpSession with the attribute defined by {@link #ACEGI_SECURITY_LAST_EXCEPTION_KEY}.

- *

To use this filter, it is necessary to specify the following properties:

- * - *

To configure this filter to redirect to specific pages as the result of specific {@link - * AuthenticationException}s you can do the following. Configure the exceptionMappings 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. + *

+ * This filter is responsible for processing authentication requests. If + * authentication is successful, the resulting {@link Authentication} object + * will be placed into the SecurityContext, which is guaranteed + * to have already been created by an earlier filter. + *

+ *

+ * If authentication fails, the AuthenticationException will be + * placed into the HttpSession with the attribute defined by + * {@link #ACEGI_SECURITY_LAST_EXCEPTION_KEY}. + *

+ *

+ * To use this filter, it is necessary to specify the following properties: + *

+ * + *

+ * To configure this filter to redirect to specific pages as the result of + * specific {@link AuthenticationException}s you can do the following. + * Configure the exceptionMappings 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: + * *

- *  <property name="exceptionMappings">
+ *  <property name="exceptionMappings">
  *    <props>
- *      <prop> key="org.acegisecurity.BadCredentialsException">/bad_credentials.jsp</prop>
+ *      <prop> key="org.acegisecurity.BadCredentialsException">/bad_credentials.jsp</prop>
  *    </props>
  *  </property>
  * 
- * The example above would redirect all {@link org.acegisecurity.BadCredentialsException}s thrown, to a page in the - * web-application called /bad_credentials.jsp.

- *

Any {@link AuthenticationException} thrown that cannot be matched in the exceptionMappings will - * be redirected to the authenticationFailureUrl

- *

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 AuthenticationManager-specific application event.

- * + * + * The example above would redirect all + * {@link org.acegisecurity.BadCredentialsException}s thrown, to a page in the + * web-application called /bad_credentials.jsp. + *

+ *

+ * Any {@link AuthenticationException} thrown that cannot be matched in the + * exceptionMappings will be redirected to the + * authenticationFailureUrl + *

+ *

+ * 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 AuthenticationManager-specific application event. + *

+ * * @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 - * null - */ - private String defaultTargetUrl; - - /** - * The URL destination that this filter intercepts and processes (usually something like - * /j_acegi_security_check) - */ - private String filterProcessesUrl = getDefaultFilterProcessesUrl(); - - /** - * If true, will always redirect to the value of {@link #getDefaultTargetUrl} upon successful - * authentication, irrespective of the page that caused the authentication request (defaults to false). - */ - 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 false. - */ - 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 HttpSession - * 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 filterProcessesUrl for the implementation. - * - * @return the default filterProcessesUrl - */ - public abstract String getDefaultFilterProcessesUrl(); - - /** - * Supplies the default target Url that will be used if no saved request is found or the - * alwaysUseDefaultTargetUrl 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 {} - - /** - *

Indicates whether this filter should attempt to process a login request for the current invocation.

- *

It strips any parameters from the "path" section of the request URL (such as the jsessionid - * parameter in http://host/myapp/index.html;jsessionid=blah) before matching against the - * filterProcessesUrl property.

- *

Subclasses may override for special requirements, such as Tapestry integration.

- * - * @param request as received from the filter chain - * @param response as received from the filter chain - * - * @return true if the filter should attempt authentication, false 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; - } - - protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, - AuthenticationException failed) throws IOException { - SecurityContextHolder.getContext().setAuthentication(null); + 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 null + */ + private String defaultTargetUrl; + + /** + * The URL destination that this filter intercepts and processes (usually + * something like /j_acegi_security_check) + */ + private String filterProcessesUrl = getDefaultFilterProcessesUrl(); + + /** + * If true, will always redirect to the value of + * {@link #getDefaultTargetUrl} upon successful authentication, irrespective + * of the page that caused the authentication request (defaults to + * false). + */ + 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 false. + */ + 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 HttpSession 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 Applicationcontext for the single instance + * of AuthenticationManager. If found invoke + * setAuthenticationManager method by providing the found instance of + * authenticationManager as a method parameter. If more than one instance of + * AuthenticationManager is found, the method throws + * IllegalStateException. + * + * @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 filterProcessesUrl for the + * implementation. + * + * @return the default filterProcessesUrl + */ + public abstract String getDefaultFilterProcessesUrl(); + + /** + * Supplies the default target Url that will be used if no saved request is + * found or the alwaysUseDefaultTargetUrl 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 { + } + + /** + *

+ * Indicates whether this filter should attempt to process a login request + * for the current invocation. + *

+ *

+ * It strips any parameters from the "path" section of the request URL (such + * as the jsessionid parameter in + * http://host/myapp/index.html;jsessionid=blah) before matching + * against the filterProcessesUrl property. + *

+ *

+ * Subclasses may override for special requirements, such as Tapestry + * integration. + *

+ * + * @param request as received from the filter chain + * @param response as received from the filter chain + * + * @return true if the filter should attempt authentication, + * false 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(); + } - if (logger.isDebugEnabled()) { - logger.debug("Updated SecurityContextHolder to contain null Authentication"); - } + return targetUrl; + } - String failureUrl = exceptionMappings.getProperty(failed.getClass().getName(), authenticationFailureUrl); + protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, + AuthenticationException failed) throws IOException { + SecurityContextHolder.getContext().setAuthentication(null); - if (logger.isDebugEnabled()) { - logger.debug("Authentication request failed: " + failed.toString()); - } + if (logger.isDebugEnabled()) { + logger.debug("Updated SecurityContextHolder to contain null Authentication"); + } - try { - request.getSession().setAttribute(ACEGI_SECURITY_LAST_EXCEPTION_KEY, failed); - } catch (Exception ignored) {} + String failureUrl = exceptionMappings.getProperty(failed.getClass().getName(), authenticationFailureUrl); - onUnsuccessfulAuthentication(request, response, failed); + if (logger.isDebugEnabled()) { + logger.debug("Authentication request failed: " + failed.toString()); + } - rememberMeServices.loginFail(request, response); + try { + request.getSession().setAttribute(ACEGI_SECURITY_LAST_EXCEPTION_KEY, failed); + } + catch (Exception ignored) { + } - sendRedirect(request, response, failureUrl); - } + onUnsuccessfulAuthentication(request, response, failed); - public AuthenticationDetailsSource getAuthenticationDetailsSource() { - // Required due to SEC-310 - return authenticationDetailsSource; - } + 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; + } + } diff --git a/core/src/main/java/org/acegisecurity/ui/ExceptionTranslationFilter.java b/core/src/main/java/org/acegisecurity/ui/ExceptionTranslationFilter.java index 35e6f870f0..26d97ef104 100644 --- a/core/src/main/java/org/acegisecurity/ui/ExceptionTranslationFilter.java +++ b/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 AccessDeniedException and AuthenticationException thrown within the filter - * chain.

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.

- *

If an {@link AuthenticationException} is detected, the filter will launch the - * authenticationEntryPoint. This allows common handling of authentication failures originating from any - * subclass of {@link org.acegisecurity.intercept.AbstractSecurityInterceptor}.

- *

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 authenticationEntryPoint 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}.

- *

To use this filter, it is necessary to specify the following properties:

- * - *

Do not use this class directly. Instead configure web.xml to use the {@link - * org.acegisecurity.util.FilterToBeanProxy}.

- * + * Handles any AccessDeniedException and + * AuthenticationException thrown within the filter chain. + *

+ * 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. + *

+ *

+ * If an {@link AuthenticationException} is detected, the filter will launch the + * authenticationEntryPoint. This allows common handling of + * authentication failures originating from any subclass of + * {@link org.acegisecurity.intercept.AbstractSecurityInterceptor}. + *

+ *

+ * 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 authenticationEntryPoint 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}. + *

+ *

+ * To use this filter, it is necessary to specify the following properties: + *

+ * + *

+ * Do not use this class directly. Instead configure + * web.xml to use the {@link + * org.acegisecurity.util.FilterToBeanProxy}. + *

+ * * @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 ===================================================================================== +public class ExceptionTranslationFilter implements Filter, InitializingBean, ApplicationContextAware { + // ~ Static fields/initializers + // ===================================================================================== - private static final Log logger = LogFactory.getLog(ExceptionTranslationFilter.class); + private static final Log logger = LogFactory.getLog(ExceptionTranslationFilter.class); - //~ Instance fields ================================================================================================ + // ~ Instance fields + // ================================================================================================ - private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl(); - private AuthenticationEntryPoint authenticationEntryPoint; - private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl(); - private PortResolver portResolver = new PortResolverImpl(); - private boolean createSessionAllowed = true; + private AccessDeniedHandler accessDeniedHandler; - //~ Methods ======================================================================================================== + private AuthenticationEntryPoint authenticationEntryPoint; - 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"); - } + private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl(); - public void destroy() {} + private PortResolver portResolver = new PortResolverImpl(); - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) - throws IOException, ServletException { - if (!(request instanceof HttpServletRequest)) { - throw new ServletException("HttpServletRequest required"); - } + private boolean createSessionAllowed = true; - if (!(response instanceof HttpServletResponse)) { - throw new ServletException("HttpServletResponse required"); - } + /* + * applicationContext will be inject as a part of the contract of + * ApplicationContextAware interface + */ + private ApplicationContext applicationContext; - try { - chain.doFilter(request, response); + /* + * boolean field to track if setter for accessDeniedHandler is invoked. If + * invoked the default value changes to true + */ + private boolean isSetAcessDeniedHandlerInvoked = false; - 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; - } - } + // ~ Methods + // ======================================================================================================== - public AuthenticationEntryPoint getAuthenticationEntryPoint() { - return authenticationEntryPoint; - } + 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 AuthenticationTrustResolver getAuthenticationTrustResolver() { - return authenticationTrustResolver; - } + // autodetect AccessDeniedHandler instance in the applicationcontext if + // it wasn't injected. + if (!isSetAcessDeniedHandlerInvoked) { + if (applicationContext != null) { + autoDetectAnyAccessDeniedHandlerAndUseIt(applicationContext); + } + } + } - public PortResolver getPortResolver() { - return portResolver; - } + /** + * Introspects the Applicationcontext 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 AccessDeniedHandler + * is found, the method throws IllegalStateException. + * + * @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(); + } - 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); - } + public void destroy() { + } - 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); - } + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, + ServletException { + if (!(request instanceof HttpServletRequest)) { + throw new ServletException("HttpServletRequest required"); + } - accessDeniedHandler.handle(request, response, (AccessDeniedException) exception); - } - } - } + if (!(response instanceof HttpServletResponse)) { + throw new ServletException("HttpServletResponse required"); + } - public void init(FilterConfig filterConfig) throws ServletException {} + try { + chain.doFilter(request, response); - /** - * If true, indicates that SecurityEnforcementFilter is permitted to store the - * target URL and exception information in the HttpSession (the default). In situations where you do - * not wish to unnecessarily create HttpSessions - because the user agent will know the failed URL, - * such as with BASIC or Digest authentication - you may wish to set this property to false. Remember - * to also set the {@link org.acegisecurity.context.HttpSessionContextIntegrationFilter#allowSessionCreation} to - * false if you set this property to false. - * - * @return true if the HttpSession will be used to store information about the failed - * request, false if the HttpSession will not be used - */ - public boolean isCreateSessionAllowed() { - return createSessionAllowed; - } + 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; + } + } - protected void sendStartAuthentication(ServletRequest request, ServletResponse response, FilterChain chain, - AuthenticationException reason) throws ServletException, IOException { - HttpServletRequest httpRequest = (HttpServletRequest) request; + public AuthenticationEntryPoint getAuthenticationEntryPoint() { + return authenticationEntryPoint; + } - SavedRequest savedRequest = new SavedRequest(httpRequest, portResolver); + public AuthenticationTrustResolver getAuthenticationTrustResolver() { + return authenticationTrustResolver; + } - if (logger.isDebugEnabled()) { - logger.debug("Authentication entry point being called; SavedRequest added to Session: " + savedRequest); - } + public PortResolver getPortResolver() { + return portResolver; + } - 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); - } + 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); + } - // SEC-112: Clear the SecurityContextHolder's Authentication, as the - // existing Authentication is no longer considered valid - SecurityContextHolder.getContext().setAuthentication(null); + 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); + } - authenticationEntryPoint.commence(httpRequest, (HttpServletResponse) response, reason); - } + 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); + } - public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) { - Assert.notNull(accessDeniedHandler, "AccessDeniedHandler required"); - this.accessDeniedHandler = accessDeniedHandler; - } + accessDeniedHandler.handle(request, response, (AccessDeniedException) exception); + } + } + } - public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) { - this.authenticationEntryPoint = authenticationEntryPoint; - } + public void init(FilterConfig filterConfig) throws ServletException { + } - public void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) { - this.authenticationTrustResolver = authenticationTrustResolver; - } + /** + * If true, indicates that + * SecurityEnforcementFilter is permitted to store the target + * URL and exception information in the HttpSession (the + * default). In situations where you do not wish to unnecessarily create + * HttpSessions - because the user agent will know the + * failed URL, such as with BASIC or Digest authentication - you may wish to + * set this property to false. Remember to also set the + * {@link org.acegisecurity.context.HttpSessionContextIntegrationFilter#allowSessionCreation} + * to false if you set this property to false. + * + * @return true if the HttpSession will be + * used to store information about the failed request, false + * if the HttpSession will not be used + */ + public boolean isCreateSessionAllowed() { + return createSessionAllowed; + } - public void setCreateSessionAllowed(boolean createSessionAllowed) { - this.createSessionAllowed = 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; + } - public void setPortResolver(PortResolver portResolver) { - this.portResolver = portResolver; - } } diff --git a/core/src/main/java/org/acegisecurity/ui/logout/LogoutFilter.java b/core/src/main/java/org/acegisecurity/ui/logout/LogoutFilter.java index e962a9e1bd..7d9c89f27d 100644 --- a/core/src/main/java/org/acegisecurity/ui/logout/LogoutFilter.java +++ b/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.

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 TokenBasedRememberMeServices and - * SecurityContextLogoutHandler (in that order).

- *

After logout, the URL specified by {@link #logoutSuccessUrl} will be shown.

- *

Do not use this class directly. Instead configure web.xml to use the {@link - * org.acegisecurity.util.FilterToBeanProxy}.

- * + * Logs a principal out. + *

+ * 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 TokenBasedRememberMeServices and + * SecurityContextLogoutHandler (in that order). + *

+ *

+ * After logout, the URL specified by {@link #logoutSuccessUrl} will be shown. + *

+ *

+ * Do not use this class directly. Instead configure + * web.xml to use the {@link + * org.acegisecurity.util.FilterToBeanProxy}. + *

+ * * @author Ben Alex * @version $Id$ */ -public class LogoutFilter implements Filter { - //~ Static fields/initializers ===================================================================================== +public class LogoutFilter implements Filter, ApplicationContextAware { + // ~ Static fields/initializers + // ===================================================================================== - private static final Log logger = LogFactory.getLog(LogoutFilter.class); + private static final Log logger = LogFactory.getLog(LogoutFilter.class); - //~ Instance fields ================================================================================================ + // ~ Instance fields + // ================================================================================================ - private String filterProcessesUrl = "/j_acegi_logout"; - private String logoutSuccessUrl; - private LogoutHandler[] handlers; + private String filterProcessesUrl = "/j_acegi_logout"; - //~ Constructors =================================================================================================== + private String logoutSuccessUrl; - public LogoutFilter(String logoutSuccessUrl, LogoutHandler[] handlers) { - Assert.hasText(logoutSuccessUrl, "LogoutSuccessUrl required"); - Assert.notEmpty(handlers, "LogoutHandlers are required"); - this.logoutSuccessUrl = logoutSuccessUrl; - this.handlers = handlers; - } + private LogoutHandler[] handlers; - //~ Methods ======================================================================================================== + private ApplicationContext applicationContext; - /** - * Not used. Use IoC container lifecycle methods instead. - */ - public void destroy() {} + // ~ Constructors + // =================================================================================================== - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) - throws IOException, ServletException { - if (!(request instanceof HttpServletRequest)) { - throw new ServletException("Can only process HttpServletRequest"); - } + public LogoutFilter(String logoutSuccessUrl, LogoutHandler[] handlers) { + Assert.hasText(logoutSuccessUrl, "LogoutSuccessUrl required"); + Assert.notEmpty(handlers, "LogoutHandlers are required"); + this.logoutSuccessUrl = logoutSuccessUrl; + this.handlers = handlers; + } - if (!(response instanceof HttpServletResponse)) { - throw new ServletException("Can only process HttpServletResponse"); - } + // ~ Methods + // ======================================================================================================== - HttpServletRequest httpRequest = (HttpServletRequest) request; - HttpServletResponse httpResponse = (HttpServletResponse) response; + /** + * Not used. Use IoC container lifecycle methods instead. + */ + public void destroy() { + } - if (requiresLogout(httpRequest, httpResponse)) { - Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, + ServletException { + if (!(request instanceof HttpServletRequest)) { + throw new ServletException("Can only process HttpServletRequest"); + } - if (logger.isDebugEnabled()) { - logger.debug("Logging out user '" + auth + "' and redirecting to logout page"); - } + if (!(response instanceof HttpServletResponse)) { + throw new ServletException("Can only process HttpServletResponse"); + } - for (int i = 0; i < handlers.length; i++) { - handlers[i].logout(httpRequest, httpResponse, auth); - } + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; - sendRedirect(httpRequest, httpResponse, logoutSuccessUrl); + if (requiresLogout(httpRequest, httpResponse)) { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - return; - } + if (logger.isDebugEnabled()) { + logger.debug("Logging out user '" + auth + "' and redirecting to logout page"); + } - chain.doFilter(request, response); - } + for (int i = 0; i < handlers.length; i++) { + handlers[i].logout(httpRequest, httpResponse, auth); + } - /** - * Not used. Use IoC container lifecycle methods instead. - * - * @param arg0 ignored - * - * @throws ServletException ignored - */ - public void init(FilterConfig arg0) throws ServletException {} + sendRedirect(httpRequest, httpResponse, logoutSuccessUrl); - /** - * Allow subclasses to modify when a logout should tak eplace. - * - * @param request the request - * @param response the response - * - * @return true if logout should occur, false otherwise - */ - protected boolean requiresLogout(HttpServletRequest request, HttpServletResponse response) { - String uri = request.getRequestURI(); - int pathParamIndex = uri.indexOf(';'); + return; + } - if (pathParamIndex > 0) { - // strip everything after the first semi-colon - uri = uri.substring(0, pathParamIndex); - } + chain.doFilter(request, response); + } - if ("".equals(request.getContextPath())) { - return uri.endsWith(filterProcessesUrl); - } - - return uri.endsWith(request.getContextPath() + filterProcessesUrl); - } + /** + * 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 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; - } + /** + * Allow subclasses to modify when a logout should tak eplace. + * + * @param request the request + * @param response the response + * + * @return true if logout should occur, false + * otherwise + */ + protected boolean requiresLogout(HttpServletRequest request, HttpServletResponse response) { + String uri = request.getRequestURI(); + int pathParamIndex = uri.indexOf(';'); - response.sendRedirect(response.encodeRedirectURL(url)); - } + 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; + } - public void setFilterProcessesUrl(String filterProcessesUrl) { - Assert.hasText(filterProcessesUrl, "FilterProcessesUrl required"); - this.filterProcessesUrl = filterProcessesUrl; - } } diff --git a/core/src/main/java/org/acegisecurity/ui/logout/SecurityContextLogoutHandler.java b/core/src/main/java/org/acegisecurity/ui/logout/SecurityContextLogoutHandler.java index d44398e2db..4af44800e9 100644 --- a/core/src/main/java/org/acegisecurity/ui/logout/SecurityContextLogoutHandler.java +++ b/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. diff --git a/core/src/main/java/org/acegisecurity/ui/rememberme/RememberMeProcessingFilter.java b/core/src/main/java/org/acegisecurity/ui/rememberme/RememberMeProcessingFilter.java index 0482e27ca4..3f4ba59162 100644 --- a/core/src/main/java/org/acegisecurity/ui/rememberme/RememberMeProcessingFilter.java +++ b/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 Applicationcontext for the single instance + * of AuthenticationManager. If found invoke + * setAuthenticationManager method by providing the found instance of + * authenticationManager as a method parameter. If more than one instance of + * AuthenticationManager is found, the method throws + * IllegalStateException. + * + * @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; + } + } diff --git a/core/src/main/java/org/acegisecurity/ui/rememberme/TokenBasedRememberMeServices.java b/core/src/main/java/org/acegisecurity/ui/rememberme/TokenBasedRememberMeServices.java index c81b90f8e8..c1a684bc15 100644 --- a/core/src/main/java/org/acegisecurity/ui/rememberme/TokenBasedRememberMeServices.java +++ b/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. - * + * *

- * 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.

- * - *

An {@link org.acegisecurity.userdetails.UserDetailsService} is required by this implementation, so that it - * can construct a valid Authentication 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.

- * + * 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. + *

+ * + *

+ * An {@link org.acegisecurity.userdetails.UserDetailsService} is required by + * this implementation, so that it can construct a valid + * Authentication 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. + *

+ * *

* The cookie encoded by this implementation adopts the following form: - *

username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)
+ * + *
+ * username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)
+ * 
+ * *

*

- * 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. *

*

- * 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. *

*

- * 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)}. *

- * + * * @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 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 ================================================================================================ + public static final String ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY = "ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE"; - 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; + public static final String DEFAULT_PARAMETER = "_acegi_security_remember_me"; - //~ Methods ======================================================================================================== + protected static final Log logger = LogFactory.getLog(TokenBasedRememberMeServices.class); - public void afterPropertiesSet() throws Exception { - Assert.hasLength(key); - Assert.hasLength(parameter); - Assert.hasLength(cookieName); - Assert.notNull(userDetailsService); - } + // ~ Instance fields + // ================================================================================================ - public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) { - Cookie[] cookies = request.getCookies(); + private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl(); - if ((cookies == null) || (cookies.length == 0)) { - return null; - } + private String key; - for (int i = 0; i < cookies.length; i++) { - if (cookieName.equals(cookies[i].getName())) { - String cookieValue = cookies[i].getValue(); + private String parameter = DEFAULT_PARAMETER; - 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"); - } + private UserDetailsService userDetailsService; - // 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, ":"); + private long tokenValiditySeconds = 1209600; // 14 days - if (cookieTokens.length == 3) { - long tokenExpiryTime; + private boolean alwaysRemember = false; - 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] + "')"); + private static final int DEFAULT_ORDER = Integer.MAX_VALUE; // ~ default - return null; - } + private int order = DEFAULT_ORDER; - // 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() + "')"); + private String cookieName = ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY; - return null; - } + private boolean isSetUserDetailsServiceInvoked = false; - // Check the user exists - // Defer lookup until after expiry time checked, to possibly avoid expensive lookup - UserDetails userDetails; + private ApplicationContext applicationContext; - try { - userDetails = this.userDetailsService.loadUserByUsername(cookieTokens[0]); - } catch (UsernameNotFoundException notFound) { - cancelCookie(request, response, - "Cookie token[0] contained username '" + cookieTokens[0] + "' but was not found"); + // ~ Methods + // ======================================================================================================== - return null; - } + 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 Applicationcontext 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 AccessDeniedHandler + * is found, the method throws IllegalStateException. + * + * @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()); + } + } + - // 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"); + public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) { + Cookie[] cookies = request.getCookies(); - return null; - } + if ((cookies == null) || (cookies.length == 0)) { + 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); + for (int i = 0; i < cookies.length; i++) { + if (cookieName.equals(cookies[i].getName())) { + String cookieValue = cookies[i].getValue(); - if (!expectedTokenSignature.equals(cookieTokens[2])) { - cancelCookie(request, response, - "Cookie token[2] contained signature '" + cookieTokens[2] + "' but expected '" - + expectedTokenSignature + "'"); + for (int j = 0; j < cookieValue.length() % 4; j++) { + cookieValue = cookieValue + "="; + } - return null; - } + if (Base64.isArrayByteBase64(cookieValue.getBytes())) { + if (logger.isDebugEnabled()) { + logger.debug("Remember-me cookie detected"); + } - // By this stage we have a valid token - if (logger.isDebugEnabled()) { - logger.debug("Remember-me cookie accepted"); - } + // 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, ":"); - RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.key, userDetails, - userDetails.getAuthorities()); - auth.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request)); + if (cookieTokens.length == 3) { + long tokenExpiryTime; - return auth; - } else { - cancelCookie(request, response, - "Cookie token did not contain 3 tokens; decoded value was '" + cookieAsPlainText + "'"); + 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; - } - } else { - cancelCookie(request, response, - "Cookie token was not Base64 encoded; value was '" + cookieValue + "'"); + return null; + } - 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; - } + return null; + } - private void cancelCookie(HttpServletRequest request, HttpServletResponse response, String reasonForLog) { - if ((reasonForLog != null) && logger.isDebugEnabled()) { - logger.debug("Cancelling cookie for reason: " + reasonForLog); - } + // Check the user exists + // Defer lookup until after expiry time checked, to + // possibly avoid expensive lookup + UserDetails userDetails; - response.addCookie(makeCancelCookie(request)); - } + try { + userDetails = this.userDetailsService.loadUserByUsername(cookieTokens[0]); + } + catch (UsernameNotFoundException notFound) { + cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0] + + "' but was not found"); - public String getKey() { - return key; - } + return null; + } - public String getParameter() { - return parameter; - } + // 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"); - public long getTokenValiditySeconds() { - return tokenValiditySeconds; - } + return null; + } - public UserDetailsService getUserDetailsService() { - return userDetailsService; - } + // 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); - public void loginFail(HttpServletRequest request, HttpServletResponse response) { - cancelCookie(request, response, "Interactive authentication attempt was unsuccessful"); - } + if (!expectedTokenSignature.equals(cookieTokens[2])) { + cancelCookie(request, response, "Cookie token[2] contained signature '" + cookieTokens[2] + + "' but expected '" + expectedTokenSignature + "'"); - protected boolean rememberMeRequested(HttpServletRequest request, String parameter) { - if (alwaysRemember) { - return true; - } + return null; + } - return RequestUtils.getBooleanParameter(request, parameter, false); - } + // By this stage we have a valid token + if (logger.isDebugEnabled()) { + logger.debug("Remember-me cookie accepted"); + } - 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 - + "')"); - } + RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.key, userDetails, + userDetails.getAuthorities()); + auth.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request)); - return; - } + return auth; + } + else { + cancelCookie(request, response, "Cookie token did not contain 3 tokens; decoded value was '" + + cookieAsPlainText + "'"); - // Determine username and password, ensuring empty strings - Assert.notNull(successfulAuthentication.getPrincipal()); - Assert.notNull(successfulAuthentication.getCredentials()); + return null; + } + } + else { + cancelCookie(request, response, "Cookie token was not Base64 encoded; value was '" + cookieValue + + "'"); - String username; - String password; + return null; + } + } + } - 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; - } + return null; + } - Assert.hasLength(username); - Assert.hasLength(password); + private void cancelCookie(HttpServletRequest request, HttpServletResponse response, String reasonForLog) { + if ((reasonForLog != null) && logger.isDebugEnabled()) { + logger.debug("Cancelling cookie for reason: " + reasonForLog); + } - long expiryTime = System.currentTimeMillis() + (tokenValiditySeconds * 1000); + response.addCookie(makeCancelCookie(request)); + } - // 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)); + public String getKey() { + return key; + } - if (logger.isDebugEnabled()) { - logger.debug("Added remember-me cookie for user '" + username - + "', expiry: '" + new Date(expiryTime) + "'"); - } - } + public String getParameter() { + return parameter; + } - public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { - cancelCookie(request, response, "Logout of user " - + (authentication == null ? "Unknown" : authentication.getName())); - } + public long getTokenValiditySeconds() { + return tokenValiditySeconds; + } - protected Cookie makeCancelCookie(HttpServletRequest request) { - Cookie cookie = new Cookie(cookieName, null); - cookie.setMaxAge(0); - cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/"); + public UserDetailsService getUserDetailsService() { + return userDetailsService; + } - return cookie; - } + public void loginFail(HttpServletRequest request, HttpServletResponse response) { + cancelCookie(request, response, "Interactive authentication attempt was unsuccessful"); + } - 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() : "/"); + protected boolean rememberMeRequested(HttpServletRequest request, String parameter) { + if (alwaysRemember) { + return true; + } - return cookie; - } + return RequestUtils.getBooleanParameter(request, parameter, false); + } - public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) { - Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required"); - this.authenticationDetailsSource = authenticationDetailsSource; - } + 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 + + "')"); + } - public void setKey(String key) { - this.key = key; - } + return; + } - public void setParameter(String parameter) { - this.parameter = parameter; - } + // Determine username and password, ensuring empty strings + Assert.notNull(successfulAuthentication.getPrincipal()); + Assert.notNull(successfulAuthentication.getCredentials()); - public void setCookieName(String cookieName) { + 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; + } + } diff --git a/core/src/main/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilterEntryPoint.java b/core/src/main/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilterEntryPoint.java index 554258d474..fd4e1fc7bb 100644 --- a/core/src/main/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilterEntryPoint.java +++ b/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; - /** - *

Used by the SecurityEnforcementFilter 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.

- *

By setting the forceHttps property to true, you may configure the class to force the protocol used - * for the login form to be HTTPS, even if the original intercepted request for a resource used the - * HTTP 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.

- * + *

+ * Used by the SecurityEnforcementFilter 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. + *

+ *

+ * By setting the forceHttps property to true, you may configure the + * class to force the protocol used for the login form to be HTTPS, + * even if the original intercepted request for a resource used the + * HTTP 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. + *

+ * * @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); + private static final Log logger = LogFactory.getLog(AuthenticationProcessingFilterEntryPoint.class); - //~ Instance fields ================================================================================================ + // ~ Instance fields + // ================================================================================================ - 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 PortMapper portMapper = new PortMapperImpl(); - //~ Methods ======================================================================================================== + private PortResolver portResolver = new PortResolverImpl(); + + private String loginFormUrl; + + private boolean forceHttps = false; + + 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()); + 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 includePort = true; + boolean inHttp = "http".equals(scheme.toLowerCase()); + boolean inHttps = "https".equals(scheme.toLowerCase()); - String redirectUrl = null; - boolean doForceHttps = false; - Integer httpsPort = null; + boolean includePort = true; - if (inHttp && (serverPort == 80)) { - includePort = false; - } else if (inHttps && (serverPort == 443)) { - includePort = false; - } + String redirectUrl = null; + boolean doForceHttps = false; + Integer httpsPort = null; - 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 (inHttp && (serverPort == 80)) { + includePort = false; + } + else if (inHttps && (serverPort == 443)) { + includePort = false; + } - 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(); + if (forceHttps && inHttp) { + httpsPort = (Integer) portMapper.lookupHttpsPort(new Integer(serverPort)); - redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath - + servletPath + (pathInfo == null ? "" : pathInfo ) + (query == null ? "" : "?"+query ); + if (httpsPort != null) { + doForceHttps = true; + if (httpsPort.intValue() == 443) { + includePort = false; + } + else { + includePort = true; + } + } - } else { + } - if (logger.isDebugEnabled()) { - logger.debug("Server side forward to: " + loginForm); - } + String loginForm = determineUrlToUseForThisRequest(req, resp, authException); - RequestDispatcher dispatcher = req.getRequestDispatcher(loginForm); + if (serverSideRedirect) { - dispatcher.forward(request, response); - - return; + if (doForceHttps) { - } + // before doing server side redirect, we need to do client + // redirect to https. - } else { + String servletPath = req.getServletPath(); + String pathInfo = req.getPathInfo(); + String query = req.getQueryString(); - if ( doForceHttps ) { + redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath + + servletPath + (pathInfo == null ? "" : pathInfo) + (query == null ? "" : "?" + query); - redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath - + loginForm; + } + else { - } else { + if (logger.isDebugEnabled()) { + logger.debug("Server side forward to: " + loginForm); + } - redirectUrl = scheme + "://" + serverName + ((includePort) ? (":" + serverPort) : "") + contextPath - + loginForm; + RequestDispatcher dispatcher = req.getRequestDispatcher(loginForm); - } - } + dispatcher.forward(request, response); - if (logger.isDebugEnabled()) { - logger.debug("Redirecting to: " + redirectUrl); - } + return; - ((HttpServletResponse) response).sendRedirect(((HttpServletResponse) response).encodeRedirectURL(redirectUrl)); - } + } - public boolean getForceHttps() { - return forceHttps; - } + } + else { - public String getLoginFormUrl() { - return loginFormUrl; - } + if (doForceHttps) { - public PortMapper getPortMapper() { - return portMapper; - } + redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath + + loginForm; - public PortResolver getPortResolver() { - return portResolver; - } + } + else { - public boolean isServerSideRedirect() { - return serverSideRedirect; - } + redirectUrl = scheme + "://" + serverName + ((includePort) ? (":" + serverPort) : "") + contextPath + + loginForm; - /** - * 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 - * https, then - * - * @param forceHttps - */ - public void setForceHttps(boolean forceHttps) { - this.forceHttps = forceHttps; - } + } + } - /** - * The URL where the AuthenticationProcessingFilter login page can be found. Should be - * relative to the web-app context path, and include a leading / - * - * @param loginFormUrl - */ - public void setLoginFormUrl(String loginFormUrl) { - this.loginFormUrl = loginFormUrl; - } + if (logger.isDebugEnabled()) { + logger.debug("Redirecting to: " + redirectUrl); + } - public void setPortMapper(PortMapper portMapper) { - this.portMapper = portMapper; - } + ((HttpServletResponse) response).sendRedirect(((HttpServletResponse) response).encodeRedirectURL(redirectUrl)); + } - public void setPortResolver(PortResolver portResolver) { - this.portResolver = portResolver; - } - - /** - * Tells if we are to do a server side include of the loginFormUrl instead of a 302 - * redirect. - * - * @param serverSideRedirect - */ - public void setServerSideRedirect(boolean serverSideRedirect) { - this.serverSideRedirect = serverSideRedirect; - } - + public boolean getForceHttps() { + return forceHttps; + } - public int getOrder() { + 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 + * https, then + * + * @param forceHttps + */ + public void setForceHttps(boolean forceHttps) { + this.forceHttps = forceHttps; + } + + /** + * The URL where the AuthenticationProcessingFilter login + * page can be found. Should be relative to the web-app context path, and + * include a leading / + * + * @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 + * loginFormUrl instead of a 302 redirect. + * + * @param serverSideRedirect + */ + public void setServerSideRedirect(boolean serverSideRedirect) { + this.serverSideRedirect = serverSideRedirect; + } + + public int getOrder() { return order; } diff --git a/core/src/test/java/org/acegisecurity/ui/ExceptionTranslationFilterTests.java b/core/src/test/java/org/acegisecurity/ui/ExceptionTranslationFilterTests.java index b4c2dc368b..3238e8b106 100644 --- a/core/src/test/java/org/acegisecurity/ui/ExceptionTranslationFilterTests.java +++ b/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 =================================================================================================== + // ~ Constructors + // =================================================================================================== - public ExceptionTranslationFilterTests() { - super(); - } + public ExceptionTranslationFilterTests() { + super(); + } - public ExceptionTranslationFilterTests(String arg0) { - super(arg0); - } + public ExceptionTranslationFilterTests(String arg0) { + super(arg0); + } - //~ Methods ======================================================================================================== + // ~ Methods + // ======================================================================================================== - public static void main(String[] args) { - junit.textui.TestRunner.run(ExceptionTranslationFilterTests.class); - } + public static void main(String[] args) { + junit.textui.TestRunner.run(ExceptionTranslationFilterTests.class); + } - public final void setUp() throws Exception { - super.setUp(); - } + public final void setUp() throws Exception { + super.setUp(); + } - protected void tearDown() throws Exception { - super.tearDown(); - SecurityContextHolder.clearContext(); - } + 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"); + 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 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")})); + // 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")); + // 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)); - } + 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"); + 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 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 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"); + // 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); + // 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()); - } + 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(); + 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()); - } - } + 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(); + 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()); - } - } + 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(); + public void testGettersSetters() { + ExceptionTranslationFilter filter = new ExceptionTranslationFilter(); - filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp")); - assertTrue(filter.getAuthenticationEntryPoint() != null); + filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp")); + assertTrue(filter.getAuthenticationEntryPoint() != null); - filter.setPortResolver(new MockPortResolver(80, 443)); - assertTrue(filter.getPortResolver() != 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"); + 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); + // 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(); + // 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)); + } - 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"); - 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); - // 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)); + } - // Test - ExceptionTranslationFilter filter = new ExceptionTranslationFilter(); - filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp")); - filter.setPortResolver(new MockPortResolver(8080, 8443)); - filter.afterPropertiesSet(); + public void testStartupDetectsMissingAuthenticationEntryPoint() throws Exception { + ExceptionTranslationFilter filter = new ExceptionTranslationFilter(); - 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)); - } + try { + filter.afterPropertiesSet(); + fail("Should have thrown IllegalArgumentException"); + } + catch (IllegalArgumentException expected) { + assertEquals("authenticationEntryPoint must be specified", expected.getMessage()); + } + } - public void testStartupDetectsMissingAuthenticationEntryPoint() - throws Exception { - ExceptionTranslationFilter filter = new ExceptionTranslationFilter(); + 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("authenticationEntryPoint must be specified", expected.getMessage()); - } - } + try { + filter.afterPropertiesSet(); + fail("Should have thrown IllegalArgumentException"); + } + catch (IllegalArgumentException expected) { + assertEquals("portResolver must be specified", expected.getMessage()); + } + } - public void testStartupDetectsMissingPortResolver() - throws Exception { - ExceptionTranslationFilter filter = new ExceptionTranslationFilter(); - filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp")); - filter.setPortResolver(null); + public void testSuccessfulAccessGrant() throws Exception { + // Setup our HTTP request + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setServletPath("/secure/page.html"); - try { - filter.afterPropertiesSet(); - fail("Should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - assertEquals("portResolver must be specified", expected.getMessage()); - } - } + // Setup the FilterChain to thrown no exceptions + MockFilterChain chain = new MockFilterChain(false, false, false, false); - public void testSuccessfulAccessGrant() throws Exception { - // Setup our HTTP request - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setServletPath("/secure/page.html"); + // Test + ExceptionTranslationFilter filter = new ExceptionTranslationFilter(); + filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp")); - // Setup the FilterChain to thrown no exceptions - MockFilterChain chain = new MockFilterChain(false, false, false, false); + MockHttpServletResponse response = new MockHttpServletResponse(); + filter.doFilter(request, response, chain); + } - // Test - ExceptionTranslationFilter filter = new ExceptionTranslationFilter(); - filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp")); + public void testSuccessfulStartupAndShutdownDown() throws Exception { + ExceptionTranslationFilter filter = new ExceptionTranslationFilter(); - MockHttpServletResponse response = new MockHttpServletResponse(); - filter.doFilter(request, response, chain); - } + filter.init(null); + filter.destroy(); + assertTrue(true); + } - public void testSuccessfulStartupAndShutdownDown() - throws Exception { - ExceptionTranslationFilter filter = new ExceptionTranslationFilter(); + public void testThrowIOException() throws Exception { + ExceptionTranslationFilter filter = new ExceptionTranslationFilter(); - filter.init(null); - filter.destroy(); - assertTrue(true); - } + 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 testThrowIOException() throws Exception { - ExceptionTranslationFilter filter = new ExceptionTranslationFilter(); + public void testThrowServletException() throws Exception { + ExceptionTranslationFilter filter = new ExceptionTranslationFilter(); - filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("")); + 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()); + } + } - filter.afterPropertiesSet(); + // ~ Inner Classes + // ================================================================================================== - 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()); - } - } + private class MockFilterChain implements FilterChain { + private boolean throwAccessDenied; - public void testThrowServletException() throws Exception { - ExceptionTranslationFilter filter = new ExceptionTranslationFilter(); + private boolean throwAuthenticationFailure; - filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("")); + private boolean throwIOException; - filter.afterPropertiesSet(); + private boolean throwServletException; - 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()); - } - } + public MockFilterChain(boolean throwAccessDenied, boolean throwAuthenticationFailure, + boolean throwServletException, boolean throwIOException) { + this.throwAccessDenied = throwAccessDenied; + this.throwAuthenticationFailure = throwAuthenticationFailure; + this.throwServletException = throwServletException; + this.throwIOException = throwIOException; + } - //~ Inner Classes ================================================================================================== + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + if (throwAccessDenied) { + throw new AccessDeniedException("As requested"); + } - private class MockFilterChain implements FilterChain { - private boolean throwAccessDenied; - private boolean throwAuthenticationFailure; - private boolean throwIOException; - private boolean throwServletException; + if (throwAuthenticationFailure) { + throw new BadCredentialsException("As requested"); + } - public MockFilterChain(boolean throwAccessDenied, boolean throwAuthenticationFailure, - boolean throwServletException, boolean throwIOException) { - this.throwAccessDenied = throwAccessDenied; - this.throwAuthenticationFailure = throwAuthenticationFailure; - this.throwServletException = throwServletException; - this.throwIOException = throwIOException; - } + if (throwServletException) { + throw new ServletException("As requested"); + } - 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"); - } - } - } + if (throwIOException) { + throw new IOException("As requested"); + } + } + } } diff --git a/core/src/test/java/org/acegisecurity/ui/rememberme/TokenBasedRememberMeServicesTests.java b/core/src/test/java/org/acegisecurity/ui/rememberme/TokenBasedRememberMeServicesTests.java index 92f080c402..05734f8fea 100644 --- a/core/src/test/java/org/acegisecurity/ui/rememberme/TokenBasedRememberMeServicesTests.java +++ b/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")); diff --git a/samples/annotations/pom.xml b/samples/annotations/pom.xml index a17d2ca1e7..03147b3e84 100644 --- a/samples/annotations/pom.xml +++ b/samples/annotations/pom.xml @@ -6,7 +6,7 @@ org.acegisecurity acegi-security-samples - 1.0.4-SNAPSHOT + 1.0.5-SNAPSHOT acegi-security-sample-annotations Acegi Security System for Spring - Annotations sample diff --git a/sandbox/pom.xml b/sandbox/pom.xml index 3cf4a2763c..90ce2931e9 100644 --- a/sandbox/pom.xml +++ b/sandbox/pom.xml @@ -4,7 +4,7 @@ org.acegisecurity acegi-security-parent - 1.0.4-SNAPSHOT + 1.0.5-SNAPSHOT acegi-security-sandbox Acegi Security System for Spring - Sandbox @@ -27,7 +27,7 @@ org.acegisecurity acegi-security - 1.1-SNAPSHOT + 1.0.5-SNAPSHOT - \ No newline at end of file + diff --git a/sandbox/spring-security-config/.classpath b/sandbox/spring-security-config/.classpath index 19af352e04..c078c92989 100644 --- a/sandbox/spring-security-config/.classpath +++ b/sandbox/spring-security-config/.classpath @@ -5,39 +5,40 @@ - - - - - - - - + + - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + diff --git a/sandbox/spring-security-config/.project b/sandbox/spring-security-config/.project index 929ae1551c..1e65037c34 100644 --- a/sandbox/spring-security-config/.project +++ b/sandbox/spring-security-config/.project @@ -9,8 +9,12 @@ org.eclipse.wst.validation.validationbuilder + + org.springframework.ide.eclipse.core.springbuilder + + org.springframework.ide.eclipse.core.springnature org.eclipse.wst.common.project.facet.core.nature org.eclipse.jdt.core.javanature org.eclipse.wst.common.modulecore.ModuleCoreNature diff --git a/sandbox/spring-security-config/.settings/org.eclipse.jdt.core.prefs b/sandbox/spring-security-config/.settings/org.eclipse.jdt.core.prefs index 7782715f60..9af6b3d081 100644 --- a/sandbox/spring-security-config/.settings/org.eclipse.jdt.core.prefs +++ b/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 diff --git a/sandbox/spring-security-config/pom.xml b/sandbox/spring-security-config/pom.xml index 0e7e9f5ac4..d7f6f39b4c 100644 --- a/sandbox/spring-security-config/pom.xml +++ b/sandbox/spring-security-config/pom.xml @@ -11,6 +11,11 @@ 2.0.0-M1 http://maven.apache.org + + org.acegisecurity + acegi-security-tiger + 1.0.5-SNAPSHOT + org.springframework spring-remoting diff --git a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/AuthenticationMechanismBeanDefinitionParser.java b/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/AuthenticationMechanismBeanDefinitionParser.java index 17f3546bc6..13a68ed377 100644 --- a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/AuthenticationMechanismBeanDefinitionParser.java +++ b/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; } } diff --git a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/AuthenticationProcessingFilterBeanDefinitionParser.java b/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/AuthenticationProcessingFilterBeanDefinitionParser.java index b73da18824..7425685ffe 100644 --- a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/AuthenticationProcessingFilterBeanDefinitionParser.java +++ b/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; } diff --git a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/AutoConfigBeanDefinitionParser.java b/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/AutoConfigBeanDefinitionParser.java index 230fe5f46c..3a108a6b8f 100644 --- a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/AutoConfigBeanDefinitionParser.java +++ b/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 FilterSecurityInterceptor bean definition and + * register it with the ParserContext + * + * @param parserContext To register the bean definition with + * @param authenticationManager The AuthenticationManager 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 BeanDefinition 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(); + } } diff --git a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/ContextIntegrationBeanDefinitionParser.java b/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/ContextIntegrationBeanDefinitionParser.java index a00a56d17c..3134cf2642 100644 --- a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/ContextIntegrationBeanDefinitionParser.java +++ b/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; diff --git a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/ExceptionTranslationFilterBeanDefinitionParser.java b/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/ExceptionTranslationFilterBeanDefinitionParser.java index 053844d871..8f2730f9e4 100644 --- a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/ExceptionTranslationFilterBeanDefinitionParser.java +++ b/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; *

* * @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 BeanDefintion for + * ExceptionTranslationFilter 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 BeanDefintion for + * AuthenticationProcessingFilterEntryPoint 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; + } } diff --git a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/LogoutFilterBeanDefinitionParser.java b/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/LogoutFilterBeanDefinitionParser.java index 678eb0786b..f8f709529d 100644 --- a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/LogoutFilterBeanDefinitionParser.java +++ b/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); diff --git a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/RememberMeFilterBeanDefinitionParser.java b/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/RememberMeFilterBeanDefinitionParser.java index e18753386d..68027a0a1f 100644 --- a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/RememberMeFilterBeanDefinitionParser.java +++ b/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; + } } diff --git a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/RememberMeServicesBeanDefinitionParser.java b/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/RememberMeServicesBeanDefinitionParser.java index 1bae3af80a..879a5f0b90 100644 --- a/sandbox/spring-security-config/src/main/java/org/acegisecurity/config/RememberMeServicesBeanDefinitionParser.java +++ b/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; } } diff --git a/sandbox/spring-security-config/src/test/java/org/acegisecurity/config/AutoConfigBeanDefinitionParserTests.java b/sandbox/spring-security-config/src/test/java/org/acegisecurity/config/AutoConfigBeanDefinitionParserTests.java index d579f8d95d..165cc3d45d 100644 --- a/sandbox/spring-security-config/src/test/java/org/acegisecurity/config/AutoConfigBeanDefinitionParserTests.java +++ b/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; } + } diff --git a/sandbox/spring-security-config/src/test/java/org/acegisecurity/config/RememberMeBeanDefinitionParserTest.java b/sandbox/spring-security-config/src/test/java/org/acegisecurity/config/RememberMeBeanDefinitionParserTest.java index 81c08e3ba9..e33168a202 100644 --- a/sandbox/spring-security-config/src/test/java/org/acegisecurity/config/RememberMeBeanDefinitionParserTest.java +++ b/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); } - } diff --git a/sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/applicationContext-acegi-security.xml b/sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/applicationContext-acegi-security.xml index a213108c15..1853072345 100644 --- a/sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/applicationContext-acegi-security.xml +++ b/sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/applicationContext-acegi-security.xml @@ -128,9 +128,9 @@ 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 + diff --git a/sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/auto-config.xml b/sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/auto-config.xml index 31bac8103b..cd932b60b4 100644 --- a/sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/auto-config.xml +++ b/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 - - + + + + diff --git a/sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/user.properties b/sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/user.properties index cd5f8e8b31..41be7a5728 100644 --- a/sandbox/spring-security-config/src/test/resources/org/acegisecurity/config/user.properties +++ b/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