    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 java.io.IOException;
    19  import java.util.Iterator;
    20
    21  import javax.servlet.FilterChain;
    22  import javax.servlet.ServletException;
    23  import javax.servlet.ServletRequest;
    24  import javax.servlet.ServletResponse;
    25
    26  import junit.framework.TestCase;
    27
    28  import org.acegisecurity.AccessDecisionManager;
    29  import org.acegisecurity.AccessDeniedException;
    30  import org.acegisecurity.Authentication;
    31  import org.acegisecurity.ConfigAttribute;
    32  import org.acegisecurity.ConfigAttributeDefinition;
    33  import org.acegisecurity.GrantedAuthority;
    34  import org.acegisecurity.GrantedAuthorityImpl;
    35  import org.acegisecurity.MockAccessDecisionManager;
    36  import org.acegisecurity.MockApplicationContext;
    37  import org.acegisecurity.MockAuthenticationManager;
    38  import org.acegisecurity.MockRunAsManager;
    39  import org.acegisecurity.RunAsManager;
    40  import org.acegisecurity.SecurityConfig;
    41  import org.acegisecurity.context.SecurityContextHolder;
    42  import org.acegisecurity.context.SecurityContextImpl;
    43  import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
    44  import org.springframework.mock.web.MockHttpServletRequest;
    45  import org.springframework.mock.web.MockHttpServletResponse;
    46
    47
    48  /***
    49   * Tests {@link FilterSecurityInterceptor}.
    50   *
    51   * @author Ben Alex
    52   * @version $Id: FilterSecurityInterceptorTests.java,v 1.12 2005/11/30 01:23:35 benalex Exp $
    53   */
    54  public class FilterSecurityInterceptorTests extends TestCase {
    55      //~ Constructors ===========================================================
    56
    57      public FilterSecurityInterceptorTests() {
    58          super();
    59      }
    60
    61      public FilterSecurityInterceptorTests(String arg0) {
    62          super(arg0);
    63      }
    64
    65      //~ Methods ================================================================
    66
    67      public final void setUp() throws Exception {
    68          super.setUp();
    69      }
    70
    71      public static void main(String[] args) {
    72          junit.textui.TestRunner.run(FilterSecurityInterceptorTests.class);
    73      }
    74
    75      public void testEnsuresAccessDecisionManagerSupportsFilterInvocationClass()
    76          throws Exception {
    77          FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
    78          interceptor.setAuthenticationManager(new MockAuthenticationManager());
    79          interceptor.setObjectDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
    80          interceptor.setRunAsManager(new MockRunAsManager());
    81
    82          interceptor.setAccessDecisionManager(new AccessDecisionManager() {
    83                  public boolean supports(Class clazz) {
    84                      return false;
    85                  }
    86
    87                  public boolean supports(ConfigAttribute attribute) {
    88                      return true;
    89                  }
    90
    91                  public void decide(Authentication authentication,
    92                      Object object, ConfigAttributeDefinition config)
    93                      throws AccessDeniedException {
    94                      throw new UnsupportedOperationException(
    95                          "mock method not implemented");
    96                  }
    97              });
    98
    99          try {
    100             interceptor.afterPropertiesSet();
    101             fail("Should have thrown IllegalArgumentException");
    102         } catch (IllegalArgumentException expected) {
    103             assertEquals("AccessDecisionManager does not support secure object class: class org.acegisecurity.intercept.web.FilterInvocation",
    104                 expected.getMessage());
    105         }
    106     }
    107
    108     public void testEnsuresRunAsManagerSupportsFilterInvocationClass()
    109         throws Exception {
    110         FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
    111         interceptor.setAccessDecisionManager(new MockAccessDecisionManager());
    112         interceptor.setAuthenticationManager(new MockAuthenticationManager());
    113         interceptor.setObjectDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
    114
    115         interceptor.setRunAsManager(new RunAsManager() {
    116                 public boolean supports(Class clazz) {
    117                     return false;
    118                 }
    119
    120                 public boolean supports(ConfigAttribute attribute) {
    121                     return true;
    122                 }
    123
    124                 public Authentication buildRunAs(
    125                     Authentication authentication, Object object,
    126                     ConfigAttributeDefinition config) {
    127                     throw new UnsupportedOperationException(
    128                         "mock method not implemented");
    129                 }
    130             });
    131
    132         try {
    133             interceptor.afterPropertiesSet();
    134             fail("Should have thrown IllegalArgumentException");
    135         } catch (IllegalArgumentException expected) {
    136             assertEquals("RunAsManager does not support secure object class: class org.acegisecurity.intercept.web.FilterInvocation",
    137                 expected.getMessage());
    138         }
    139     }
    140
    141     public void testHttpsInvocationReflectsPortNumber()
    142         throws Throwable {
    143         // Setup the FilterSecurityInterceptor
    144         FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
    145         interceptor.setAccessDecisionManager(new MockAccessDecisionManager());
    146         interceptor.setAuthenticationManager(new MockAuthenticationManager());
    147         interceptor.setRunAsManager(new MockRunAsManager());
    148         interceptor.setApplicationEventPublisher(MockApplicationContext.getContext());
    149
    150         // Setup a mock config attribute definition
    151         ConfigAttributeDefinition def = new ConfigAttributeDefinition();
    152         def.addConfigAttribute(new SecurityConfig("MOCK_OK"));
    153
    154         MockFilterInvocationDefinitionMap mockSource = new MockFilterInvocationDefinitionMap("/secure/page.html",
    155                 def);
    156         interceptor.setObjectDefinitionSource(mockSource);
    157
    158         // Setup our expectation that the filter chain will be invoked, as access is granted
    159         MockFilterChain chain = new MockFilterChain(true);
    160
    161         // Setup our HTTPS request and response
    162         MockHttpServletResponse response = new MockHttpServletResponse();
    163         MockHttpServletRequest request = new MockHttpServletRequest();
    164         request.setServletPath("/secure/page.html");
    165         request.setScheme("https");
    166         request.setServerPort(443);
    167
    168         // Setup a Context
    169         UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
    170                 "Password",
    171                 new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_OK")});
    172         SecurityContextHolder.getContext().setAuthentication(token);
    173
    174         // Create and test our secure object
    175         FilterInvocation fi = new FilterInvocation(request, response, chain);
    176         interceptor.invoke(fi);
    177
    178         // Destroy the Context
    179         SecurityContextHolder.setContext(new SecurityContextImpl());
    180     }
    181
    182     public void testNormalStartupAndGetter() throws Exception {
    183         FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
    184         interceptor.setAccessDecisionManager(new MockAccessDecisionManager());
    185         interceptor.setAuthenticationManager(new MockAuthenticationManager());
    186
    187         RegExpBasedFilterInvocationDefinitionMap fidp = new RegExpBasedFilterInvocationDefinitionMap();
    188         interceptor.setObjectDefinitionSource(fidp);
    189         interceptor.setRunAsManager(new MockRunAsManager());
    190         interceptor.afterPropertiesSet();
    191         assertTrue(true);
    192         assertEquals(fidp, interceptor.getObjectDefinitionSource());
    193     }
    194
    195     /***
    196      * We just test invocation works in a success event. There is no need to
    197      * test  access denied events as the abstract parent enforces that logic,
    198      * which is extensively tested separately.
    199      *
    200      */
    201     public void testSuccessfulInvocation() throws Throwable {
    202         // Setup the FilterSecurityInterceptor
    203         FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
    204         interceptor.setAccessDecisionManager(new MockAccessDecisionManager());
    205         interceptor.setAuthenticationManager(new MockAuthenticationManager());
    206         interceptor.setRunAsManager(new MockRunAsManager());
    207         interceptor.setApplicationEventPublisher(MockApplicationContext.getContext());
    208
    209         // Setup a mock config attribute definition
    210         ConfigAttributeDefinition def = new ConfigAttributeDefinition();
    211         def.addConfigAttribute(new SecurityConfig("MOCK_OK"));
    212
    213         MockFilterInvocationDefinitionMap mockSource = new MockFilterInvocationDefinitionMap("/secure/page.html",
    214                 def);
    215         interceptor.setObjectDefinitionSource(mockSource);
    216
    217         // Setup our expectation that the filter chain will be invoked, as access is granted
    218         MockFilterChain chain = new MockFilterChain(true);
    219
    220         // Setup our HTTP request and response
    221         MockHttpServletResponse response = new MockHttpServletResponse();
    222         MockHttpServletRequest request = new MockHttpServletRequest();
    223         request.setServletPath("/secure/page.html");
    224
    225         // Setup a Context
    226         UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
    227                 "Password",
    228                 new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_OK")});
    229         SecurityContextHolder.getContext().setAuthentication(token);
    230
    231         // Create and test our secure object
    232         FilterInvocation fi = new FilterInvocation(request, response, chain);
    233         interceptor.invoke(fi);
    234
    235         // Destroy the Context
    236         SecurityContextHolder.setContext(new SecurityContextImpl());
    237     }
    238
    239     //~ Inner Classes ==========================================================
    240
    241     private class MockFilterChain implements FilterChain {
    242         private boolean expectToProceed;
    243
    244         public MockFilterChain(boolean expectToProceed) {
    245             this.expectToProceed = expectToProceed;
    246         }
    247
    248         private MockFilterChain() {
    249             super();
    250         }
    251
    252         public void doFilter(ServletRequest request, ServletResponse response)
    253             throws IOException, ServletException {
    254             if (expectToProceed) {
    255                 assertTrue(true);
    256             } else {
    257                 fail("Did not expect filter chain to proceed");
    258             }
    259         }
    260     }
    261
    262     private class MockFilterInvocationDefinitionMap
    263         implements FilterInvocationDefinitionSource {
    264         private ConfigAttributeDefinition toReturn;
    265         private String servletPath;
    266
    267         public MockFilterInvocationDefinitionMap(String servletPath,
    268             ConfigAttributeDefinition toReturn) {
    269             this.servletPath = servletPath;
    270             this.toReturn = toReturn;
    271         }
    272
    273         private MockFilterInvocationDefinitionMap() {
    274             super();
    275         }
    276
    277         public ConfigAttributeDefinition getAttributes(Object object)
    278             throws IllegalArgumentException {
    279             FilterInvocation fi = (FilterInvocation) object;
    280
    281             if (servletPath.equals(fi.getHttpRequest().getServletPath())) {
    282                 return toReturn;
    283             } else {
    284                 return null;
    285             }
    286         }
    287
    288         public Iterator getConfigAttributeDefinitions() {
    289             return null;
    290         }
    291
    292         public boolean supports(Class clazz) {
    293             return true;
    294         }
    295     }
    296 }