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 }