    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
    16  package org.acegisecurity.intercept.web;
    17
    18  import junit.framework.TestCase;
    19
    20  import org.acegisecurity.AccessDeniedException;
    21  import org.acegisecurity.BadCredentialsException;
    22  import org.acegisecurity.GrantedAuthority;
    23  import org.acegisecurity.GrantedAuthorityImpl;
    24  import org.acegisecurity.MockAuthenticationEntryPoint;
    25  import org.acegisecurity.MockPortResolver;
    26  import org.acegisecurity.context.SecurityContextHolder;
    27  import org.acegisecurity.context.SecurityContextImpl;
    28  import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
    29  import org.acegisecurity.ui.webapp.AuthenticationProcessingFilter;
    30
    31  import org.springframework.mock.web.MockHttpServletRequest;
    32  import org.springframework.mock.web.MockHttpServletResponse;
    33
    34  import java.io.IOException;
    35
    36  import javax.servlet.FilterChain;
    37  import javax.servlet.ServletException;
    38  import javax.servlet.ServletRequest;
    39  import javax.servlet.ServletResponse;
    40
    41
    42  /***
    43   * Tests {@link SecurityEnforcementFilter}.
    44   *
    45   * @author Ben Alex
    46   * @version $Id: SecurityEnforcementFilterTests.java,v 1.16 2005/11/17 00:55:50 benalex Exp $
    47   */
    48  public class SecurityEnforcementFilterTests extends TestCase {
    49      //~ Constructors ===========================================================
    50
    51      public SecurityEnforcementFilterTests() {
    52          super();
    53      }
    54
    55      public SecurityEnforcementFilterTests(String arg0) {
    56          super(arg0);
    57      }
    58
    59      //~ Methods ================================================================
    60
    61      public final void setUp() throws Exception {
    62          super.setUp();
    63      }
    64
    65      public static void main(String[] args) {
    66          junit.textui.TestRunner.run(SecurityEnforcementFilterTests.class);
    67      }
    68
    69      public void testAccessDeniedWhenAnonymous() throws Exception {
    70          // Setup our HTTP request
    71          MockHttpServletRequest request = new MockHttpServletRequest();
    72          request.setServletPath("/secure/page.html");
    73          request.setServerPort(80);
    74          request.setScheme("http");
    75          request.setServerName("www.example.com");
    76          request.setContextPath("/mycontext");
    77          request.setRequestURI("/mycontext/secure/page.html");
    78
    79          // Setup our expectation that the filter chain will not be invoked, as access is denied
    80          MockFilterChain chain = new MockFilterChain(false);
    81
    82          // Setup the FilterSecurityInterceptor thrown an access denied exception
    83          MockFilterSecurityInterceptor interceptor = new MockFilterSecurityInterceptor(true,
    84                  false, false, false);
    85
    86          // Setup SecurityContextHolder, as filter needs to check if user is anonymous
    87          SecurityContextHolder.getContext().setAuthentication(new AnonymousAuthenticationToken(
    88                  "ignored", "ignored",
    89                  new GrantedAuthority[] {new GrantedAuthorityImpl("IGNORED")}));
    90
    91          // Test
    92          SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
    93          filter.setFilterSecurityInterceptor(interceptor);
    94          filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(
    95                  "/login.jsp"));
    96
    97          MockHttpServletResponse response = new MockHttpServletResponse();
    98          filter.doFilter(request, response, chain);
    99          assertEquals("/mycontext/login.jsp", response.getRedirectedUrl());
    100         assertEquals("http://www.example.com/mycontext/secure/page.html",
    101             request.getSession().getAttribute(AuthenticationProcessingFilter.ACEGI_SECURITY_TARGET_URL_KEY));
    102     }
    103
    104     public void testAccessDeniedWhenNonAnonymous() throws Exception {
    105         // Setup our HTTP request
    106         MockHttpServletRequest request = new MockHttpServletRequest();
    107         request.setServletPath("/secure/page.html");
    108
    109         // Setup our expectation that the filter chain will not be invoked, as access is denied
    110         MockFilterChain chain = new MockFilterChain(false);
    111
    112         // Setup the FilterSecurityInterceptor thrown an access denied exception
    113         MockFilterSecurityInterceptor interceptor = new MockFilterSecurityInterceptor(true,
    114                 false, false, false);
    115
    116         // Setup SecurityContextHolder, as filter needs to check if user is anonymous
    117         SecurityContextHolder.getContext().setAuthentication(null);
    118
    119         // Test
    120         SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
    121         filter.setFilterSecurityInterceptor(interceptor);
    122         filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(
    123                 "/login.jsp"));
    124
    125         MockHttpServletResponse response = new MockHttpServletResponse();
    126         filter.doFilter(request, response, chain);
    127         assertEquals(403, response.getStatus());
    128         assertEquals(AccessDeniedException.class,
    129             request.getSession()
    130                    .getAttribute(SecurityEnforcementFilter.ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY)
    131                    .getClass());
    132     }
    133
    134     public void testDoFilterWithNonHttpServletRequestDetected()
    135         throws Exception {
    136         SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
    137
    138         try {
    139             filter.doFilter(null, new MockHttpServletResponse(),
    140                 new MockFilterChain());
    141             fail("Should have thrown ServletException");
    142         } catch (ServletException expected) {
    143             assertEquals("HttpServletRequest required", expected.getMessage());
    144         }
    145     }
    146
    147     public void testDoFilterWithNonHttpServletResponseDetected()
    148         throws Exception {
    149         SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
    150
    151         try {
    152             filter.doFilter(new MockHttpServletRequest(null, null), null,
    153                 new MockFilterChain());
    154             fail("Should have thrown ServletException");
    155         } catch (ServletException expected) {
    156             assertEquals("HttpServletResponse required", expected.getMessage());
    157         }
    158     }
    159
    160     public void testGettersSetters() {
    161         SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
    162         filter.setFilterSecurityInterceptor(new MockFilterSecurityInterceptor(
    163                 false, false, false, false));
    164         assertTrue(filter.getFilterSecurityInterceptor() != null);
    165
    166         filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(
    167                 "/login.jsp"));
    168         assertTrue(filter.getAuthenticationEntryPoint() != null);
    169
    170         filter.setPortResolver(new MockPortResolver(80, 443));
    171         assertTrue(filter.getPortResolver() != null);
    172     }
    173
    174     public void testRedirectedToLoginFormAndSessionShowsOriginalTargetWhenAuthenticationException()
    175         throws Exception {
    176         // Setup our HTTP request
    177         MockHttpServletRequest request = new MockHttpServletRequest();
    178         request.setServletPath("/secure/page.html");
    179         request.setServerPort(80);
    180         request.setScheme("http");
    181         request.setServerName("www.example.com");
    182         request.setContextPath("/mycontext");
    183         request.setRequestURI("/mycontext/secure/page.html");
    184
    185         // Setup our expectation that the filter chain will not be invoked, as access is denied
    186         MockFilterChain chain = new MockFilterChain(false);
    187
    188         // Setup the FilterSecurityInterceptor thrown an authentication failure exceptions
    189         MockFilterSecurityInterceptor interceptor = new MockFilterSecurityInterceptor(false,
    190                 true, false, false);
    191
    192         // Test
    193         SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
    194         filter.setFilterSecurityInterceptor(interceptor);
    195         filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(
    196                 "/login.jsp"));
    197         filter.setPortResolver(new MockPortResolver(80, 443));
    198         filter.afterPropertiesSet();
    199
    200         MockHttpServletResponse response = new MockHttpServletResponse();
    201         filter.doFilter(request, response, chain);
    202         assertEquals("/mycontext/login.jsp", response.getRedirectedUrl());
    203         assertEquals("http://www.example.com/mycontext/secure/page.html",
    204             request.getSession().getAttribute(AuthenticationProcessingFilter.ACEGI_SECURITY_TARGET_URL_KEY));
    205     }
    206
    207     public void testRedirectedToLoginFormAndSessionShowsOriginalTargetWithExoticPortWhenAuthenticationException()
    208         throws Exception {
    209         // Setup our HTTP request
    210         MockHttpServletRequest request = new MockHttpServletRequest();
    211         request.setServletPath("/secure/page.html");
    212         request.setServerPort(8080);
    213         request.setScheme("http");
    214         request.setServerName("www.example.com");
    215         request.setContextPath("/mycontext");
    216         request.setRequestURI("/mycontext/secure/page.html");
    217
    218         // Setup our expectation that the filter chain will not be invoked, as access is denied
    219         MockFilterChain chain = new MockFilterChain(false);
    220
    221         // Setup the FilterSecurityInterceptor thrown an authentication failure exceptions
    222         MockFilterSecurityInterceptor interceptor = new MockFilterSecurityInterceptor(false,
    223                 true, false, false);
    224
    225         // Test
    226         SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
    227         filter.setFilterSecurityInterceptor(interceptor);
    228         filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(
    229                 "/login.jsp"));
    230         filter.setPortResolver(new MockPortResolver(8080, 8443));
    231         filter.afterPropertiesSet();
    232
    233         MockHttpServletResponse response = new MockHttpServletResponse();
    234         filter.doFilter(request, response, chain);
    235         assertEquals("/mycontext/login.jsp", response.getRedirectedUrl());
    236         assertEquals("http://www.example.com:8080/mycontext/secure/page.html",
    237             request.getSession().getAttribute(AuthenticationProcessingFilter.ACEGI_SECURITY_TARGET_URL_KEY));
    238     }
    239
    240     public void testStartupDetectsMissingAuthenticationEntryPoint()
    241         throws Exception {
    242         SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
    243         filter.setFilterSecurityInterceptor(new MockFilterSecurityInterceptor(
    244                 false, false, false, false));
    245
    246         try {
    247             filter.afterPropertiesSet();
    248             fail("Should have thrown IllegalArgumentException");
    249         } catch (IllegalArgumentException expected) {
    250             assertEquals("authenticationEntryPoint must be specified",
    251                 expected.getMessage());
    252         }
    253     }
    254
    255     public void testStartupDetectsMissingFilterSecurityInterceptor()
    256         throws Exception {
    257         SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
    258         filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(
    259                 "/login.jsp"));
    260
    261         try {
    262             filter.afterPropertiesSet();
    263             fail("Should have thrown IllegalArgumentException");
    264         } catch (IllegalArgumentException expected) {
    265             assertEquals("filterSecurityInterceptor must be specified",
    266                 expected.getMessage());
    267         }
    268     }
    269
    270     public void testStartupDetectsMissingPortResolver()
    271         throws Exception {
    272         SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
    273         filter.setFilterSecurityInterceptor(new MockFilterSecurityInterceptor(
    274                 false, false, false, false));
    275         filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(
    276                 "/login.jsp"));
    277         filter.setPortResolver(null);
    278
    279         try {
    280             filter.afterPropertiesSet();
    281             fail("Should have thrown IllegalArgumentException");
    282         } catch (IllegalArgumentException expected) {
    283             assertEquals("portResolver must be specified", expected.getMessage());
    284         }
    285     }
    286
    287     public void testSuccessfulAccessGrant() throws Exception {
    288         // Setup our HTTP request
    289         MockHttpServletRequest request = new MockHttpServletRequest();
    290         request.setServletPath("/secure/page.html");
    291
    292         // Setup our expectation that the filter chain will be invoked, as access is granted
    293         MockFilterChain chain = new MockFilterChain(true);
    294
    295         // Setup the FilterSecurityInterceptor to not thrown any exceptions
    296         MockFilterSecurityInterceptor interceptor = new MockFilterSecurityInterceptor(false,
    297                 false, false, false);
    298
    299         // Test
    300         SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
    301         filter.setFilterSecurityInterceptor(interceptor);
    302         filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(
    303                 "/login.jsp"));
    304
    305         MockHttpServletResponse response = new MockHttpServletResponse();
    306         filter.doFilter(request, response, chain);
    307     }
    308
    309     public void testSuccessfulStartupAndShutdownDown()
    310         throws Exception {
    311         SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
    312
    313         filter.init(null);
    314         filter.destroy();
    315         assertTrue(true);
    316     }
    317
    318     public void testThrowIOException() throws Exception {
    319         SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
    320
    321         filter.setFilterSecurityInterceptor(new MockFilterSecurityInterceptor(
    322                 false, false, false, true));
    323
    324         filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(""));
    325
    326         filter.afterPropertiesSet();
    327
    328         try {
    329             filter.doFilter(new MockHttpServletRequest(),
    330                 new MockHttpServletResponse(), new MockFilterChain(false));
    331             fail("Should have thrown IOException");
    332         } catch (IOException e) {
    333             assertNull("The IOException thrown should not have been wrapped",
    334                 e.getCause());
    335         }
    336     }
    337
    338     public void testThrowServletException() throws Exception {
    339         SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
    340
    341         filter.setFilterSecurityInterceptor(new MockFilterSecurityInterceptor(
    342                 false, false, true, false));
    343
    344         filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(""));
    345
    346         filter.afterPropertiesSet();
    347
    348         try {
    349             filter.doFilter(new MockHttpServletRequest(),
    350                 new MockHttpServletResponse(), new MockFilterChain(false));
    351             fail("Should have thrown ServletException");
    352         } catch (ServletException e) {
    353             assertNull("The ServletException thrown should not have been wrapped",
    354                 e.getCause());
    355         }
    356     }
    357
    358     protected void tearDown() throws Exception {
    359         super.tearDown();
    360         SecurityContextHolder.setContext(new SecurityContextImpl());
    361     }
    362
    363     //~ Inner Classes ==========================================================
    364
    365     private class MockFilterChain implements FilterChain {
    366         private boolean expectToProceed;
    367
    368         public MockFilterChain(boolean expectToProceed) {
    369             this.expectToProceed = expectToProceed;
    370         }
    371
    372         private MockFilterChain() {
    373             super();
    374         }
    375
    376         public void doFilter(ServletRequest request, ServletResponse response)
    377             throws IOException, ServletException {
    378             if (expectToProceed) {
    379                 assertTrue(true);
    380             } else {
    381                 fail("Did not expect filter chain to proceed");
    382             }
    383         }
    384     }
    385
    386     private class MockFilterSecurityInterceptor
    387         extends FilterSecurityInterceptor {
    388         private boolean throwAccessDenied;
    389         private boolean throwAuthenticationFailure;
    390         private boolean throwIOException;
    391         private boolean throwServletException;
    392
    393         public MockFilterSecurityInterceptor(boolean throwAccessDenied,
    394             boolean throwAuthenticationFailure, boolean throwServletException,
    395             boolean throwIOException) {
    396             this.throwAccessDenied = throwAccessDenied;
    397             this.throwAuthenticationFailure = throwAuthenticationFailure;
    398             this.throwServletException = throwServletException;
    399             this.throwIOException = throwIOException;
    400         }
    401
    402         public void invoke(FilterInvocation fi) throws Throwable {
    403             if (throwAccessDenied) {
    404                 throw new AccessDeniedException("As requested");
    405             }
    406
    407             if (throwAuthenticationFailure) {
    408                 throw new BadCredentialsException("As requested");
    409             }
    410
    411             if (throwServletException) {
    412                 throw new ServletException("As requested");
    413             }
    414
    415             if (throwIOException) {
    416                 throw new IOException("As requested");
    417             }
    418
    419             fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
    420         }
    421     }
    422 }