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