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 }