Clover coverage report - Acegi Security System for Spring - 1.0.0-RC1
Coverage timestamp: Mon Dec 5 2005 09:05:15 EST
file stats: LOC: 289   Methods: 16
NCLOC: 160   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
SecurityEnforcementFilter.java 69.2% 79.2% 75% 75.8%
coverage coverage
 1    /* Copyright 2004, 2005 Acegi Technology Pty Limited
 2    *
 3    * Licensed under the Apache License, Version 2.0 (the "License");
 4    * you may not use this file except in compliance with the License.
 5    * You may obtain a copy of the License at
 6    *
 7    * http://www.apache.org/licenses/LICENSE-2.0
 8    *
 9    * Unless required by applicable law or agreed to in writing, software
 10    * distributed under the License is distributed on an "AS IS" BASIS,
 11    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12    * See the License for the specific language governing permissions and
 13    * limitations under the License.
 14    */
 15    package org.acegisecurity.intercept.web;
 16   
 17    import org.acegisecurity.AccessDeniedException;
 18    import org.acegisecurity.AuthenticationException;
 19    import org.acegisecurity.AuthenticationTrustResolver;
 20    import org.acegisecurity.AuthenticationTrustResolverImpl;
 21    import org.acegisecurity.InsufficientAuthenticationException;
 22    import org.acegisecurity.context.SecurityContextHolder;
 23    import org.acegisecurity.ui.AbstractProcessingFilter;
 24    import org.acegisecurity.util.PortResolver;
 25    import org.acegisecurity.util.PortResolverImpl;
 26   
 27    import org.apache.commons.logging.Log;
 28    import org.apache.commons.logging.LogFactory;
 29   
 30    import org.springframework.beans.factory.InitializingBean;
 31   
 32    import org.springframework.util.Assert;
 33   
 34    import java.io.IOException;
 35   
 36    import javax.servlet.Filter;
 37    import javax.servlet.FilterChain;
 38    import javax.servlet.FilterConfig;
 39    import javax.servlet.ServletException;
 40    import javax.servlet.ServletRequest;
 41    import javax.servlet.ServletResponse;
 42    import javax.servlet.http.HttpServletRequest;
 43    import javax.servlet.http.HttpServletResponse;
 44   
 45   
 46    /**
 47    * Wraps requests to the {@link FilterSecurityInterceptor}.
 48    *
 49    * <p>
 50    * This filter is necessary because it provides the bridge between incoming
 51    * requests and the <code>FilterSecurityInterceptor</code> instance.
 52    * </p>
 53    *
 54    * <p>
 55    * If an {@link AuthenticationException} is detected, the filter will launch
 56    * the <code>authenticationEntryPoint</code>. This allows common handling of
 57    * authentication failures originating from any subclass of {@link
 58    * org.acegisecurity.intercept.AbstractSecurityInterceptor}.
 59    * </p>
 60    *
 61    * <p>
 62    * If an {@link AccessDeniedException} is detected, the filter will determine
 63    * whether or not the user is an anonymous user. If they are an anonymous
 64    * user, the <code>authenticationEntryPoint</code> will be launched. If they
 65    * are not an anonymous user, the filter will respond with a
 66    * <code>HttpServletResponse.SC_FORBIDDEN</code> (403 error). In addition,
 67    * the <code>AccessDeniedException</code> itself will be placed in the
 68    * <code>HttpSession</code> attribute keyed against {@link
 69    * #ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY} (to allow access to the stack
 70    * trace etc). Again, this allows common access denied handling irrespective
 71    * of the originating security interceptor.
 72    * </p>
 73    *
 74    * <p>
 75    * To use this filter, it is necessary to specify the following properties:
 76    * </p>
 77    *
 78    * <ul>
 79    * <li>
 80    * <code>filterSecurityInterceptor</code> indicates the
 81    * <code>FilterSecurityInterceptor</code> to delegate HTTP security decisions
 82    * to.
 83    * </li>
 84    * <li>
 85    * <code>authenticationEntryPoint</code> indicates the handler that should
 86    * commence the authentication process if an
 87    * <code>AuthenticationException</code> is detected. Note that this may also
 88    * switch the current protocol from http to https for an SSL login.
 89    * </li>
 90    * <li>
 91    * <code>portResolver</code> is used to determine the "real" port that a
 92    * request was received on.
 93    * </li>
 94    * </ul>
 95    *
 96    * <P>
 97    * <B>Do not use this class directly.</B> Instead configure
 98    * <code>web.xml</code> to use the {@link
 99    * org.acegisecurity.util.FilterToBeanProxy}.
 100    * </p>
 101    *
 102    * @author Ben Alex
 103    * @author colin sampaleanu
 104    * @version $Id: SecurityEnforcementFilter.java,v 1.22 2005/11/25 04:38:18 benalex Exp $
 105    */
 106    public class SecurityEnforcementFilter implements Filter, InitializingBean {
 107    private static final Log logger = LogFactory.getLog(SecurityEnforcementFilter.class);
 108    public static final String ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY = "ACEGI_SECURITY_403_EXCEPTION";
 109    private AuthenticationEntryPoint authenticationEntryPoint;
 110    private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
 111    private FilterSecurityInterceptor filterSecurityInterceptor;
 112    private PortResolver portResolver = new PortResolverImpl();
 113    private boolean createSessionAllowed = true;
 114   
 115  10 public void setAuthenticationEntryPoint(
 116    AuthenticationEntryPoint authenticationEntryPoint) {
 117  10 this.authenticationEntryPoint = authenticationEntryPoint;
 118    }
 119   
 120  1 public AuthenticationEntryPoint getAuthenticationEntryPoint() {
 121  1 return authenticationEntryPoint;
 122    }
 123   
 124  0 public void setAuthenticationTrustResolver(
 125    AuthenticationTrustResolver authenticationTrustResolver) {
 126  0 this.authenticationTrustResolver = authenticationTrustResolver;
 127    }
 128   
 129    /**
 130    * If <code>true</code>, indicates that <code>SecurityEnforcementFilter</code> is permitted
 131    * to store the target URL and exception information in the <code>HttpSession</code> (the
 132    * default). In situations where you do not wish to unnecessarily create <code>HttpSession</code>s
 133    * - because the user agent will know the failed URL, such as with BASIC or Digest authentication
 134    * - you may wish to set this property to <code>false</code>. Remember to also set the
 135    * {@link org.acegisecurity.context.HttpSessionContextIntegrationFilter#allowSessionCreation}
 136    * to <code>false</code> if you set this property to <code>false</code>.
 137    *
 138    * @return <code>true</code> if the <code>HttpSession</code> will be used to store information
 139    * about the failed request, <code>false</code> if the <code>HttpSession</code> will not be
 140    * used
 141    */
 142  0 public boolean isCreateSessionAllowed() {
 143  0 return createSessionAllowed;
 144    }
 145   
 146  0 public void setCreateSessionAllowed(boolean createSessionAllowed) {
 147  0 this.createSessionAllowed = createSessionAllowed;
 148    }
 149   
 150  0 public AuthenticationTrustResolver getAuthenticationTrustResolver() {
 151  0 return authenticationTrustResolver;
 152    }
 153   
 154  10 public void setFilterSecurityInterceptor(
 155    FilterSecurityInterceptor filterSecurityInterceptor) {
 156  10 this.filterSecurityInterceptor = filterSecurityInterceptor;
 157    }
 158   
 159  1 public FilterSecurityInterceptor getFilterSecurityInterceptor() {
 160  1 return filterSecurityInterceptor;
 161    }
 162   
 163  4 public void setPortResolver(PortResolver portResolver) {
 164  4 this.portResolver = portResolver;
 165    }
 166   
 167  1 public PortResolver getPortResolver() {
 168  1 return portResolver;
 169    }
 170   
 171  7 public void afterPropertiesSet() throws Exception {
 172  7 Assert.notNull(authenticationEntryPoint,
 173    "authenticationEntryPoint must be specified");
 174  6 Assert.notNull(filterSecurityInterceptor,
 175    "filterSecurityInterceptor must be specified");
 176  5 Assert.notNull(portResolver, "portResolver must be specified");
 177  4 Assert.notNull(authenticationTrustResolver,
 178    "authenticationTrustResolver must be specified");
 179    }
 180   
 181  1 public void destroy() {
 182    }
 183   
 184  9 public void doFilter(ServletRequest request, ServletResponse response,
 185    FilterChain chain) throws IOException, ServletException {
 186  9 if (!(request instanceof HttpServletRequest)) {
 187  1 throw new ServletException("HttpServletRequest required");
 188    }
 189   
 190  8 if (!(response instanceof HttpServletResponse)) {
 191  1 throw new ServletException("HttpServletResponse required");
 192    }
 193   
 194  7 FilterInvocation fi = new FilterInvocation(request, response, chain);
 195   
 196  7 try {
 197  7 filterSecurityInterceptor.invoke(fi);
 198   
 199  1 if (logger.isDebugEnabled()) {
 200  0 logger.debug("Chain processed normally");
 201    }
 202    } catch (AuthenticationException authentication) {
 203  2 if (logger.isDebugEnabled()) {
 204  0 logger.debug("Authentication exception occurred; redirecting to authentication entry point",
 205    authentication);
 206    }
 207   
 208  2 sendStartAuthentication(fi, authentication);
 209    } catch (AccessDeniedException accessDenied) {
 210  2 if (authenticationTrustResolver.isAnonymous(
 211    SecurityContextHolder.getContext().getAuthentication())) {
 212  1 if (logger.isDebugEnabled()) {
 213  0 logger.debug("Access is denied (user is anonymous); redirecting to authentication entry point",
 214    accessDenied);
 215    }
 216   
 217  1 sendStartAuthentication(fi,
 218    new InsufficientAuthenticationException(
 219    "Full authentication is required to access this resource"));
 220    } else {
 221  1 if (logger.isDebugEnabled()) {
 222  0 logger.debug("Access is denied (user is not anonymous); sending back forbidden response",
 223    accessDenied);
 224    }
 225   
 226  1 sendAccessDeniedError(fi, accessDenied);
 227    }
 228    } catch (ServletException e) {
 229  1 throw e;
 230    } catch (IOException e) {
 231  1 throw e;
 232    } catch (Throwable otherException) {
 233  0 throw new ServletException(otherException);
 234    }
 235    }
 236   
 237  1 public void init(FilterConfig filterConfig) throws ServletException {
 238    }
 239   
 240  1 protected void sendAccessDeniedError(FilterInvocation fi,
 241    AccessDeniedException accessDenied)
 242    throws ServletException, IOException {
 243  1 if (createSessionAllowed) {
 244  1 ((HttpServletRequest) fi.getRequest()).getSession().setAttribute(ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY,
 245    accessDenied);
 246    }
 247   
 248  1 ((HttpServletResponse) fi.getResponse()).sendError(HttpServletResponse.SC_FORBIDDEN,
 249    accessDenied.getMessage()); // 403
 250    }
 251   
 252  3 protected void sendStartAuthentication(FilterInvocation fi,
 253    AuthenticationException reason) throws ServletException, IOException {
 254  3 HttpServletRequest request = (HttpServletRequest) fi.getRequest();
 255   
 256  3 int port = portResolver.getServerPort(request);
 257  3 boolean includePort = true;
 258   
 259  3 if ("http".equals(request.getScheme().toLowerCase()) && (port == 80)) {
 260  2 includePort = false;
 261    }
 262   
 263  3 if ("https".equals(request.getScheme().toLowerCase()) && (port == 443)) {
 264  0 includePort = false;
 265    }
 266   
 267  3 String targetUrl = request.getScheme() + "://" +
 268  3 request.getServerName() + ((includePort) ? (":" + port) : "") +
 269    request.getContextPath() + fi.getRequestUrl();
 270   
 271  3 if (logger.isDebugEnabled()) {
 272  0 logger.debug(
 273    "Authentication entry point being called; target URL added to Session: " +
 274    targetUrl);
 275    }
 276   
 277  3 if (createSessionAllowed) {
 278  3 ((HttpServletRequest) request).getSession().setAttribute(AbstractProcessingFilter.ACEGI_SECURITY_TARGET_URL_KEY,
 279    targetUrl);
 280    }
 281   
 282    // SEC-112: Clear the SecurityContextHolder's Authentication, as the
 283    // existing Authentication is no longer considered valid
 284  3 SecurityContextHolder.getContext().setAuthentication(null);
 285   
 286  3 authenticationEntryPoint.commence(request,
 287    (HttpServletResponse) fi.getResponse(), reason);
 288    }
 289    }