From b2c30277f42a952a7c4208759ad992489c9427c6 Mon Sep 17 00:00:00 2001
From: Vishal Puri This filter is responsible for
- * processing authentication requests. If authentication is successful, the resulting {@link Authentication} object
- * will be placed into the If authentication fails, the 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
+ * This filter is responsible for processing authentication requests. If
+ * authentication is successful, the resulting {@link Authentication} object
+ * will be placed into the
+ * If authentication fails, the
+ * 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 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.SecurityContext, which is guaranteed to have already been created by an
- * earlier filter.AuthenticationException will be placed into the
- * HttpSession with the attribute defined by {@link #ACEGI_SECURITY_LAST_EXCEPTION_KEY}.
- *
- * defaultTargetUrl indicates the URL that should be used for redirection if the
- * HttpSession attribute named {@link #ACEGI_SAVED_REQUEST_KEY} does not indicate the target URL once
- * authentication is completed successfully. eg: /. The defaultTargetUrl will be treated
- * as relative to the web-app's context path, and should include the leading /. Alternatively,
- * inclusion of a scheme name (eg http:// or https://) as the prefix will denote a fully-qualified URL and this is
- * also supported.authenticationFailureUrl indicates the URL that should be used for redirection if the
- * authentication request fails. eg: /login.jsp?login_error=1.filterProcessesUrl indicates the URL that this filter will respond to. This parameter
- * varies by subclass.alwaysUseDefaultTargetUrl causes successful authentication to always redirect to the
- * defaultTargetUrl, even if the HttpSession attribute named {@link
- * #ACEGI_SAVED_REQUEST_KEY} defines the intended target URL.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.
+ * SecurityContext, which is guaranteed
+ * to have already been created by an earlier filter.
+ * AuthenticationException will be
+ * placed into the HttpSession with the attribute defined by
+ * {@link #ACEGI_SECURITY_LAST_EXCEPTION_KEY}.
+ *
+ *
+ * defaultTargetUrl indicates the URL that should be used
+ * for redirection if the HttpSession attribute named
+ * {@link #ACEGI_SAVED_REQUEST_KEY} does not indicate the target URL once
+ * authentication is completed successfully. eg: /. The
+ * defaultTargetUrl will be treated as relative to the web-app's
+ * context path, and should include the leading /.
+ * Alternatively, inclusion of a scheme name (eg http:// or https://) as the
+ * prefix will denote a fully-qualified URL and this is also supported.authenticationFailureUrl indicates the URL that should be
+ * used for redirection if the authentication request fails. eg:
+ * /login.jsp?login_error=1.filterProcessesUrl indicates the URL that this filter
+ * will respond to. This parameter varies by subclass.alwaysUseDefaultTargetUrl causes successful
+ * authentication to always redirect to the defaultTargetUrl,
+ * even if the HttpSession attribute named {@link
+ * #ACEGI_SAVED_REQUEST_KEY} defines the intended target URL.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.
+ * 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.
+ *
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 - * - * @returntrue 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 + * + * @returntrue 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:
- *authenticationEntryPoint indicates the handler that should commence the authentication
- * process if an AuthenticationException is detected. Note that this may also switch the current
- * protocol from http to https for an SSL login.portResolver is used to determine the "real" port that a request was received on.Do not use this class directly. Instead configure web.xml to use the {@link
- * org.acegisecurity.util.FilterToBeanProxy}.
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: + *
+ *authenticationEntryPoint indicates the handler that
+ * should commence the authentication process if an
+ * AuthenticationException is detected. Note that this may also
+ * switch the current protocol from http to https for an SSL login.portResolver is used to determine the "real" port that a
+ * request was received on.
+ * Do not use this class directly. Instead configure
+ * web.xml to use the {@link
+ * org.acegisecurity.util.FilterToBeanProxy}.
+ *
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}.
+ * 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}.
+ *
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.
+ * 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 theApplicationcontext 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.
+ *
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 @@
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 @@