001: /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
002: *
003: * Licensed under the Apache License, Version 2.0 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at
006: *
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015:
016: package org.acegisecurity.ui;
017:
018: import junit.framework.TestCase;
019: import org.acegisecurity.AccountExpiredException;
020: import org.acegisecurity.Authentication;
021: import org.acegisecurity.AuthenticationException;
022: import org.acegisecurity.BadCredentialsException;
023: import org.acegisecurity.GrantedAuthority;
024: import org.acegisecurity.GrantedAuthorityImpl;
025: import org.acegisecurity.MockAuthenticationManager;
026: import org.acegisecurity.context.SecurityContextHolder;
027: import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
028: import org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices;
029: import org.acegisecurity.ui.savedrequest.SavedRequest;
030: import org.acegisecurity.util.PortResolverImpl;
031: import org.springframework.mock.web.MockFilterConfig;
032: import org.springframework.mock.web.MockHttpServletRequest;
033: import org.springframework.mock.web.MockHttpServletResponse;
034:
035: import javax.servlet.Filter;
036: import javax.servlet.FilterChain;
037: import javax.servlet.FilterConfig;
038: import javax.servlet.ServletException;
039: import javax.servlet.ServletRequest;
040: import javax.servlet.ServletResponse;
041: import javax.servlet.http.HttpServletRequest;
042: import javax.servlet.http.HttpServletResponse;
043: import java.io.IOException;
044: import java.util.Properties;
045:
046: /**
047: * Tests {@link AbstractProcessingFilter}.
048: *
049: * @author Ben Alex
050: * @version $Id: AbstractProcessingFilterTests.java 1861 2007-05-25 01:24:07Z benalex $
051: */
052: public class AbstractProcessingFilterTests extends TestCase {
053: //~ Constructors ===================================================================================================
054:
055: public AbstractProcessingFilterTests() {
056: super ();
057: }
058:
059: public AbstractProcessingFilterTests(String arg0) {
060: super (arg0);
061: }
062:
063: //~ Methods ========================================================================================================
064:
065: private MockHttpServletRequest createMockRequest() {
066: MockHttpServletRequest request = new MockHttpServletRequest();
067:
068: request.setServletPath("/j_mock_post");
069: request.setScheme("http");
070: request.setServerName("www.example.com");
071: request.setRequestURI("/mycontext/j_mock_post");
072: request.setContextPath("/mycontext");
073:
074: return request;
075: }
076:
077: private void executeFilterInContainerSimulator(
078: FilterConfig filterConfig, Filter filter,
079: ServletRequest request, ServletResponse response,
080: FilterChain filterChain) throws ServletException,
081: IOException {
082: filter.init(filterConfig);
083: filter.doFilter(request, response, filterChain);
084: filter.destroy();
085: }
086:
087: public static void main(String[] args) {
088: junit.textui.TestRunner
089: .run(AbstractProcessingFilterTests.class);
090: }
091:
092: private SavedRequest makeSavedRequestForUrl() {
093: MockHttpServletRequest request = createMockRequest();
094: request.setServletPath("/some_protected_file.html");
095: request.setScheme("http");
096: request.setServerName("www.example.com");
097: request.setRequestURI("/mycontext/some_protected_file.html");
098:
099: return new SavedRequest(request, new PortResolverImpl());
100: }
101:
102: protected void setUp() throws Exception {
103: super .setUp();
104: SecurityContextHolder.clearContext();
105: }
106:
107: protected void tearDown() throws Exception {
108: super .tearDown();
109: SecurityContextHolder.clearContext();
110: }
111:
112: public void testDefaultProcessesFilterUrlWithPathParameter() {
113: MockHttpServletRequest request = createMockRequest();
114: MockHttpServletResponse response = new MockHttpServletResponse();
115: MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter();
116: filter.setFilterProcessesUrl("/j_acegi_security_check");
117:
118: request
119: .setRequestURI("/mycontext/j_acegi_security_check;jsessionid=I8MIONOSTHOR");
120: assertTrue(filter.requiresAuthentication(request, response));
121: }
122:
123: public void testDoFilterWithNonHttpServletRequestDetected()
124: throws Exception {
125: AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
126:
127: try {
128: filter.doFilter(null, new MockHttpServletResponse(),
129: new MockFilterChain());
130: fail("Should have thrown ServletException");
131: } catch (ServletException expected) {
132: assertEquals("Can only process HttpServletRequest",
133: expected.getMessage());
134: }
135: }
136:
137: public void testDoFilterWithNonHttpServletResponseDetected()
138: throws Exception {
139: AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
140:
141: try {
142: filter.doFilter(new MockHttpServletRequest(null, null),
143: null, new MockFilterChain());
144: fail("Should have thrown ServletException");
145: } catch (ServletException expected) {
146: assertEquals("Can only process HttpServletResponse",
147: expected.getMessage());
148: }
149: }
150:
151: public void testFailedAuthenticationRedirectsAppropriately()
152: throws Exception {
153: // Setup our HTTP request
154: MockHttpServletRequest request = createMockRequest();
155:
156: // Setup our filter configuration
157: MockFilterConfig config = new MockFilterConfig(null, null);
158:
159: // Setup our expectation that the filter chain will not be invoked, as we redirect to authenticationFailureUrl
160: MockFilterChain chain = new MockFilterChain(false);
161: MockHttpServletResponse response = new MockHttpServletResponse();
162:
163: // Setup our test object, to deny access
164: MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(
165: false);
166: filter.setAuthenticationFailureUrl("/failed.jsp");
167:
168: // Test
169: executeFilterInContainerSimulator(config, filter, request,
170: response, chain);
171:
172: assertEquals("/mycontext/failed.jsp", response
173: .getRedirectedUrl());
174: assertNull(SecurityContextHolder.getContext()
175: .getAuthentication());
176:
177: //Prepare again, this time using the exception mapping
178: filter = new MockAbstractProcessingFilter(
179: new AccountExpiredException("You're account is expired"));
180: filter.setAuthenticationFailureUrl("/failed.jsp");
181:
182: Properties exceptionMappings = filter.getExceptionMappings();
183: exceptionMappings.setProperty(AccountExpiredException.class
184: .getName(), "/accountExpired.jsp");
185: filter.setExceptionMappings(exceptionMappings);
186: response = new MockHttpServletResponse();
187:
188: // Test
189: executeFilterInContainerSimulator(config, filter, request,
190: response, chain);
191:
192: assertEquals("/mycontext/accountExpired.jsp", response
193: .getRedirectedUrl());
194: assertNull(SecurityContextHolder.getContext()
195: .getAuthentication());
196: assertEquals(8 * 1024, response.getBufferSize());
197: }
198:
199: public void testFilterProcessesUrlVariationsRespected()
200: throws Exception {
201: // Setup our HTTP request
202: MockHttpServletRequest request = createMockRequest();
203: request.setServletPath("/j_OTHER_LOCATION");
204: request.setRequestURI("/mycontext/j_OTHER_LOCATION");
205:
206: // Setup our filter configuration
207: MockFilterConfig config = new MockFilterConfig(null, null);
208:
209: // Setup our expectation that the filter chain will not be invoked, as we redirect to defaultTargetUrl
210: MockFilterChain chain = new MockFilterChain(false);
211: MockHttpServletResponse response = new MockHttpServletResponse();
212:
213: // Setup our test object, to grant access
214: MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(
215: true);
216: filter.setFilterProcessesUrl("/j_OTHER_LOCATION");
217: filter.setDefaultTargetUrl("/logged_in.jsp");
218:
219: // Test
220: executeFilterInContainerSimulator(config, filter, request,
221: response, chain);
222: assertEquals("/mycontext/logged_in.jsp", response
223: .getRedirectedUrl());
224: assertNotNull(SecurityContextHolder.getContext()
225: .getAuthentication());
226: assertEquals("test", SecurityContextHolder.getContext()
227: .getAuthentication().getPrincipal().toString());
228: assertEquals(8 * 1024, response.getBufferSize());
229: }
230:
231: public void testGettersSetters() {
232: AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
233: assertNotNull(filter.getRememberMeServices());
234: filter
235: .setRememberMeServices(new TokenBasedRememberMeServices());
236: assertEquals(TokenBasedRememberMeServices.class, filter
237: .getRememberMeServices().getClass());
238:
239: filter.setAuthenticationFailureUrl("/x");
240: assertEquals("/x", filter.getAuthenticationFailureUrl());
241:
242: filter
243: .setAuthenticationManager(new MockAuthenticationManager());
244: assertTrue(filter.getAuthenticationManager() != null);
245:
246: filter.setDefaultTargetUrl("/default");
247: assertEquals("/default", filter.getDefaultTargetUrl());
248:
249: filter.setFilterProcessesUrl("/p");
250: assertEquals("/p", filter.getFilterProcessesUrl());
251:
252: filter.setAuthenticationFailureUrl("/fail");
253: assertEquals("/fail", filter.getAuthenticationFailureUrl());
254: }
255:
256: public void testDefaultUrlMuststartWithSlashOrHttpScheme() {
257: AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
258:
259: filter.setDefaultTargetUrl("/acceptableRelativeUrl");
260: filter.setDefaultTargetUrl("http://some.site.org/index.html");
261: filter.setDefaultTargetUrl("https://some.site.org/index.html");
262:
263: try {
264: filter.setDefaultTargetUrl("missingSlash");
265: fail("Shouldn't accept default target without leading slash");
266: } catch (IllegalArgumentException expected) {
267: }
268: }
269:
270: public void testIgnoresAnyServletPathOtherThanFilterProcessesUrl()
271: throws Exception {
272: // Setup our HTTP request
273: MockHttpServletRequest request = createMockRequest();
274: request.setServletPath("/some.file.html");
275: request.setRequestURI("/mycontext/some.file.html");
276:
277: // Setup our filter configuration
278: MockFilterConfig config = new MockFilterConfig(null, null);
279:
280: // Setup our expectation that the filter chain will be invoked, as our request is for a page the filter isn't monitoring
281: MockFilterChain chain = new MockFilterChain(true);
282: MockHttpServletResponse response = new MockHttpServletResponse();
283:
284: // Setup our test object, to deny access
285: MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(
286: false);
287:
288: // Test
289: executeFilterInContainerSimulator(config, filter, request,
290: response, chain);
291: }
292:
293: public void testNormalOperationWithDefaultFilterProcessesUrl()
294: throws Exception {
295: // Setup our HTTP request
296: MockHttpServletRequest request = createMockRequest();
297:
298: // Setup our filter configuration
299: MockFilterConfig config = new MockFilterConfig(null, null);
300:
301: // Setup our expectation that the filter chain will not be invoked, as we redirect to defaultTargetUrl
302: MockFilterChain chain = new MockFilterChain(false);
303: MockHttpServletResponse response = new MockHttpServletResponse();
304:
305: // Setup our test object, to grant access
306: MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(
307: true);
308: filter.setFilterProcessesUrl("/j_mock_post");
309: filter.setDefaultTargetUrl("/logged_in.jsp");
310: filter.setAuthenticationFailureUrl("/failure.jsp");
311: filter.setAuthenticationManager(new MockAuthenticationManager(
312: true));
313: filter.afterPropertiesSet();
314:
315: // Test
316: executeFilterInContainerSimulator(config, filter, request,
317: response, chain);
318: assertEquals("/mycontext/logged_in.jsp", response
319: .getRedirectedUrl());
320: assertNotNull(SecurityContextHolder.getContext()
321: .getAuthentication());
322: assertEquals("test", SecurityContextHolder.getContext()
323: .getAuthentication().getPrincipal().toString());
324: assertEquals(8 * 1024, response.getBufferSize());
325: }
326:
327: public void testStartupDetectsInvalidAuthenticationFailureUrl()
328: throws Exception {
329: AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
330: filter
331: .setAuthenticationManager(new MockAuthenticationManager());
332: filter.setDefaultTargetUrl("/");
333: filter.setFilterProcessesUrl("/j_acegi_security_check");
334:
335: try {
336: filter.afterPropertiesSet();
337: fail("Should have thrown IllegalArgumentException");
338: } catch (IllegalArgumentException expected) {
339: assertEquals("authenticationFailureUrl must be specified",
340: expected.getMessage());
341: }
342: }
343:
344: public void testStartupDetectsInvalidAuthenticationManager()
345: throws Exception {
346: AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
347: filter.setAuthenticationFailureUrl("/failed.jsp");
348: filter.setDefaultTargetUrl("/");
349: filter.setFilterProcessesUrl("/j_acegi_security_check");
350:
351: try {
352: filter.afterPropertiesSet();
353: fail("Should have thrown IllegalArgumentException");
354: } catch (IllegalArgumentException expected) {
355: assertEquals("authenticationManager must be specified",
356: expected.getMessage());
357: }
358: }
359:
360: public void testStartupDetectsInvalidDefaultTargetUrl()
361: throws Exception {
362: AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
363: filter.setAuthenticationFailureUrl("/failed.jsp");
364: filter
365: .setAuthenticationManager(new MockAuthenticationManager());
366: filter.setFilterProcessesUrl("/j_acegi_security_check");
367:
368: try {
369: filter.afterPropertiesSet();
370: fail("Should have thrown IllegalArgumentException");
371: } catch (IllegalArgumentException expected) {
372: assertEquals("defaultTargetUrl must be specified", expected
373: .getMessage());
374: }
375: }
376:
377: public void testStartupDetectsInvalidFilterProcessesUrl()
378: throws Exception {
379: AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
380: filter.setAuthenticationFailureUrl("/failed.jsp");
381: filter
382: .setAuthenticationManager(new MockAuthenticationManager());
383: filter.setDefaultTargetUrl("/");
384: filter.setFilterProcessesUrl(null);
385:
386: try {
387: filter.afterPropertiesSet();
388: fail("Should have thrown IllegalArgumentException");
389: } catch (IllegalArgumentException expected) {
390: assertEquals("filterProcessesUrl must be specified",
391: expected.getMessage());
392: }
393: }
394:
395: public void testSuccessLoginThenFailureLoginResultsInSessionLosingToken()
396: throws Exception {
397: // Setup our HTTP request
398: MockHttpServletRequest request = createMockRequest();
399:
400: // Setup our filter configuration
401: MockFilterConfig config = new MockFilterConfig(null, null);
402:
403: // Setup our expectation that the filter chain will not be invoked, as we redirect to defaultTargetUrl
404: MockFilterChain chain = new MockFilterChain(false);
405: MockHttpServletResponse response = new MockHttpServletResponse();
406:
407: // Setup our test object, to grant access
408: MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(
409: true);
410: filter.setFilterProcessesUrl("/j_mock_post");
411: filter.setDefaultTargetUrl("/logged_in.jsp");
412:
413: // Test
414: executeFilterInContainerSimulator(config, filter, request,
415: response, chain);
416: assertEquals("/mycontext/logged_in.jsp", response
417: .getRedirectedUrl());
418: assertNotNull(SecurityContextHolder.getContext()
419: .getAuthentication());
420: assertEquals("test", SecurityContextHolder.getContext()
421: .getAuthentication().getPrincipal().toString());
422: assertEquals(8 * 1024, response.getBufferSize());
423:
424: // Now try again but this time have filter deny access
425: // Setup our HTTP request
426: // Setup our expectation that the filter chain will not be invoked, as we redirect to authenticationFailureUrl
427: chain = new MockFilterChain(false);
428: response = new MockHttpServletResponse();
429:
430: // Setup our test object, to deny access
431: filter = new MockAbstractProcessingFilter(false);
432: filter.setFilterProcessesUrl("/j_mock_post");
433: filter.setAuthenticationFailureUrl("/failed.jsp");
434:
435: // Test
436: executeFilterInContainerSimulator(config, filter, request,
437: response, chain);
438: assertNull(SecurityContextHolder.getContext()
439: .getAuthentication());
440: }
441:
442: public void testSuccessfulAuthenticationButWithAlwaysUseDefaultTargetUrlCausesRedirectToDefaultTargetUrl()
443: throws Exception {
444: // Setup our HTTP request
445: MockHttpServletRequest request = createMockRequest();
446: request.getSession().setAttribute(
447: AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY,
448: makeSavedRequestForUrl());
449:
450: // Setup our filter configuration
451: MockFilterConfig config = new MockFilterConfig(null, null);
452:
453: // Setup our expectation that the filter chain will be invoked, as we want to go to the location requested in the session
454: MockFilterChain chain = new MockFilterChain(true);
455: MockHttpServletResponse response = new MockHttpServletResponse();
456:
457: // Setup our test object, to grant access
458: MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(
459: true);
460: filter.setFilterProcessesUrl("/j_mock_post");
461: filter.setDefaultTargetUrl("/foobar");
462: assertFalse(filter.isAlwaysUseDefaultTargetUrl()); // check default
463: filter.setAlwaysUseDefaultTargetUrl(true);
464: assertTrue(filter.isAlwaysUseDefaultTargetUrl()); // check changed
465:
466: // Test
467: executeFilterInContainerSimulator(config, filter, request,
468: response, chain);
469: assertEquals("/mycontext/foobar", response.getRedirectedUrl());
470: assertNotNull(SecurityContextHolder.getContext()
471: .getAuthentication());
472: }
473:
474: public void testSuccessfulAuthenticationCausesRedirectToSessionSpecifiedUrl()
475: throws Exception {
476: // Setup our HTTP request
477: MockHttpServletRequest request = createMockRequest();
478: request.getSession().setAttribute(
479: AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY,
480: makeSavedRequestForUrl());
481:
482: // Setup our filter configuration
483: MockFilterConfig config = new MockFilterConfig(null, null);
484:
485: // Setup our expectation that the filter chain will be invoked, as we want to go to the location requested in the session
486: MockFilterChain chain = new MockFilterChain(true);
487: MockHttpServletResponse response = new MockHttpServletResponse();
488:
489: // Setup our test object, to grant access
490: MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(
491: true);
492: filter.setFilterProcessesUrl("/j_mock_post");
493:
494: // Test
495: executeFilterInContainerSimulator(config, filter, request,
496: response, chain);
497: assertEquals(makeSavedRequestForUrl().getFullRequestUrl(),
498: response.getRedirectedUrl());
499: assertNotNull(SecurityContextHolder.getContext()
500: .getAuthentication());
501: assertEquals(8 * 1024, response.getBufferSize());
502: }
503:
504: /**
505: * SEC-297 fix.
506: */
507: public void testFullDefaultTargetUrlDoesNotHaveContextPathPrepended()
508: throws Exception {
509: MockHttpServletRequest request = createMockRequest();
510: MockFilterConfig config = new MockFilterConfig(null, null);
511:
512: MockFilterChain chain = new MockFilterChain(true);
513: MockHttpServletResponse response = new MockHttpServletResponse();
514:
515: // Setup our test object, to grant access
516: MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(
517: true);
518: filter.setFilterProcessesUrl("/j_mock_post");
519: filter.setDefaultTargetUrl("http://monkeymachine.co.uk/");
520: filter.setAlwaysUseDefaultTargetUrl(true);
521:
522: executeFilterInContainerSimulator(config, filter, request,
523: response, chain);
524: assertEquals("http://monkeymachine.co.uk/", response
525: .getRedirectedUrl());
526: assertNotNull(SecurityContextHolder.getContext()
527: .getAuthentication());
528: }
529:
530: //~ Inner Classes ==================================================================================================
531:
532: private class MockAbstractProcessingFilter extends
533: AbstractProcessingFilter {
534: private AuthenticationException exceptionToThrow;
535: private boolean grantAccess;
536:
537: public MockAbstractProcessingFilter(boolean grantAccess) {
538: this .grantAccess = grantAccess;
539: this .exceptionToThrow = new BadCredentialsException(
540: "Mock requested to do so");
541: }
542:
543: public MockAbstractProcessingFilter(
544: AuthenticationException exceptionToThrow) {
545: this .grantAccess = false;
546: this .exceptionToThrow = exceptionToThrow;
547: }
548:
549: private MockAbstractProcessingFilter() {
550: super ();
551: }
552:
553: public Authentication attemptAuthentication(
554: HttpServletRequest request)
555: throws AuthenticationException {
556: if (grantAccess) {
557: return new UsernamePasswordAuthenticationToken(
558: "test",
559: "test",
560: new GrantedAuthority[] { new GrantedAuthorityImpl(
561: "TEST") });
562: } else {
563: throw exceptionToThrow;
564: }
565: }
566:
567: public String getDefaultFilterProcessesUrl() {
568: return "/j_mock_post";
569: }
570:
571: public void init(FilterConfig arg0) throws ServletException {
572: }
573:
574: public boolean requiresAuthentication(
575: HttpServletRequest request, HttpServletResponse response) {
576: return super .requiresAuthentication(request, response);
577: }
578: }
579:
580: private class MockFilterChain implements FilterChain {
581: private boolean expectToProceed;
582:
583: public MockFilterChain(boolean expectToProceed) {
584: this .expectToProceed = expectToProceed;
585: }
586:
587: private MockFilterChain() {
588: super ();
589: }
590:
591: public void doFilter(ServletRequest request,
592: ServletResponse response) throws IOException,
593: ServletException {
594: if (expectToProceed) {
595: assertTrue(true);
596: } else {
597: fail("Did not expect filter chain to proceed");
598: }
599: }
600: }
601: }
|