mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-30 22:28:46 +00:00 
			
		
		
		
	Anonymous principal support. As requested by the community at various times, including in http://forum.springframework.org/viewtopic.php?t=1925.
This commit is contained in:
		
							parent
							
								
									3c4faf58c7
								
							
						
					
					
						commit
						693ac5a24a
					
				| @ -1,4 +1,4 @@ | ||||
| /* Copyright 2004 Acegi Technology Pty Limited | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @ -32,10 +32,14 @@ public interface AccessDecisionManager { | ||||
|      * @param config the configuration attributes associated with the secured | ||||
|      *        object being invoked | ||||
|      * | ||||
|      * @throws AccessDeniedException if access is denied | ||||
|      * @throws AccessDeniedException if access is denied as the authentication | ||||
|      *         does not hold a required authority or ACL privilege | ||||
|      * @throws InsufficientAuthenticationException if access is denied as the | ||||
|      *         authentication does not provide a sufficient level of trust | ||||
|      */ | ||||
|     public void decide(Authentication authentication, Object object, | ||||
|         ConfigAttributeDefinition config) throws AccessDeniedException; | ||||
|         ConfigAttributeDefinition config) | ||||
|         throws AccessDeniedException, InsufficientAuthenticationException; | ||||
| 
 | ||||
|     /** | ||||
|      * Indicates whether this <code>AccessDecisionManager</code> is able to | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| /* Copyright 2004 Acegi Technology Pty Limited | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @ -16,9 +16,8 @@ | ||||
| package net.sf.acegisecurity; | ||||
| 
 | ||||
| /** | ||||
|  * Abstract superclass for all exceptions related to the {@link | ||||
|  * AuthenticationManager} being unable to authenticate an {@link | ||||
|  * Authentication} object. | ||||
|  * Abstract superclass for all exceptions related an {@link Authentication} | ||||
|  * object being invalid for whatever reason. | ||||
|  * | ||||
|  * @author Ben Alex | ||||
|  * @version $Id$ | ||||
| @ -57,11 +56,11 @@ public abstract class AuthenticationException extends AcegiSecurityException { | ||||
| 
 | ||||
|     //~ Methods ================================================================ | ||||
| 
 | ||||
|     void setAuthentication(Authentication authentication) { | ||||
|         this.authentication = authentication; | ||||
|     } | ||||
| 
 | ||||
|     public Authentication getAuthentication() { | ||||
|         return authentication; | ||||
|     } | ||||
| 
 | ||||
|     void setAuthentication(Authentication authentication) { | ||||
|         this.authentication = authentication; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,66 @@ | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package net.sf.acegisecurity; | ||||
| 
 | ||||
| /** | ||||
|  * Evaluates <code>Authentication</code> tokens | ||||
|  * | ||||
|  * @author Ben Alex | ||||
|  * @version $Id$ | ||||
|  */ | ||||
| public interface AuthenticationTrustResolver { | ||||
|     //~ Methods ================================================================ | ||||
| 
 | ||||
|     /** | ||||
|      * Indicates whether the passed <code>Authentication</code> token | ||||
|      * represents an anonymous user. Typically the framework will call this | ||||
|      * method if it is trying to decide whether an | ||||
|      * <code>AccessDeniedException</code> should result in a final rejection | ||||
|      * (ie as would be the case if the principal was non-anonymous/fully | ||||
|      * authenticated) or direct the principal to attempt actual authentication | ||||
|      * (ie as would be the case if the <code>Authentication</code> was merely | ||||
|      * anonymous). | ||||
|      * | ||||
|      * @param authentication to test (may be <code>null</code> in which case | ||||
|      *        the method will always return <code>false</code>) | ||||
|      * | ||||
|      * @return <code>true</code> the passed authentication token represented an | ||||
|      *         anonymous principal, <code>false</code> otherwise | ||||
|      */ | ||||
|     public boolean isAnonymous(Authentication authentication); | ||||
| 
 | ||||
|     /** | ||||
|      * Indicates whether the passed <code>Authentication</code> token | ||||
|      * represents user that has been remembered (ie not a user that has been | ||||
|      * fully authenticated). | ||||
|      *  | ||||
|      * <p> | ||||
|      * <b>No part of the framework uses this method</b>, as it is a weak | ||||
|      * definition of trust levels. The method is provided simply to assist | ||||
|      * with custom <code>AccessDecisionVoter</code>s and the like that you | ||||
|      * might develop. Of course, you don't need to use this method either and | ||||
|      * can develop your own "trust level" hierarchy instead. | ||||
|      * </p> | ||||
|      * | ||||
|      * @param authentication to test (may be <code>null</code> in which case | ||||
|      *        the method will always return <code>false</code>) | ||||
|      * | ||||
|      * @return <code>true</code> the passed authentication token represented a | ||||
|      *         principal authenticated using a remember-me token, | ||||
|      *         <code>false</code> otherwise | ||||
|      */ | ||||
|     public boolean isRememberMe(Authentication authentication); | ||||
| } | ||||
| @ -0,0 +1,77 @@ | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package net.sf.acegisecurity; | ||||
| 
 | ||||
| import net.sf.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Basic implementation of {@link AuthenticationTrustResolverImpl}. | ||||
|  *  | ||||
|  * <P> | ||||
|  * Makes trust decisions based on whether the passed | ||||
|  * <code>Authentication</code> is an instance of a defined class. | ||||
|  * </p> | ||||
|  *  | ||||
|  * <p> | ||||
|  * If {@link #anonymousClass} or {@link #rememberMeClass} is <code>null</code>, | ||||
|  * the corresponding method will always return <code>false</code>. | ||||
|  * </p> | ||||
|  * | ||||
|  * @author Ben Alex | ||||
|  * @version $Id$ | ||||
|  */ | ||||
| public class AuthenticationTrustResolverImpl | ||||
|     implements AuthenticationTrustResolver { | ||||
|     //~ Instance fields ======================================================== | ||||
| 
 | ||||
|     private Class anonymousClass = AnonymousAuthenticationToken.class; | ||||
|     private Class rememberMeClass; | ||||
| 
 | ||||
|     //~ Methods ================================================================ | ||||
| 
 | ||||
|     public boolean isAnonymous(Authentication authentication) { | ||||
|         if ((anonymousClass == null) || (authentication == null)) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return anonymousClass.isAssignableFrom(authentication.getClass()); | ||||
|     } | ||||
| 
 | ||||
|     public void setAnonymousClass(Class anonymousClass) { | ||||
|         this.anonymousClass = anonymousClass; | ||||
|     } | ||||
| 
 | ||||
|     public Class getAnonymousClass() { | ||||
|         return anonymousClass; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isRememberMe(Authentication authentication) { | ||||
|         if ((rememberMeClass == null) || (authentication == null)) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return rememberMeClass.isAssignableFrom(authentication.getClass()); | ||||
|     } | ||||
| 
 | ||||
|     public void setRememberMeClass(Class rememberMeClass) { | ||||
|         this.rememberMeClass = rememberMeClass; | ||||
|     } | ||||
| 
 | ||||
|     public Class getRememberMeClass() { | ||||
|         return rememberMeClass; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,58 @@ | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package net.sf.acegisecurity; | ||||
| 
 | ||||
| /** | ||||
|  * Thrown if an authentication request is rejected because the credentials are | ||||
|  * not sufficiently trusted. | ||||
|  *  | ||||
|  * <p> | ||||
|  * {{@link net.sf.acegisecurity.vote.AccessDecisionVoter}s will typically throw | ||||
|  * this exception if they are dissatisfied with the level of the | ||||
|  * authentication, such as if performed using a remember-me mechnanism or | ||||
|  * anonymously. The commonly used {@link | ||||
|  * net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter} will thus | ||||
|  * cause the <code>AuthenticationEntryPoint</code> to be called, allowing the | ||||
|  * principal to authenticate with a stronger level of authentication. } | ||||
|  * </p> | ||||
|  * | ||||
|  * @author Ben Alex | ||||
|  * @version $Id$ | ||||
|  */ | ||||
| public class InsufficientAuthenticationException extends AuthenticationException { | ||||
|     //~ Constructors =========================================================== | ||||
| 
 | ||||
|     /** | ||||
|      * Constructs an <code>InsufficientAuthenticationException</code> with the | ||||
|      * specified message. | ||||
|      * | ||||
|      * @param msg the detail message | ||||
|      */ | ||||
|     public InsufficientAuthenticationException(String msg) { | ||||
|         super(msg); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Constructs an <code>InsufficientAuthenticationException</code> with the | ||||
|      * specified message and root cause. | ||||
|      * | ||||
|      * @param msg the detail message | ||||
|      * @param t root cause | ||||
|      */ | ||||
|     public InsufficientAuthenticationException(String msg, Throwable t) { | ||||
|         super(msg, t); | ||||
|     } | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| /* Copyright 2004 Acegi Technology Pty Limited | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @ -17,6 +17,9 @@ package net.sf.acegisecurity.intercept.web; | ||||
| 
 | ||||
| import net.sf.acegisecurity.AccessDeniedException; | ||||
| import net.sf.acegisecurity.AuthenticationException; | ||||
| import net.sf.acegisecurity.AuthenticationTrustResolver; | ||||
| import net.sf.acegisecurity.AuthenticationTrustResolverImpl; | ||||
| import net.sf.acegisecurity.context.security.SecureContextUtils; | ||||
| import net.sf.acegisecurity.ui.AbstractProcessingFilter; | ||||
| import net.sf.acegisecurity.util.PortResolver; | ||||
| import net.sf.acegisecurity.util.PortResolverImpl; | ||||
| @ -26,6 +29,8 @@ import org.apache.commons.logging.LogFactory; | ||||
| 
 | ||||
| import org.springframework.beans.factory.InitializingBean; | ||||
| 
 | ||||
| import org.springframework.util.Assert; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| import javax.servlet.Filter; | ||||
| @ -54,10 +59,13 @@ import javax.servlet.http.HttpServletResponse; | ||||
|  * </p> | ||||
|  *  | ||||
|  * <p> | ||||
|  * If an {@link AccessDeniedException} is detected, the filter will respond | ||||
|  * with a <code>HttpServletResponse.SC_FORBIDDEN</code> (403 error).  In | ||||
|  * addition, the <code>AccessDeniedException</code> itself will be placed in | ||||
|  * the <code>HttpSession</code> attribute keyed against {@link | ||||
|  * If an {@link AccessDeniedException} is detected, the filter will determine | ||||
|  * whether or not the user is an anonymous user. If they are an anonymous | ||||
|  * user, the <code>authenticationEntryPoint</code> will be launched. If they | ||||
|  * are not an anonymous user, the filter will respond with a | ||||
|  * <code>HttpServletResponse.SC_FORBIDDEN</code> (403 error).  In addition, | ||||
|  * the <code>AccessDeniedException</code> itself will be placed in the | ||||
|  * <code>HttpSession</code> attribute keyed against {@link | ||||
|  * #ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY} (to allow access to the stack | ||||
|  * trace etc). Again, this allows common access denied handling irrespective | ||||
|  * of the originating security interceptor. | ||||
| @ -104,6 +112,7 @@ public class SecurityEnforcementFilter implements Filter, InitializingBean { | ||||
|     //~ Instance fields ======================================================== | ||||
| 
 | ||||
|     private AuthenticationEntryPoint authenticationEntryPoint; | ||||
|     private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl(); | ||||
|     private FilterSecurityInterceptor filterSecurityInterceptor; | ||||
|     private PortResolver portResolver = new PortResolverImpl(); | ||||
| 
 | ||||
| @ -118,6 +127,15 @@ public class SecurityEnforcementFilter implements Filter, InitializingBean { | ||||
|         return authenticationEntryPoint; | ||||
|     } | ||||
| 
 | ||||
|     public void setAuthenticationTrustResolver( | ||||
|         AuthenticationTrustResolver authenticationTrustResolver) { | ||||
|         this.authenticationTrustResolver = authenticationTrustResolver; | ||||
|     } | ||||
| 
 | ||||
|     public AuthenticationTrustResolver getAuthenticationTrustResolver() { | ||||
|         return authenticationTrustResolver; | ||||
|     } | ||||
| 
 | ||||
|     public void setFilterSecurityInterceptor( | ||||
|         FilterSecurityInterceptor filterSecurityInterceptor) { | ||||
|         this.filterSecurityInterceptor = filterSecurityInterceptor; | ||||
| @ -136,19 +154,13 @@ public class SecurityEnforcementFilter implements Filter, InitializingBean { | ||||
|     } | ||||
| 
 | ||||
|     public void afterPropertiesSet() throws Exception { | ||||
|         if (authenticationEntryPoint == null) { | ||||
|             throw new IllegalArgumentException( | ||||
|                 "authenticationEntryPoint must be specified"); | ||||
|         } | ||||
| 
 | ||||
|         if (filterSecurityInterceptor == null) { | ||||
|             throw new IllegalArgumentException( | ||||
|                 "filterSecurityInterceptor must be specified"); | ||||
|         } | ||||
| 
 | ||||
|         if (portResolver == null) { | ||||
|             throw new IllegalArgumentException("portResolver must be specified"); | ||||
|         } | ||||
|         Assert.notNull(authenticationEntryPoint, | ||||
|             "authenticationEntryPoint must be specified"); | ||||
|         Assert.notNull(filterSecurityInterceptor, | ||||
|             "filterSecurityInterceptor must be specified"); | ||||
|         Assert.notNull(portResolver, "portResolver must be specified"); | ||||
|         Assert.notNull(authenticationTrustResolver, | ||||
|             "authenticationTrustResolver must be specified"); | ||||
|     } | ||||
| 
 | ||||
|     public void destroy() {} | ||||
| @ -172,43 +184,29 @@ public class SecurityEnforcementFilter implements Filter, InitializingBean { | ||||
|                 logger.debug("Chain processed normally"); | ||||
|             } | ||||
|         } catch (AuthenticationException authentication) { | ||||
|             HttpServletRequest httpRequest = (HttpServletRequest) request; | ||||
| 
 | ||||
|             int port = portResolver.getServerPort(request); | ||||
|             boolean includePort = true; | ||||
| 
 | ||||
|             if ("http".equals(request.getScheme().toLowerCase()) | ||||
|                 && (port == 80)) { | ||||
|                 includePort = false; | ||||
|             } | ||||
| 
 | ||||
|             if ("https".equals(request.getScheme().toLowerCase()) | ||||
|                 && (port == 443)) { | ||||
|                 includePort = false; | ||||
|             } | ||||
| 
 | ||||
|             String targetUrl = request.getScheme() + "://" | ||||
|                 + request.getServerName() + ((includePort) ? (":" + port) : "") | ||||
|                 + httpRequest.getContextPath() + fi.getRequestUrl(); | ||||
| 
 | ||||
|             if (logger.isDebugEnabled()) { | ||||
|                 logger.debug( | ||||
|                     "Authentication failed - adding target URL to Session: " | ||||
|                     + targetUrl, authentication); | ||||
|                 logger.debug("Authentication exception occurred; redirecting to authentication entry point", | ||||
|                     authentication); | ||||
|             } | ||||
| 
 | ||||
|             ((HttpServletRequest) request).getSession().setAttribute(AbstractProcessingFilter.ACEGI_SECURITY_TARGET_URL_KEY, | ||||
|                 targetUrl); | ||||
|             authenticationEntryPoint.commence(request, response, authentication); | ||||
|             sendStartAuthentication(fi, authentication); | ||||
|         } catch (AccessDeniedException accessDenied) { | ||||
|             if (logger.isDebugEnabled()) { | ||||
|                 logger.debug( | ||||
|                     "Access is denied - sending back forbidden response"); | ||||
|             } | ||||
|             if (authenticationTrustResolver.isAnonymous( | ||||
|                     SecureContextUtils.getSecureContext().getAuthentication())) { | ||||
|                 if (logger.isDebugEnabled()) { | ||||
|                     logger.debug("Access is denied (user is anonymous); redirecting to authentication entry point", | ||||
|                         accessDenied); | ||||
|                 } | ||||
| 
 | ||||
|             ((HttpServletRequest) request).getSession().setAttribute(ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY, | ||||
|                 accessDenied); | ||||
|             sendAccessDeniedError(request, response, accessDenied); | ||||
|                 sendStartAuthentication(fi, null); | ||||
|             } else { | ||||
|                 if (logger.isDebugEnabled()) { | ||||
|                     logger.debug("Access is denied (user is not anonymous); sending back forbidden response", | ||||
|                         accessDenied); | ||||
|                 } | ||||
| 
 | ||||
|                 sendAccessDeniedError(fi, accessDenied); | ||||
|             } | ||||
|         } catch (Throwable otherException) { | ||||
|             throw new ServletException(otherException); | ||||
|         } | ||||
| @ -216,19 +214,43 @@ public class SecurityEnforcementFilter implements Filter, InitializingBean { | ||||
| 
 | ||||
|     public void init(FilterConfig filterConfig) throws ServletException {} | ||||
| 
 | ||||
|     /** | ||||
|      * Allows subclasses to override if required | ||||
|      * | ||||
|      * @param request | ||||
|      * @param response | ||||
|      * @param accessDenied | ||||
|      * | ||||
|      * @throws IOException | ||||
|      */ | ||||
|     protected void sendAccessDeniedError(ServletRequest request, | ||||
|         ServletResponse response, AccessDeniedException accessDenied) | ||||
|         throws IOException { | ||||
|         ((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN, | ||||
|     protected void sendAccessDeniedError(FilterInvocation fi, | ||||
|         AccessDeniedException accessDenied) | ||||
|         throws ServletException, IOException { | ||||
|         ((HttpServletRequest) fi.getRequest()).getSession().setAttribute(ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY, | ||||
|             accessDenied); | ||||
|         ((HttpServletResponse) fi.getResponse()).sendError(HttpServletResponse.SC_FORBIDDEN, | ||||
|             accessDenied.getMessage()); // 403 | ||||
|     } | ||||
| 
 | ||||
|     protected void sendStartAuthentication(FilterInvocation fi, | ||||
|         AuthenticationException reason) throws ServletException, IOException { | ||||
|         HttpServletRequest request = (HttpServletRequest) fi.getRequest(); | ||||
| 
 | ||||
|         int port = portResolver.getServerPort(request); | ||||
|         boolean includePort = true; | ||||
| 
 | ||||
|         if ("http".equals(request.getScheme().toLowerCase()) && (port == 80)) { | ||||
|             includePort = false; | ||||
|         } | ||||
| 
 | ||||
|         if ("https".equals(request.getScheme().toLowerCase()) && (port == 443)) { | ||||
|             includePort = false; | ||||
|         } | ||||
| 
 | ||||
|         String targetUrl = request.getScheme() + "://" | ||||
|             + request.getServerName() + ((includePort) ? (":" + port) : "") | ||||
|             + request.getContextPath() + fi.getRequestUrl(); | ||||
| 
 | ||||
|         if (logger.isDebugEnabled()) { | ||||
|             logger.debug( | ||||
|                 "Authentication entry point being called; target URL added to Session: " | ||||
|                 + targetUrl); | ||||
|         } | ||||
| 
 | ||||
|         ((HttpServletRequest) request).getSession().setAttribute(AbstractProcessingFilter.ACEGI_SECURITY_TARGET_URL_KEY, | ||||
|             targetUrl); | ||||
|         authenticationEntryPoint.commence(request, | ||||
|             (HttpServletResponse) fi.getResponse(), reason); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,86 @@ | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package net.sf.acegisecurity.providers.anonymous; | ||||
| 
 | ||||
| import net.sf.acegisecurity.Authentication; | ||||
| import net.sf.acegisecurity.AuthenticationException; | ||||
| import net.sf.acegisecurity.BadCredentialsException; | ||||
| import net.sf.acegisecurity.providers.AuthenticationProvider; | ||||
| 
 | ||||
| import org.apache.commons.logging.Log; | ||||
| import org.apache.commons.logging.LogFactory; | ||||
| 
 | ||||
| import org.springframework.beans.factory.InitializingBean; | ||||
| 
 | ||||
| import org.springframework.util.Assert; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * An {@link AuthenticationProvider} implementation that validates {@link | ||||
|  * net.sf.acegisecurity.providers.anonymous.AnonymousAuthenticationToken}s. | ||||
|  *  | ||||
|  * <p> | ||||
|  * To be successfully validated, the  {@link | ||||
|  * net.sf.acegisecurity.providers.anonymous.AnonymousAuthenticationToken#getKeyHash()} | ||||
|  * must match this class' {@link #getKey()}. | ||||
|  * </p> | ||||
|  * | ||||
|  * @author Ben Alex | ||||
|  * @version $Id$ | ||||
|  */ | ||||
| public class AnonymousAuthenticationProvider implements AuthenticationProvider, | ||||
|     InitializingBean { | ||||
|     //~ Static fields/initializers ============================================= | ||||
| 
 | ||||
|     private static final Log logger = LogFactory.getLog(AnonymousAuthenticationProvider.class); | ||||
| 
 | ||||
|     //~ Instance fields ======================================================== | ||||
| 
 | ||||
|     private String key; | ||||
| 
 | ||||
|     //~ Methods ================================================================ | ||||
| 
 | ||||
|     public void setKey(String key) { | ||||
|         this.key = key; | ||||
|     } | ||||
| 
 | ||||
|     public String getKey() { | ||||
|         return key; | ||||
|     } | ||||
| 
 | ||||
|     public void afterPropertiesSet() throws Exception { | ||||
|         Assert.hasLength(key); | ||||
|     } | ||||
| 
 | ||||
|     public Authentication authenticate(Authentication authentication) | ||||
|         throws AuthenticationException { | ||||
|         if (!supports(authentication.getClass())) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         if (this.key.hashCode() != ((AnonymousAuthenticationToken) authentication) | ||||
|             .getKeyHash()) { | ||||
|             throw new BadCredentialsException( | ||||
|                 "The presented AnonymousAuthenticationToken does not contain the expected key"); | ||||
|         } | ||||
| 
 | ||||
|         return authentication; | ||||
|     } | ||||
| 
 | ||||
|     public boolean supports(Class authentication) { | ||||
|         return (AnonymousAuthenticationToken.class.isAssignableFrom(authentication)); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,133 @@ | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package net.sf.acegisecurity.providers.anonymous; | ||||
| 
 | ||||
| import net.sf.acegisecurity.GrantedAuthority; | ||||
| import net.sf.acegisecurity.providers.AbstractAuthenticationToken; | ||||
| 
 | ||||
| import java.io.Serializable; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Represents an anonymous <code>Authentication</code>. | ||||
|  * | ||||
|  * @author Ben Alex | ||||
|  * @version $Id$ | ||||
|  */ | ||||
| public class AnonymousAuthenticationToken extends AbstractAuthenticationToken | ||||
|     implements Serializable { | ||||
|     //~ Instance fields ======================================================== | ||||
| 
 | ||||
|     private Object principal; | ||||
|     private GrantedAuthority[] authorities; | ||||
|     private int keyHash; | ||||
| 
 | ||||
|     //~ Constructors =========================================================== | ||||
| 
 | ||||
|     /** | ||||
|      * Constructor. | ||||
|      * | ||||
|      * @param key to identify if this object made by an authorised client | ||||
|      * @param principal the principal (typically a <code>UserDetails</code>) | ||||
|      * @param authorities the authorities granted to the principal | ||||
|      * | ||||
|      * @throws IllegalArgumentException if a <code>null</code> was passed | ||||
|      */ | ||||
|     public AnonymousAuthenticationToken(String key, Object principal, | ||||
|         GrantedAuthority[] authorities) { | ||||
|         if ((key == null) || ("".equals(key)) || (principal == null) | ||||
|             || "".equals(principal) || (authorities == null) | ||||
|             || (authorities.length == 0)) { | ||||
|             throw new IllegalArgumentException( | ||||
|                 "Cannot pass null or empty values to constructor"); | ||||
|         } | ||||
| 
 | ||||
|         for (int i = 0; i < authorities.length; i++) { | ||||
|             if (authorities[i] == null) { | ||||
|                 throw new IllegalArgumentException("Granted authority element " | ||||
|                     + i | ||||
|                     + " is null - GrantedAuthority[] cannot contain any null elements"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         this.keyHash = key.hashCode(); | ||||
|         this.principal = principal; | ||||
|         this.authorities = authorities; | ||||
|     } | ||||
| 
 | ||||
|     protected AnonymousAuthenticationToken() { | ||||
|         throw new IllegalArgumentException("Cannot use default constructor"); | ||||
|     } | ||||
| 
 | ||||
|     //~ Methods ================================================================ | ||||
| 
 | ||||
|     /** | ||||
|      * Ignored (always <code>true</code>). | ||||
|      * | ||||
|      * @param isAuthenticated ignored | ||||
|      */ | ||||
|     public void setAuthenticated(boolean isAuthenticated) { | ||||
|         // ignored | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Always returns <code>true</code>. | ||||
|      * | ||||
|      * @return true | ||||
|      */ | ||||
|     public boolean isAuthenticated() { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     public GrantedAuthority[] getAuthorities() { | ||||
|         return this.authorities; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Always returns an empty <code>String</code> | ||||
|      * | ||||
|      * @return an empty String | ||||
|      */ | ||||
|     public Object getCredentials() { | ||||
|         return ""; | ||||
|     } | ||||
| 
 | ||||
|     public int getKeyHash() { | ||||
|         return this.keyHash; | ||||
|     } | ||||
| 
 | ||||
|     public Object getPrincipal() { | ||||
|         return this.principal; | ||||
|     } | ||||
| 
 | ||||
|     public boolean equals(Object obj) { | ||||
|         if (!super.equals(obj)) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (obj instanceof AnonymousAuthenticationToken) { | ||||
|             AnonymousAuthenticationToken test = (AnonymousAuthenticationToken) obj; | ||||
| 
 | ||||
|             if (this.getKeyHash() != test.getKeyHash()) { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,169 @@ | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package net.sf.acegisecurity.providers.anonymous; | ||||
| 
 | ||||
| import net.sf.acegisecurity.Authentication; | ||||
| import net.sf.acegisecurity.context.security.SecureContext; | ||||
| import net.sf.acegisecurity.context.security.SecureContextUtils; | ||||
| import net.sf.acegisecurity.intercept.web.AuthenticationEntryPoint; | ||||
| import net.sf.acegisecurity.providers.dao.memory.UserAttribute; | ||||
| import net.sf.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint; | ||||
| 
 | ||||
| import org.apache.commons.logging.Log; | ||||
| import org.apache.commons.logging.LogFactory; | ||||
| 
 | ||||
| import org.springframework.beans.factory.InitializingBean; | ||||
| 
 | ||||
| import org.springframework.util.Assert; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| import javax.servlet.Filter; | ||||
| import javax.servlet.FilterChain; | ||||
| import javax.servlet.FilterConfig; | ||||
| import javax.servlet.ServletException; | ||||
| import javax.servlet.ServletRequest; | ||||
| import javax.servlet.ServletResponse; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Detects if there is no <code>Authentication</code> object in the | ||||
|  * <code>ContextHolder</code>,  and populates it with one if needed. | ||||
|  *  | ||||
|  * <P></p> | ||||
|  *  | ||||
|  * <p> | ||||
|  * In summary, this filter is responsible for processing any request that has a | ||||
|  * HTTP request header of <code>Authorization</code> with an authentication | ||||
|  * scheme of <code>Basic</code> and a Base64-encoded | ||||
|  * <code>username:password</code> token. For example, to authenticate user | ||||
|  * "Aladdin" with password "open sesame" the following header would be | ||||
|  * presented: | ||||
|  * </p> | ||||
|  *  | ||||
|  * <p> | ||||
|  * <code>Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==</code>. | ||||
|  * </p> | ||||
|  *  | ||||
|  * <p> | ||||
|  * This filter can be used to provide BASIC authentication services to both | ||||
|  * remoting protocol clients (such as Hessian and SOAP) as well as standard | ||||
|  * user agents (such as Internet Explorer and Netscape). | ||||
|  * </p> | ||||
|  *  | ||||
|  * <P> | ||||
|  * If authentication is successful, the resulting {@link Authentication} object | ||||
|  * will be placed into the <code>ContextHolder</code>. | ||||
|  * </p> | ||||
|  *  | ||||
|  * <p> | ||||
|  * If authentication fails, an {@link AuthenticationEntryPoint} implementation | ||||
|  * is called. Usually this should be {@link BasicProcessingFilterEntryPoint}, | ||||
|  * which will prompt the user to authenticate again via BASIC authentication. | ||||
|  * </p> | ||||
|  *  | ||||
|  * <P> | ||||
|  * Basic authentication is an attractive protocol because it is simple and | ||||
|  * widely deployed. However, it still transmits a password in clear text and | ||||
|  * as such is undesirable in many situations. Digest authentication is also | ||||
|  * provided by Acegi Security and should be used instead of Basic | ||||
|  * authentication wherever possible. See {@link | ||||
|  * net.sf.acegisecurity.ui.digestauth.DigestProcessingFilter}. | ||||
|  * </p> | ||||
|  *  | ||||
|  * <P> | ||||
|  * <B>Do not use this class directly.</B> Instead configure | ||||
|  * <code>web.xml</code> to use the {@link | ||||
|  * net.sf.acegisecurity.util.FilterToBeanProxy}. | ||||
|  * </p> | ||||
|  * | ||||
|  * @author Ben Alex | ||||
|  * @version $Id$ | ||||
|  */ | ||||
| public class AnonymousProcessingFilter implements Filter, InitializingBean { | ||||
|     //~ Static fields/initializers ============================================= | ||||
| 
 | ||||
|     private static final Log logger = LogFactory.getLog(AnonymousProcessingFilter.class); | ||||
| 
 | ||||
|     //~ Instance fields ======================================================== | ||||
| 
 | ||||
|     private String key; | ||||
|     private UserAttribute userAttribute; | ||||
| 
 | ||||
|     //~ Methods ================================================================ | ||||
| 
 | ||||
|     public void setKey(String key) { | ||||
|         this.key = key; | ||||
|     } | ||||
| 
 | ||||
|     public String getKey() { | ||||
|         return key; | ||||
|     } | ||||
| 
 | ||||
|     public void setUserAttribute(UserAttribute userAttributeDefinition) { | ||||
|         this.userAttribute = userAttributeDefinition; | ||||
|     } | ||||
| 
 | ||||
|     public UserAttribute getUserAttribute() { | ||||
|         return userAttribute; | ||||
|     } | ||||
| 
 | ||||
|     public void afterPropertiesSet() throws Exception { | ||||
|         Assert.notNull(userAttribute); | ||||
|         Assert.hasLength(key); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Does nothing - we reply on IoC lifecycle services instead. | ||||
|      */ | ||||
|     public void destroy() {} | ||||
| 
 | ||||
|     public void doFilter(ServletRequest request, ServletResponse response, | ||||
|         FilterChain chain) throws IOException, ServletException { | ||||
|         SecureContext sc = SecureContextUtils.getSecureContext(); | ||||
| 
 | ||||
|         if (sc.getAuthentication() == null) { | ||||
|             sc.setAuthentication(createAuthentication(request)); | ||||
| 
 | ||||
|             if (logger.isDebugEnabled()) { | ||||
|                 logger.debug("Replaced ContextHolder with anonymous token: '" | ||||
|                     + sc.getAuthentication() + "'"); | ||||
|             } | ||||
|         } else { | ||||
|             if (logger.isDebugEnabled()) { | ||||
|                 logger.debug( | ||||
|                     "ContextHolder not replaced with anonymous token, as ContextHolder already contained: '" | ||||
|                     + sc.getAuthentication() + "'"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         chain.doFilter(request, response); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Does nothing - we reply on IoC lifecycle services instead. | ||||
|      * | ||||
|      * @param arg0 DOCUMENT ME! | ||||
|      * | ||||
|      * @throws ServletException DOCUMENT ME! | ||||
|      */ | ||||
|     public void init(FilterConfig arg0) throws ServletException {} | ||||
| 
 | ||||
|     protected Authentication createAuthentication(ServletRequest request) { | ||||
|         return new AnonymousAuthenticationToken(key, | ||||
|             userAttribute.getPassword(), userAttribute.getAuthorities()); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,7 @@ | ||||
| <html> | ||||
| <body> | ||||
| Allows you to secure every invocation (especially useful for web request | ||||
| URI security) by always having either an actual principal or an anonymous | ||||
| principal authenticated. | ||||
| </body> | ||||
| </html> | ||||
| @ -1,4 +1,4 @@ | ||||
| /* Copyright 2004 Acegi Technology Pty Limited | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @ -29,7 +29,7 @@ import java.util.Vector; | ||||
|  * @author Ben Alex | ||||
|  * @version $Id$ | ||||
|  */ | ||||
| public class UserAttributeDefinition { | ||||
| public class UserAttribute { | ||||
|     //~ Instance fields ======================================================== | ||||
| 
 | ||||
|     private List authorities = new Vector(); | ||||
| @ -38,7 +38,7 @@ public class UserAttributeDefinition { | ||||
| 
 | ||||
|     //~ Constructors =========================================================== | ||||
| 
 | ||||
|     public UserAttributeDefinition() { | ||||
|     public UserAttribute() { | ||||
|         super(); | ||||
|     } | ||||
| 
 | ||||
| @ -1,4 +1,4 @@ | ||||
| /* Copyright 2004 Acegi Technology Pty Limited | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @ -23,8 +23,8 @@ import java.beans.PropertyEditorSupport; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Property editor that creates a {@link UserAttributeDefinition} from a  comma | ||||
|  * separated list of values. | ||||
|  * Property editor that creates a {@link UserAttribute} from a  comma separated | ||||
|  * list of values. | ||||
|  * | ||||
|  * @author Ben Alex | ||||
|  * @version $Id$ | ||||
| @ -37,7 +37,7 @@ public class UserAttributeEditor extends PropertyEditorSupport { | ||||
|             setValue(null); | ||||
|         } else { | ||||
|             String[] tokens = StringUtils.commaDelimitedListToStringArray(s); | ||||
|             UserAttributeDefinition userAttrib = new UserAttributeDefinition(); | ||||
|             UserAttribute userAttrib = new UserAttribute(); | ||||
| 
 | ||||
|             for (int i = 0; i < tokens.length; i++) { | ||||
|                 String currentToken = tokens[i]; | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| /* Copyright 2004 Acegi Technology Pty Limited | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @ -91,8 +91,7 @@ public class UserMapEditor extends PropertyEditorSupport { | ||||
|                 // Convert value to a password, enabled setting, and list of granted authorities | ||||
|                 configAttribEd.setAsText(value); | ||||
| 
 | ||||
|                 UserAttributeDefinition attr = (UserAttributeDefinition) configAttribEd | ||||
|                     .getValue(); | ||||
|                 UserAttribute attr = (UserAttribute) configAttribEd.getValue(); | ||||
| 
 | ||||
|                 // Make a user object, assuming the properties were properly provided | ||||
|                 if (attr != null) { | ||||
|  | ||||
| @ -0,0 +1,67 @@ | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package net.sf.acegisecurity; | ||||
| 
 | ||||
| import junit.framework.TestCase; | ||||
| 
 | ||||
| import net.sf.acegisecurity.providers.TestingAuthenticationToken; | ||||
| import net.sf.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Tests {@link net.sf.acegisecurity.AuthenticationTrustResolverImpl}. | ||||
|  * | ||||
|  * @author Ben Alex | ||||
|  * @version $Id$ | ||||
|  */ | ||||
| public class AuthenticationTrustResolverImplTests extends TestCase { | ||||
|     //~ Constructors =========================================================== | ||||
| 
 | ||||
|     public AuthenticationTrustResolverImplTests() { | ||||
|         super(); | ||||
|     } | ||||
| 
 | ||||
|     public AuthenticationTrustResolverImplTests(String arg0) { | ||||
|         super(arg0); | ||||
|     } | ||||
| 
 | ||||
|     //~ Methods ================================================================ | ||||
| 
 | ||||
|     public static void main(String[] args) { | ||||
|         junit.textui.TestRunner.run(AuthenticationTrustResolverImplTests.class); | ||||
|     } | ||||
| 
 | ||||
|     public void testCorrectOperationIsAnonymous() { | ||||
|         AuthenticationTrustResolverImpl trustResolver = new AuthenticationTrustResolverImpl(); | ||||
|         assertTrue(trustResolver.isAnonymous( | ||||
|                 new AnonymousAuthenticationToken("ignored", "ignored", | ||||
|                     new GrantedAuthority[] {new GrantedAuthorityImpl("ignored")}))); | ||||
|         assertFalse(trustResolver.isAnonymous( | ||||
|                 new TestingAuthenticationToken("ignored", "ignored", | ||||
|                     new GrantedAuthority[] {new GrantedAuthorityImpl("ignored")}))); | ||||
|     } | ||||
| 
 | ||||
|     public void testGettersSetters() { | ||||
|         AuthenticationTrustResolverImpl trustResolver = new AuthenticationTrustResolverImpl(); | ||||
| 
 | ||||
|         assertEquals(AnonymousAuthenticationToken.class, | ||||
|             trustResolver.getAnonymousClass()); | ||||
|         trustResolver.setAnonymousClass(String.class); | ||||
|         assertEquals(String.class, trustResolver.getAnonymousClass()); | ||||
| 
 | ||||
|         assertNull(trustResolver.getRememberMeClass()); | ||||
|     } | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| /* Copyright 2004 Acegi Technology Pty Limited | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @ -19,11 +19,17 @@ import junit.framework.TestCase; | ||||
| 
 | ||||
| import net.sf.acegisecurity.AccessDeniedException; | ||||
| import net.sf.acegisecurity.BadCredentialsException; | ||||
| import net.sf.acegisecurity.GrantedAuthority; | ||||
| import net.sf.acegisecurity.GrantedAuthorityImpl; | ||||
| import net.sf.acegisecurity.MockAuthenticationEntryPoint; | ||||
| import net.sf.acegisecurity.MockHttpServletRequest; | ||||
| import net.sf.acegisecurity.MockHttpServletResponse; | ||||
| import net.sf.acegisecurity.MockHttpSession; | ||||
| import net.sf.acegisecurity.MockPortResolver; | ||||
| import net.sf.acegisecurity.context.ContextHolder; | ||||
| import net.sf.acegisecurity.context.security.SecureContext; | ||||
| import net.sf.acegisecurity.context.security.SecureContextImpl; | ||||
| import net.sf.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; | ||||
| import net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| @ -62,8 +68,47 @@ public class SecurityEnforcementFilterTests extends TestCase { | ||||
|         junit.textui.TestRunner.run(SecurityEnforcementFilterTests.class); | ||||
|     } | ||||
| 
 | ||||
|     public void testAccessDeniedWhenAccessDeniedException() | ||||
|         throws Exception { | ||||
|     public void testAccessDeniedWhenAnonymous() throws Exception { | ||||
|         // Setup our HTTP request | ||||
|         HttpSession session = new MockHttpSession(); | ||||
|         MockHttpServletRequest request = new MockHttpServletRequest(null, | ||||
|                 session); | ||||
|         request.setServletPath("/secure/page.html"); | ||||
|         request.setServerPort(80); | ||||
|         request.setScheme("http"); | ||||
|         request.setServerName("www.example.com"); | ||||
|         request.setContextPath("/mycontext"); | ||||
|         request.setRequestURL( | ||||
|             "http://www.example.com/mycontext/secure/page.html"); | ||||
| 
 | ||||
|         // Setup our expectation that the filter chain will not be invoked, as access is denied | ||||
|         MockFilterChain chain = new MockFilterChain(false); | ||||
| 
 | ||||
|         // Setup the FilterSecurityInterceptor thrown an access denied exception | ||||
|         MockFilterSecurityInterceptor interceptor = new MockFilterSecurityInterceptor(true, | ||||
|                 false); | ||||
| 
 | ||||
|         // Setup ContextHolder, as filter needs to check if user is anonymous | ||||
|         SecureContext sc = new SecureContextImpl(); | ||||
|         sc.setAuthentication(new AnonymousAuthenticationToken("ignored", | ||||
|                 "ignored", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("IGNORED")})); | ||||
|         ContextHolder.setContext(sc); | ||||
| 
 | ||||
|         // Test | ||||
|         SecurityEnforcementFilter filter = new SecurityEnforcementFilter(); | ||||
|         filter.setFilterSecurityInterceptor(interceptor); | ||||
|         filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint( | ||||
|                 "/login.jsp")); | ||||
| 
 | ||||
|         MockHttpServletResponse response = new MockHttpServletResponse(); | ||||
|         filter.doFilter(request, response, chain); | ||||
|         assertEquals("/mycontext/login.jsp", response.getRedirect()); | ||||
|         assertEquals("http://www.example.com/mycontext/secure/page.html", | ||||
|             request.getSession().getAttribute(AuthenticationProcessingFilter.ACEGI_SECURITY_TARGET_URL_KEY)); | ||||
|     } | ||||
| 
 | ||||
|     public void testAccessDeniedWhenNonAnonymous() throws Exception { | ||||
|         // Setup our HTTP request | ||||
|         HttpSession session = new MockHttpSession(); | ||||
|         MockHttpServletRequest request = new MockHttpServletRequest(null, | ||||
| @ -77,6 +122,11 @@ public class SecurityEnforcementFilterTests extends TestCase { | ||||
|         MockFilterSecurityInterceptor interceptor = new MockFilterSecurityInterceptor(true, | ||||
|                 false); | ||||
| 
 | ||||
|         // Setup ContextHolder, as filter needs to check if user is anonymous | ||||
|         SecureContext sc = new SecureContextImpl(); | ||||
|         sc.setAuthentication(null); | ||||
|         ContextHolder.setContext(sc); | ||||
| 
 | ||||
|         // Test | ||||
|         SecurityEnforcementFilter filter = new SecurityEnforcementFilter(); | ||||
|         filter.setFilterSecurityInterceptor(interceptor); | ||||
| @ -281,6 +331,11 @@ public class SecurityEnforcementFilterTests extends TestCase { | ||||
|         assertTrue(true); | ||||
|     } | ||||
| 
 | ||||
|     protected void tearDown() throws Exception { | ||||
|         super.tearDown(); | ||||
|         ContextHolder.setContext(null); | ||||
|     } | ||||
| 
 | ||||
|     //~ Inner Classes ========================================================== | ||||
| 
 | ||||
|     private class MockFilterChain implements FilterChain { | ||||
|  | ||||
| @ -0,0 +1,122 @@ | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package net.sf.acegisecurity.providers.anonymous; | ||||
| 
 | ||||
| import junit.framework.TestCase; | ||||
| 
 | ||||
| import net.sf.acegisecurity.Authentication; | ||||
| import net.sf.acegisecurity.BadCredentialsException; | ||||
| import net.sf.acegisecurity.GrantedAuthority; | ||||
| import net.sf.acegisecurity.GrantedAuthorityImpl; | ||||
| import net.sf.acegisecurity.providers.TestingAuthenticationToken; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Tests {@link AnonymousAuthenticationProvider}. | ||||
|  * | ||||
|  * @author Ben Alex | ||||
|  * @version $Id$ | ||||
|  */ | ||||
| public class AnonymousAuthenticationProviderTests extends TestCase { | ||||
|     //~ Constructors =========================================================== | ||||
| 
 | ||||
|     public AnonymousAuthenticationProviderTests() { | ||||
|         super(); | ||||
|     } | ||||
| 
 | ||||
|     public AnonymousAuthenticationProviderTests(String arg0) { | ||||
|         super(arg0); | ||||
|     } | ||||
| 
 | ||||
|     //~ Methods ================================================================ | ||||
| 
 | ||||
|     public final void setUp() throws Exception { | ||||
|         super.setUp(); | ||||
|     } | ||||
| 
 | ||||
|     public static void main(String[] args) { | ||||
|         junit.textui.TestRunner.run(AnonymousAuthenticationProviderTests.class); | ||||
|     } | ||||
| 
 | ||||
|     public void testDetectsAnInvalidKey() throws Exception { | ||||
|         AnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider(); | ||||
|         aap.setKey("qwerty"); | ||||
| 
 | ||||
|         AnonymousAuthenticationToken token = new AnonymousAuthenticationToken("WRONG_KEY", | ||||
|                 "Test", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( | ||||
|                         "ROLE_TWO")}); | ||||
| 
 | ||||
|         try { | ||||
|             Authentication result = aap.authenticate(token); | ||||
|             fail("Should have thrown BadCredentialsException"); | ||||
|         } catch (BadCredentialsException expected) { | ||||
|             assertEquals("The presented AnonymousAuthenticationToken does not contain the expected key", | ||||
|                 expected.getMessage()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void testDetectsMissingKey() throws Exception { | ||||
|         AnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider(); | ||||
| 
 | ||||
|         try { | ||||
|             aap.afterPropertiesSet(); | ||||
|             fail("Should have thrown IllegalArgumentException"); | ||||
|         } catch (IllegalArgumentException expected) { | ||||
|             assertTrue(true); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void testGettersSetters() throws Exception { | ||||
|         AnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider(); | ||||
|         aap.setKey("qwerty"); | ||||
|         aap.afterPropertiesSet(); | ||||
|         assertEquals("qwerty", aap.getKey()); | ||||
|     } | ||||
| 
 | ||||
|     public void testIgnoresClassesItDoesNotSupport() throws Exception { | ||||
|         AnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider(); | ||||
|         aap.setKey("qwerty"); | ||||
| 
 | ||||
|         TestingAuthenticationToken token = new TestingAuthenticationToken("user", | ||||
|                 "password", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_A")}); | ||||
|         assertFalse(aap.supports(TestingAuthenticationToken.class)); | ||||
| 
 | ||||
|         // Try it anyway | ||||
|         assertNull(aap.authenticate(token)); | ||||
|     } | ||||
| 
 | ||||
|     public void testNormalOperation() throws Exception { | ||||
|         AnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider(); | ||||
|         aap.setKey("qwerty"); | ||||
| 
 | ||||
|         AnonymousAuthenticationToken token = new AnonymousAuthenticationToken("qwerty", | ||||
|                 "Test", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( | ||||
|                         "ROLE_TWO")}); | ||||
| 
 | ||||
|         Authentication result = aap.authenticate(token); | ||||
| 
 | ||||
|         assertEquals(result, token); | ||||
|     } | ||||
| 
 | ||||
|     public void testSupports() { | ||||
|         AnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider(); | ||||
|         assertTrue(aap.supports(AnonymousAuthenticationToken.class)); | ||||
|         assertFalse(aap.supports(TestingAuthenticationToken.class)); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,190 @@ | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package net.sf.acegisecurity.providers.anonymous; | ||||
| 
 | ||||
| import junit.framework.TestCase; | ||||
| 
 | ||||
| import net.sf.acegisecurity.GrantedAuthority; | ||||
| import net.sf.acegisecurity.GrantedAuthorityImpl; | ||||
| import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; | ||||
| 
 | ||||
| import java.util.List; | ||||
| import java.util.Vector; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Tests {@link AnonymousAuthenticationToken}. | ||||
|  * | ||||
|  * @author Ben Alex | ||||
|  * @version $Id$ | ||||
|  */ | ||||
| public class AnonymousAuthenticationTokenTests extends TestCase { | ||||
|     //~ Constructors =========================================================== | ||||
| 
 | ||||
|     public AnonymousAuthenticationTokenTests() { | ||||
|         super(); | ||||
|     } | ||||
| 
 | ||||
|     public AnonymousAuthenticationTokenTests(String arg0) { | ||||
|         super(arg0); | ||||
|     } | ||||
| 
 | ||||
|     //~ Methods ================================================================ | ||||
| 
 | ||||
|     public final void setUp() throws Exception { | ||||
|         super.setUp(); | ||||
|     } | ||||
| 
 | ||||
|     public static void main(String[] args) { | ||||
|         junit.textui.TestRunner.run(AnonymousAuthenticationTokenTests.class); | ||||
|     } | ||||
| 
 | ||||
|     public void testConstructorRejectsNulls() { | ||||
|         try { | ||||
|             new AnonymousAuthenticationToken(null, "Test", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( | ||||
|                         "ROLE_TWO")}); | ||||
|             fail("Should have thrown IllegalArgumentException"); | ||||
|         } catch (IllegalArgumentException expected) { | ||||
|             assertTrue(true); | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             new AnonymousAuthenticationToken("key", null, | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( | ||||
|                         "ROLE_TWO")}); | ||||
|             fail("Should have thrown IllegalArgumentException"); | ||||
|         } catch (IllegalArgumentException expected) { | ||||
|             assertTrue(true); | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             new AnonymousAuthenticationToken("key", "Test", null); | ||||
|             fail("Should have thrown IllegalArgumentException"); | ||||
|         } catch (IllegalArgumentException expected) { | ||||
|             assertTrue(true); | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             new AnonymousAuthenticationToken("key", "Test", | ||||
|                 new GrantedAuthority[] {null}); | ||||
|             fail("Should have thrown IllegalArgumentException"); | ||||
|         } catch (IllegalArgumentException expected) { | ||||
|             assertTrue(true); | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             new AnonymousAuthenticationToken("key", "Test", | ||||
|                 new GrantedAuthority[] {}); | ||||
|             fail("Should have thrown IllegalArgumentException"); | ||||
|         } catch (IllegalArgumentException expected) { | ||||
|             assertTrue(true); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void testEqualsWhenEqual() { | ||||
|         List proxyList1 = new Vector(); | ||||
|         proxyList1.add("https://localhost/newPortal/j_acegi_cas_security_check"); | ||||
| 
 | ||||
|         AnonymousAuthenticationToken token1 = new AnonymousAuthenticationToken("key", | ||||
|                 "Test", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( | ||||
|                         "ROLE_TWO")}); | ||||
| 
 | ||||
|         AnonymousAuthenticationToken token2 = new AnonymousAuthenticationToken("key", | ||||
|                 "Test", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( | ||||
|                         "ROLE_TWO")}); | ||||
| 
 | ||||
|         assertEquals(token1, token2); | ||||
|     } | ||||
| 
 | ||||
|     public void testGetters() { | ||||
|         AnonymousAuthenticationToken token = new AnonymousAuthenticationToken("key", | ||||
|                 "Test", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( | ||||
|                         "ROLE_TWO")}); | ||||
| 
 | ||||
|         assertEquals("key".hashCode(), token.getKeyHash()); | ||||
|         assertEquals("Test", token.getPrincipal()); | ||||
|         assertEquals("", token.getCredentials()); | ||||
|         assertEquals("ROLE_ONE", token.getAuthorities()[0].getAuthority()); | ||||
|         assertEquals("ROLE_TWO", token.getAuthorities()[1].getAuthority()); | ||||
|         assertTrue(token.isAuthenticated()); | ||||
|     } | ||||
| 
 | ||||
|     public void testNoArgConstructor() { | ||||
|         try { | ||||
|             new AnonymousAuthenticationToken(); | ||||
|             fail("Should have thrown IllegalArgumentException"); | ||||
|         } catch (IllegalArgumentException expected) { | ||||
|             assertTrue(true); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void testNotEqualsDueToAbstractParentEqualsCheck() { | ||||
|         AnonymousAuthenticationToken token1 = new AnonymousAuthenticationToken("key", | ||||
|                 "Test", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( | ||||
|                         "ROLE_TWO")}); | ||||
| 
 | ||||
|         AnonymousAuthenticationToken token2 = new AnonymousAuthenticationToken("key", | ||||
|                 "DIFFERENT_PRINCIPAL", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( | ||||
|                         "ROLE_TWO")}); | ||||
| 
 | ||||
|         assertFalse(token1.equals(token2)); | ||||
|     } | ||||
| 
 | ||||
|     public void testNotEqualsDueToDifferentAuthenticationClass() { | ||||
|         AnonymousAuthenticationToken token1 = new AnonymousAuthenticationToken("key", | ||||
|                 "Test", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( | ||||
|                         "ROLE_TWO")}); | ||||
| 
 | ||||
|         UsernamePasswordAuthenticationToken token2 = new UsernamePasswordAuthenticationToken("Test", | ||||
|                 "Password", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( | ||||
|                         "ROLE_TWO")}); | ||||
|         token2.setAuthenticated(true); | ||||
| 
 | ||||
|         assertFalse(token1.equals(token2)); | ||||
|     } | ||||
| 
 | ||||
|     public void testNotEqualsDueToKey() { | ||||
|         AnonymousAuthenticationToken token1 = new AnonymousAuthenticationToken("key", | ||||
|                 "Test", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( | ||||
|                         "ROLE_TWO")}); | ||||
| 
 | ||||
|         AnonymousAuthenticationToken token2 = new AnonymousAuthenticationToken("DIFFERENT_KEY", | ||||
|                 "Test", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( | ||||
|                         "ROLE_TWO")}); | ||||
| 
 | ||||
|         assertFalse(token1.equals(token2)); | ||||
|     } | ||||
| 
 | ||||
|     public void testSetAuthenticatedIgnored() { | ||||
|         AnonymousAuthenticationToken token = new AnonymousAuthenticationToken("key", | ||||
|                 "Test", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( | ||||
|                         "ROLE_TWO")}); | ||||
|         assertTrue(token.isAuthenticated()); | ||||
|         token.setAuthenticated(false); // ignored | ||||
|         assertTrue(token.isAuthenticated()); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,200 @@ | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package net.sf.acegisecurity.providers.anonymous; | ||||
| 
 | ||||
| import junit.framework.TestCase; | ||||
| 
 | ||||
| import net.sf.acegisecurity.Authentication; | ||||
| import net.sf.acegisecurity.GrantedAuthority; | ||||
| import net.sf.acegisecurity.GrantedAuthorityImpl; | ||||
| import net.sf.acegisecurity.MockFilterConfig; | ||||
| import net.sf.acegisecurity.MockHttpServletRequest; | ||||
| import net.sf.acegisecurity.MockHttpServletResponse; | ||||
| import net.sf.acegisecurity.context.ContextHolder; | ||||
| import net.sf.acegisecurity.context.security.SecureContext; | ||||
| import net.sf.acegisecurity.context.security.SecureContextImpl; | ||||
| import net.sf.acegisecurity.context.security.SecureContextUtils; | ||||
| import net.sf.acegisecurity.providers.TestingAuthenticationToken; | ||||
| import net.sf.acegisecurity.providers.dao.memory.UserAttribute; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| import javax.servlet.Filter; | ||||
| import javax.servlet.FilterChain; | ||||
| import javax.servlet.FilterConfig; | ||||
| import javax.servlet.ServletException; | ||||
| import javax.servlet.ServletRequest; | ||||
| import javax.servlet.ServletResponse; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Tests {@link AnonymousProcessingFilter}. | ||||
|  * | ||||
|  * @author Ben Alex | ||||
|  * @version $Id$ | ||||
|  */ | ||||
| public class AnonymousProcessingFilterTests extends TestCase { | ||||
|     //~ Constructors =========================================================== | ||||
| 
 | ||||
|     public AnonymousProcessingFilterTests() { | ||||
|         super(); | ||||
|     } | ||||
| 
 | ||||
|     public AnonymousProcessingFilterTests(String arg0) { | ||||
|         super(arg0); | ||||
|     } | ||||
| 
 | ||||
|     //~ Methods ================================================================ | ||||
| 
 | ||||
|     public static void main(String[] args) { | ||||
|         junit.textui.TestRunner.run(AnonymousProcessingFilterTests.class); | ||||
|     } | ||||
| 
 | ||||
|     public void testDetectsMissingKey() throws Exception { | ||||
|         UserAttribute user = new UserAttribute(); | ||||
|         user.setPassword("anonymousUsername"); | ||||
|         user.addAuthority(new GrantedAuthorityImpl("ROLE_ANONYMOUS")); | ||||
| 
 | ||||
|         AnonymousProcessingFilter filter = new AnonymousProcessingFilter(); | ||||
|         filter.setUserAttribute(user); | ||||
| 
 | ||||
|         try { | ||||
|             filter.afterPropertiesSet(); | ||||
|             fail("Should have thrown IllegalArgumentException"); | ||||
|         } catch (IllegalArgumentException expected) { | ||||
|             assertTrue(true); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void testDetectsUserAttribute() throws Exception { | ||||
|         AnonymousProcessingFilter filter = new AnonymousProcessingFilter(); | ||||
|         filter.setKey("qwerty"); | ||||
| 
 | ||||
|         try { | ||||
|             filter.afterPropertiesSet(); | ||||
|             fail("Should have thrown IllegalArgumentException"); | ||||
|         } catch (IllegalArgumentException expected) { | ||||
|             assertTrue(true); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void testGettersSetters() throws Exception { | ||||
|         UserAttribute user = new UserAttribute(); | ||||
|         user.setPassword("anonymousUsername"); | ||||
|         user.addAuthority(new GrantedAuthorityImpl("ROLE_ANONYMOUS")); | ||||
| 
 | ||||
|         AnonymousProcessingFilter filter = new AnonymousProcessingFilter(); | ||||
|         filter.setKey("qwerty"); | ||||
|         filter.setUserAttribute(user); | ||||
|         filter.afterPropertiesSet(); | ||||
| 
 | ||||
|         assertEquals("qwerty", filter.getKey()); | ||||
|         assertEquals(user, filter.getUserAttribute()); | ||||
|     } | ||||
| 
 | ||||
|     public void testOperationWhenAuthenticationExistsInContextHolder() | ||||
|         throws Exception { | ||||
|         // Put an Authentication object into the ContextHolder | ||||
|         SecureContext sc = SecureContextUtils.getSecureContext(); | ||||
|         Authentication originalAuth = new TestingAuthenticationToken("user", | ||||
|                 "password", | ||||
|                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_A")}); | ||||
|         sc.setAuthentication(originalAuth); | ||||
|         ContextHolder.setContext(sc); | ||||
| 
 | ||||
|         // Setup our filter correctly | ||||
|         UserAttribute user = new UserAttribute(); | ||||
|         user.setPassword("anonymousUsername"); | ||||
|         user.addAuthority(new GrantedAuthorityImpl("ROLE_ANONYMOUS")); | ||||
| 
 | ||||
|         AnonymousProcessingFilter filter = new AnonymousProcessingFilter(); | ||||
|         filter.setKey("qwerty"); | ||||
|         filter.setUserAttribute(user); | ||||
|         filter.afterPropertiesSet(); | ||||
| 
 | ||||
|         // Test | ||||
|         executeFilterInContainerSimulator(new MockFilterConfig(), filter, | ||||
|             new MockHttpServletRequest("x"), new MockHttpServletResponse(), | ||||
|             new MockFilterChain(true)); | ||||
| 
 | ||||
|         // Ensure filter didn't change our original object | ||||
|         assertEquals(originalAuth, | ||||
|             SecureContextUtils.getSecureContext().getAuthentication()); | ||||
|     } | ||||
| 
 | ||||
|     public void testOperationWhenNoAuthenticationInContextHolder() | ||||
|         throws Exception { | ||||
|         UserAttribute user = new UserAttribute(); | ||||
|         user.setPassword("anonymousUsername"); | ||||
|         user.addAuthority(new GrantedAuthorityImpl("ROLE_ANONYMOUS")); | ||||
| 
 | ||||
|         AnonymousProcessingFilter filter = new AnonymousProcessingFilter(); | ||||
|         filter.setKey("qwerty"); | ||||
|         filter.setUserAttribute(user); | ||||
|         filter.afterPropertiesSet(); | ||||
| 
 | ||||
|         executeFilterInContainerSimulator(new MockFilterConfig(), filter, | ||||
|             new MockHttpServletRequest("x"), new MockHttpServletResponse(), | ||||
|             new MockFilterChain(true)); | ||||
| 
 | ||||
|         Authentication auth = SecureContextUtils.getSecureContext() | ||||
|                                                 .getAuthentication(); | ||||
|         assertEquals("anonymousUsername", auth.getPrincipal()); | ||||
|         assertEquals(new GrantedAuthorityImpl("ROLE_ANONYMOUS"), | ||||
|             auth.getAuthorities()[0]); | ||||
|     } | ||||
| 
 | ||||
|     protected void setUp() throws Exception { | ||||
|         super.setUp(); | ||||
|         ContextHolder.setContext(new SecureContextImpl()); | ||||
|     } | ||||
| 
 | ||||
|     protected void tearDown() throws Exception { | ||||
|         super.tearDown(); | ||||
|         ContextHolder.setContext(null); | ||||
|     } | ||||
| 
 | ||||
|     private void executeFilterInContainerSimulator(FilterConfig filterConfig, | ||||
|         Filter filter, ServletRequest request, ServletResponse response, | ||||
|         FilterChain filterChain) throws ServletException, IOException { | ||||
|         filter.init(filterConfig); | ||||
|         filter.doFilter(request, response, filterChain); | ||||
|         filter.destroy(); | ||||
|     } | ||||
| 
 | ||||
|     //~ Inner Classes ========================================================== | ||||
| 
 | ||||
|     private class MockFilterChain implements FilterChain { | ||||
|         private boolean expectToProceed; | ||||
| 
 | ||||
|         public MockFilterChain(boolean expectToProceed) { | ||||
|             this.expectToProceed = expectToProceed; | ||||
|         } | ||||
| 
 | ||||
|         private MockFilterChain() { | ||||
|             super(); | ||||
|         } | ||||
| 
 | ||||
|         public void doFilter(ServletRequest request, ServletResponse response) | ||||
|             throws IOException, ServletException { | ||||
|             if (expectToProceed) { | ||||
|                 assertTrue(true); | ||||
|             } else { | ||||
|                 fail("Did not expect filter chain to proceed"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| /* Copyright 2004 Acegi Technology Pty Limited | ||||
| /* Copyright 2004, 2005 Acegi Technology Pty Limited | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @ -19,8 +19,7 @@ import junit.framework.TestCase; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Tests {@link UserAttributeEditor} and associated {@link | ||||
|  * UserAttributeDefinition}. | ||||
|  * Tests {@link UserAttributeEditor} and associated {@link UserAttribute}. | ||||
|  * | ||||
|  * @author Ben Alex | ||||
|  * @version $Id$ | ||||
| @ -50,8 +49,7 @@ public class UserAttributeEditorTests extends TestCase { | ||||
|         UserAttributeEditor editor = new UserAttributeEditor(); | ||||
|         editor.setAsText("password,ROLE_ONE,ROLE_TWO"); | ||||
| 
 | ||||
|         UserAttributeDefinition user = (UserAttributeDefinition) editor | ||||
|             .getValue(); | ||||
|         UserAttribute user = (UserAttribute) editor.getValue(); | ||||
|         assertTrue(user.isValid()); | ||||
|         assertTrue(user.isEnabled()); // default | ||||
|         assertEquals("password", user.getPassword()); | ||||
| @ -64,8 +62,7 @@ public class UserAttributeEditorTests extends TestCase { | ||||
|         UserAttributeEditor editor = new UserAttributeEditor(); | ||||
|         editor.setAsText("password,disabled,ROLE_ONE,ROLE_TWO"); | ||||
| 
 | ||||
|         UserAttributeDefinition user = (UserAttributeDefinition) editor | ||||
|             .getValue(); | ||||
|         UserAttribute user = (UserAttribute) editor.getValue(); | ||||
|         assertTrue(user.isValid()); | ||||
|         assertTrue(!user.isEnabled()); | ||||
|         assertEquals("password", user.getPassword()); | ||||
| @ -78,8 +75,7 @@ public class UserAttributeEditorTests extends TestCase { | ||||
|         UserAttributeEditor editor = new UserAttributeEditor(); | ||||
|         editor.setAsText(""); | ||||
| 
 | ||||
|         UserAttributeDefinition user = (UserAttributeDefinition) editor | ||||
|             .getValue(); | ||||
|         UserAttribute user = (UserAttribute) editor.getValue(); | ||||
|         assertTrue(user == null); | ||||
|     } | ||||
| 
 | ||||
| @ -87,8 +83,7 @@ public class UserAttributeEditorTests extends TestCase { | ||||
|         UserAttributeEditor editor = new UserAttributeEditor(); | ||||
|         editor.setAsText("password,ROLE_ONE,enabled,ROLE_TWO"); | ||||
| 
 | ||||
|         UserAttributeDefinition user = (UserAttributeDefinition) editor | ||||
|             .getValue(); | ||||
|         UserAttribute user = (UserAttribute) editor.getValue(); | ||||
|         assertTrue(user.isValid()); | ||||
|         assertTrue(user.isEnabled()); | ||||
|         assertEquals("password", user.getPassword()); | ||||
| @ -101,8 +96,7 @@ public class UserAttributeEditorTests extends TestCase { | ||||
|         UserAttributeEditor editor = new UserAttributeEditor(); | ||||
|         editor.setAsText("MALFORMED_STRING"); | ||||
| 
 | ||||
|         UserAttributeDefinition user = (UserAttributeDefinition) editor | ||||
|             .getValue(); | ||||
|         UserAttribute user = (UserAttribute) editor.getValue(); | ||||
|         assertTrue(user == null); | ||||
|     } | ||||
| 
 | ||||
| @ -110,8 +104,7 @@ public class UserAttributeEditorTests extends TestCase { | ||||
|         UserAttributeEditor editor = new UserAttributeEditor(); | ||||
|         editor.setAsText("disabled"); | ||||
| 
 | ||||
|         UserAttributeDefinition user = (UserAttributeDefinition) editor | ||||
|             .getValue(); | ||||
|         UserAttribute user = (UserAttribute) editor.getValue(); | ||||
|         assertTrue(user == null); | ||||
|     } | ||||
| 
 | ||||
| @ -119,8 +112,7 @@ public class UserAttributeEditorTests extends TestCase { | ||||
|         UserAttributeEditor editor = new UserAttributeEditor(); | ||||
|         editor.setAsText("password,enabled"); | ||||
| 
 | ||||
|         UserAttributeDefinition user = (UserAttributeDefinition) editor | ||||
|             .getValue(); | ||||
|         UserAttribute user = (UserAttribute) editor.getValue(); | ||||
|         assertTrue(user == null); | ||||
|     } | ||||
| 
 | ||||
| @ -128,8 +120,7 @@ public class UserAttributeEditorTests extends TestCase { | ||||
|         UserAttributeEditor editor = new UserAttributeEditor(); | ||||
|         editor.setAsText(null); | ||||
| 
 | ||||
|         UserAttributeDefinition user = (UserAttributeDefinition) editor | ||||
|             .getValue(); | ||||
|         UserAttribute user = (UserAttribute) editor.getValue(); | ||||
|         assertTrue(user == null); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -2485,7 +2485,7 @@ public boolean supports(Class clazz);</programlisting></para> | ||||
|         <para><programlisting>base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key)) | ||||
| 
 | ||||
| expirationTime:   The date and time when the nonce expires, expressed in milliseconds | ||||
| key:              A private key to prevent modification of the nounce token | ||||
| key:              A private key to prevent modification of the nonce token | ||||
| </programlisting></para> | ||||
| 
 | ||||
|         <para>The <literal>DigestProcessingFilterEntryPoint</literal> has a | ||||
| @ -2548,7 +2548,7 @@ key:              A private key to prevent modification of the nounce token | ||||
|         <para>The configured <literal>AuthenticationDao</literal> is needed | ||||
|         because <literal>DigestProcessingFilter</literal> must have direct | ||||
|         access to the clear text password of a user. Digest Authentication | ||||
|         will NOT work if you are using encoded passwords ni your DAO. The DAO | ||||
|         will NOT work if you are using encoded passwords in your DAO. The DAO | ||||
|         collaborator, along with the <literal>UserCache</literal>, are | ||||
|         typically shared directly with a | ||||
|         <literal>DaoAuthenticationProvider</literal>. The | ||||
| @ -2556,7 +2556,7 @@ key:              A private key to prevent modification of the nounce token | ||||
|         <literal>DigestProcessingFilterEntryPoint</literal>, so that | ||||
|         <literal>DigestProcessingFilter</literal> can obtain the correct | ||||
|         <literal>realmName</literal> and <literal>key</literal> for digest | ||||
|         calculations. </para> | ||||
|         calculations.</para> | ||||
| 
 | ||||
|         <para>Like <literal>BasicAuthenticationFilter</literal>, if | ||||
|         authentication is successful an <literal>Authentication</literal> | ||||
| @ -2580,6 +2580,95 @@ key:              A private key to prevent modification of the nounce token | ||||
|         does comply with the minimum standards of this RFC.</para> | ||||
|       </sect2> | ||||
| 
 | ||||
|       <sect2 id="security-ui-anonymous"> | ||||
|         <title>Anonymous Authentication</title> | ||||
| 
 | ||||
|         <para>Particularly in the case of web request URI security, sometimes | ||||
|         it is more convenient to assign configuration attributes against every | ||||
|         possible secure object invocation. Put differently, sometimes it is | ||||
|         nice to say <literal>ROLE_SOMETHING</literal> is required by default | ||||
|         and only allow certain exceptions to this rule, such as for login, | ||||
|         logout and home pages of an application. There are also other | ||||
|         situations where anonymous authentication would be desired, such as | ||||
|         when an auditing interceptor queries the | ||||
|         <literal>ContextHolder</literal> to identify which principal was | ||||
|         responsible for a given operation. Such classes can be authored with | ||||
|         more robustness if they know the <literal>ContextHolder</literal> | ||||
|         always contains an <literal>Authentication</literal> object, and never | ||||
|         <literal>null</literal>.</para> | ||||
| 
 | ||||
|         <para>Acegi Security provides three classes that together provide an | ||||
|         anoymous authentication feature. | ||||
|         <literal>AnonymousAuthenticationToken</literal> is an implementation | ||||
|         of <literal>Authentication</literal>, and stores the | ||||
|         <literal>GrantedAuthority</literal>[]s which apply to the anonymous | ||||
|         principal. There is a corresponding | ||||
|         <literal>AnonymousAuthenticationProvider</literal>, which is chained | ||||
|         into the <literal>ProviderManager</literal> so that | ||||
|         <literal>AnonymousAuthenticationTokens</literal> are accepted. | ||||
|         Finally, there is an AnonymousProcessingFilter, which is chained after | ||||
|         the normal authentication mechanisms and automatically add an | ||||
|         <literal>AnonymousAuthenticationToken</literal> to the | ||||
|         <literal>ContextHolder</literal> if there is no existing | ||||
|         <literal>Authentication</literal> held there. The definition of the | ||||
|         filter and authentication provider appears as follows:</para> | ||||
| 
 | ||||
|         <para><programlisting><bean id="anonymousProcessingFilter" class="net.sf.acegisecurity.providers.anonymous.AnonymousProcessingFilter"> | ||||
|   <property name="key"><value>foobar</value></property> | ||||
|   <property name="userAttribute"><value>anonymousUser,ROLE_ANONYMOUS</value></property> | ||||
| </bean> | ||||
| 
 | ||||
| <bean id="anonymousAuthenticationProvider" class="net.sf.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider"> | ||||
|   <property name="key"><value>foobar</value></property> | ||||
| </bean></programlisting></para> | ||||
| 
 | ||||
|         <para>The <literal>key</literal> is shared between the filter and | ||||
|         authentication provider, so that tokens created by the former are | ||||
|         accepted by the latter. The <literal>userAttribute</literal> is | ||||
|         expressed in the form of | ||||
|         <literal>usernameInTheAuthenticationToken,grantedAuthority[,grantedAuthority]</literal>. | ||||
|         This is the same syntax as used after the equals sign for | ||||
|         <literal>InMemoryDaoImpl</literal>'s <literal>userMap</literal> | ||||
|         property.</para> | ||||
| 
 | ||||
|         <para>As explained earlier, the benefit of anonymous authentication is | ||||
|         that all URI patterns can have security applied to them. For | ||||
|         example:</para> | ||||
| 
 | ||||
|         <para><programlisting><bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor"> | ||||
|   <property name="authenticationManager"><ref bean="authenticationManager"/></property> | ||||
|   <property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property> | ||||
|   <property name="objectDefinitionSource"> | ||||
|     <value> | ||||
|       CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON | ||||
|       PATTERN_TYPE_APACHE_ANT | ||||
|       /index.jsp=ROLE_ANONYMOUS,ROLE_USER | ||||
|       /hello.htm=ROLE_ANONYMOUS,ROLE_USER | ||||
|       /logoff.jsp=ROLE_ANONYMOUS,ROLE_USER | ||||
|       /acegilogin.jsp=ROLE_ANONYMOUS,ROLE_USER | ||||
|       /**=ROLE_USER | ||||
|     </value> | ||||
|   </property> | ||||
| </bean></programlisting>Rounding out the anonymous authentication | ||||
|         discussion is the <literal>AuthenticationTrustResolver</literal> | ||||
|         interface, with its corresponding | ||||
|         <literal>AuthenticationTrustResolverImpl</literal> implementation. | ||||
|         This interface provides an | ||||
|         <literal>isAnonymous(Authentication)</literal> method, which allows | ||||
|         interested classes to take into account this special type of | ||||
|         authentication status. The | ||||
|         <literal>SecurityEnforcementFilter</literal> uses this interface in | ||||
|         processing <literal>AccessDeniedException</literal>s. If an | ||||
|         <literal>AccessDeniedException</literal> is thrown, and the | ||||
|         authentication is of an anonymous type, instead of throwing a 403 | ||||
|         (forbidden) response, the filter will instead commence the | ||||
|         <literal>AuthenticationEntryPoint</literal> so the principal can | ||||
|         authenticate properly. This is a necessary distinction, otherwise | ||||
|         principals would always be deemed "authenticated" and never be given | ||||
|         an opportunity to login via form, basic, digest or some other normal | ||||
|         authentication mechanism.</para> | ||||
|       </sect2> | ||||
| 
 | ||||
|       <sect2 id="security-ui-well-known"> | ||||
|         <title>Well-Known Locations</title> | ||||
| 
 | ||||
| @ -4393,6 +4482,13 @@ INSERT INTO acl_permission VALUES (null, 6, 'scott', 1);</programlisting></para> | ||||
|             container</para> | ||||
|           </listitem> | ||||
| 
 | ||||
|           <listitem> | ||||
|             <para><literal>AnonymousProcessingFilter</literal>, so that if no | ||||
|             earlier authentication processing mechanism updated the | ||||
|             <literal>ContextHolder</literal>, an anonymous | ||||
|             <literal>Authentication</literal> object will be put there</para> | ||||
|           </listitem> | ||||
| 
 | ||||
|           <listitem> | ||||
|             <para><literal>SecurityEnforcementFilter</literal>, to protect web | ||||
|             URIs and catch any Acegi Security exceptions so that an | ||||
|  | ||||
| @ -21,7 +21,7 @@ | ||||
|          <value> | ||||
| 		    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON | ||||
| 		    PATTERN_TYPE_APACHE_ANT | ||||
|             /**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,basicProcessingFilter,securityEnforcementFilter | ||||
|             /**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,basicProcessingFilter,anonymousProcessingFilter,securityEnforcementFilter | ||||
|          </value> | ||||
|       </property> | ||||
|     </bean> | ||||
| @ -32,6 +32,7 @@ | ||||
|       <property name="providers"> | ||||
|          <list> | ||||
|             <ref local="daoAuthenticationProvider"/> | ||||
|             <ref local="anonymousAuthenticationProvider"/> | ||||
|          </list> | ||||
|       </property> | ||||
|    </bean> | ||||
| @ -75,6 +76,15 @@ | ||||
|       <property name="realmName"><value>Contacts Realm</value></property> | ||||
|    </bean> | ||||
| 
 | ||||
|    <bean id="anonymousProcessingFilter" class="net.sf.acegisecurity.providers.anonymous.AnonymousProcessingFilter"> | ||||
|       <property name="key"><value>foobar</value></property> | ||||
|       <property name="userAttribute"><value>anonymousUser,ROLE_ANONYMOUS</value></property> | ||||
|    </bean> | ||||
| 
 | ||||
|    <bean id="anonymousAuthenticationProvider" class="net.sf.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider"> | ||||
|       <property name="key"><value>foobar</value></property> | ||||
|    </bean> | ||||
| 
 | ||||
|    <bean id="httpSessionContextIntegrationFilter" class="net.sf.acegisecurity.context.HttpSessionContextIntegrationFilter"> | ||||
|       <property name="context"><value>net.sf.acegisecurity.context.security.SecureContextImpl</value></property> | ||||
|    </bean> | ||||
| @ -146,33 +156,14 @@ | ||||
|       <property name="objectDefinitionSource"> | ||||
|          <value> | ||||
| 			    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON | ||||
| 				\A/secure/super.*\Z=ROLE_WE_DONT_HAVE | ||||
| 				\A/secure/.*\Z=ROLE_SUPERVISOR,ROLE_USER | ||||
| 			    PATTERN_TYPE_APACHE_ANT | ||||
| 			    /index.jsp=ROLE_ANONYMOUS,ROLE_USER | ||||
| 			    /hello.htm=ROLE_ANONYMOUS,ROLE_USER | ||||
| 			    /logoff.jsp=ROLE_ANONYMOUS,ROLE_USER | ||||
| 			    /acegilogin.jsp=ROLE_ANONYMOUS,ROLE_USER | ||||
| 				/**=ROLE_USER | ||||
|          </value> | ||||
|       </property> | ||||
|    </bean> | ||||
| 
 | ||||
|    <!-- BASIC Regular Expression Syntax (for beginners): | ||||
| 
 | ||||
|         \A means the start of the string (ie the beginning of the URL) | ||||
|         \Z means the end of the string (ie the end of the URL) | ||||
|         .  means any single character | ||||
|         *  means null or any number of repetitions of the last expression (so .* means zero or more characters) | ||||
| 
 | ||||
|         Some examples: | ||||
| 
 | ||||
|         Expression:   \A/my/directory/.*\Z | ||||
|         Would match:    /my/directory/ | ||||
|                         /my/directory/hello.html | ||||
| 
 | ||||
|         Expression:   \A/.*\Z | ||||
|         Would match:    /hello.html | ||||
|                         / | ||||
| 
 | ||||
|         Expression:   \A/.*/secret.html\Z | ||||
|         Would match:    /some/directory/secret.html | ||||
|                         /another/secret.html | ||||
|         Not match:      /anothersecret.html (missing required /) | ||||
|    --> | ||||
| 
 | ||||
| </beans> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user