1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.acegisecurity.ui.digestauth;
17
18 import junit.framework.TestCase;
19
20 import org.acegisecurity.DisabledException;
21 import org.acegisecurity.MockFilterConfig;
22
23 import org.acegisecurity.context.SecurityContextHolder;
24 import org.acegisecurity.context.SecurityContextImpl;
25
26 import org.acegisecurity.providers.dao.UserCache;
27
28 import org.acegisecurity.userdetails.UserDetailsService;
29 import org.acegisecurity.userdetails.UserDetails;
30 import org.acegisecurity.userdetails.UsernameNotFoundException;
31 import org.acegisecurity.util.StringSplitUtils;
32
33 import org.apache.commons.codec.binary.Base64;
34
35 import org.springframework.context.ApplicationContext;
36 import org.springframework.context.support.ClassPathXmlApplicationContext;
37
38 import org.springframework.dao.DataAccessException;
39
40 import org.springframework.mock.web.MockHttpServletRequest;
41 import org.springframework.mock.web.MockHttpServletResponse;
42
43 import org.springframework.util.StringUtils;
44
45 import java.io.IOException;
46
47 import java.util.Map;
48
49 import javax.servlet.Filter;
50 import javax.servlet.FilterChain;
51 import javax.servlet.FilterConfig;
52 import javax.servlet.ServletException;
53 import javax.servlet.ServletRequest;
54 import javax.servlet.ServletResponse;
55
56
57 /***
58 * Tests {@link DigestProcessingFilter}.
59 *
60 * @author Ben Alex
61 * @version $Id: DigestProcessingFilterTests.java,v 1.10 2005/11/30 00:20:13 benalex Exp $
62 */
63 public class DigestProcessingFilterTests extends TestCase {
64
65
66 public DigestProcessingFilterTests() {
67 super();
68 }
69
70 public DigestProcessingFilterTests(String arg0) {
71 super(arg0);
72 }
73
74
75
76 public static void main(String[] args) {
77 junit.textui.TestRunner.run(DigestProcessingFilterTests.class);
78 }
79
80 public void testDoFilterWithNonHttpServletRequestDetected()
81 throws Exception {
82 DigestProcessingFilter filter = new DigestProcessingFilter();
83
84 try {
85 filter.doFilter(null, new MockHttpServletResponse(),
86 new MockFilterChain());
87 fail("Should have thrown ServletException");
88 } catch (ServletException expected) {
89 assertEquals("Can only process HttpServletRequest",
90 expected.getMessage());
91 }
92 }
93
94 public void testDoFilterWithNonHttpServletResponseDetected()
95 throws Exception {
96 DigestProcessingFilter filter = new DigestProcessingFilter();
97
98 try {
99 filter.doFilter(new MockHttpServletRequest(null, null), null,
100 new MockFilterChain());
101 fail("Should have thrown ServletException");
102 } catch (ServletException expected) {
103 assertEquals("Can only process HttpServletResponse",
104 expected.getMessage());
105 }
106 }
107
108 public void testExpiredNonceReturnsForbiddenWithStaleHeader()
109 throws Exception {
110 Map responseHeaderMap = generateValidHeaders(0);
111
112 String username = "marissa";
113 String realm = (String) responseHeaderMap.get("realm");
114 String nonce = (String) responseHeaderMap.get("nonce");
115 String uri = "/some_file.html";
116 String qop = (String) responseHeaderMap.get("qop");
117 String nc = "00000002";
118 String cnonce = "c822c727a648aba7";
119 String password = "koala";
120 String responseDigest = DigestProcessingFilter.generateDigest(false,
121 username, realm, password, "GET", uri, qop, nonce, nc, cnonce);
122
123
124 MockHttpServletRequest request = new MockHttpServletRequest("GET", uri);
125 request.addHeader("Authorization",
126 createAuthorizationHeader(username, realm, nonce, uri,
127 responseDigest, qop, nc, cnonce));
128 request.setServletPath("/some_file.html");
129
130
131 ApplicationContext ctx = new ClassPathXmlApplicationContext(
132 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
133 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
134 "digestProcessingFilter");
135
136
137 MockFilterConfig config = new MockFilterConfig();
138
139
140 MockFilterChain chain = new MockFilterChain(true);
141 MockHttpServletResponse response = new MockHttpServletResponse();
142
143
144 Thread.sleep(1000);
145 executeFilterInContainerSimulator(config, filter, request, response,
146 chain);
147
148 assertNull(SecurityContextHolder.getContext().getAuthentication());
149 assertEquals(401, response.getStatus());
150
151 String header = response.getHeader("WWW-Authenticate").toString()
152 .substring(7);
153 String[] headerEntries = StringUtils.commaDelimitedListToStringArray(header);
154 Map headerMap = StringSplitUtils.splitEachArrayElementAndCreateMap(headerEntries,
155 "=", "\"");
156 assertEquals("true", headerMap.get("stale"));
157 }
158
159 public void testFilterIgnoresRequestsContainingNoAuthorizationHeader()
160 throws Exception {
161
162 MockHttpServletRequest request = new MockHttpServletRequest();
163 request.setServletPath("/some_file.html");
164
165
166 ApplicationContext ctx = new ClassPathXmlApplicationContext(
167 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
168 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
169 "digestProcessingFilter");
170
171
172 MockFilterConfig config = new MockFilterConfig();
173
174
175 MockFilterChain chain = new MockFilterChain(true);
176 MockHttpServletResponse response = new MockHttpServletResponse();
177
178
179 executeFilterInContainerSimulator(config, filter, request, response,
180 chain);
181
182 assertNull(SecurityContextHolder.getContext().getAuthentication());
183 }
184
185 public void testGettersSetters() {
186 DigestProcessingFilter filter = new DigestProcessingFilter();
187 filter.setUserDetailsService(new MockAuthenticationDao());
188 assertTrue(filter.getUserDetailsService() != null);
189
190 filter.setAuthenticationEntryPoint(new DigestProcessingFilterEntryPoint());
191 assertTrue(filter.getAuthenticationEntryPoint() != null);
192
193 filter.setUserCache(null);
194 assertNull(filter.getUserCache());
195 filter.setUserCache(new MockUserCache());
196 assertNotNull(filter.getUserCache());
197 }
198
199 public void testInvalidDigestAuthorizationTokenGeneratesError()
200 throws Exception {
201
202 String token = "NOT_A_VALID_TOKEN_AS_MISSING_COLON";
203
204 MockHttpServletRequest request = new MockHttpServletRequest();
205 request.addHeader("Authorization",
206 "Digest " + new String(Base64.encodeBase64(token.getBytes())));
207 request.setServletPath("/some_file.html");
208
209
210 ApplicationContext ctx = new ClassPathXmlApplicationContext(
211 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
212 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
213 "digestProcessingFilter");
214
215
216 MockFilterConfig config = new MockFilterConfig();
217
218
219 MockFilterChain chain = new MockFilterChain(false);
220 MockHttpServletResponse response = new MockHttpServletResponse();
221
222
223 executeFilterInContainerSimulator(config, filter, request, response,
224 chain);
225 assertEquals(401, response.getStatus());
226
227 assertNull(SecurityContextHolder.getContext().getAuthentication());
228 }
229
230 public void testMalformedHeaderReturnsForbidden() throws Exception {
231
232 MockHttpServletRequest request = new MockHttpServletRequest();
233 request.addHeader("Authorization", "Digest scsdcsdc");
234 request.setServletPath("/some_file.html");
235
236
237 ApplicationContext ctx = new ClassPathXmlApplicationContext(
238 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
239 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
240 "digestProcessingFilter");
241
242
243 MockFilterConfig config = new MockFilterConfig();
244
245
246 MockFilterChain chain = new MockFilterChain(true);
247 MockHttpServletResponse response = new MockHttpServletResponse();
248
249
250 executeFilterInContainerSimulator(config, filter, request, response,
251 chain);
252
253 assertNull(SecurityContextHolder.getContext().getAuthentication());
254 assertEquals(401, response.getStatus());
255 }
256
257 public void testNonBase64EncodedNonceReturnsForbidden()
258 throws Exception {
259 Map responseHeaderMap = generateValidHeaders(60);
260
261 String username = "marissa";
262 String realm = (String) responseHeaderMap.get("realm");
263 String nonce = "NOT_BASE_64_ENCODED";
264 String uri = "/some_file.html";
265 String qop = (String) responseHeaderMap.get("qop");
266 String nc = "00000002";
267 String cnonce = "c822c727a648aba7";
268 String password = "koala";
269 String responseDigest = DigestProcessingFilter.generateDigest(false,
270 username, realm, password, "GET", uri, qop, nonce, nc, cnonce);
271
272
273 MockHttpServletRequest request = new MockHttpServletRequest();
274 request.addHeader("Authorization",
275 createAuthorizationHeader(username, realm, nonce, uri,
276 responseDigest, qop, nc, cnonce));
277 request.setServletPath("/some_file.html");
278
279
280 ApplicationContext ctx = new ClassPathXmlApplicationContext(
281 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
282 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
283 "digestProcessingFilter");
284
285
286 MockFilterConfig config = new MockFilterConfig();
287
288
289 MockFilterChain chain = new MockFilterChain(true);
290 MockHttpServletResponse response = new MockHttpServletResponse();
291
292
293 executeFilterInContainerSimulator(config, filter, request, response,
294 chain);
295
296 assertNull(SecurityContextHolder.getContext().getAuthentication());
297 assertEquals(401, response.getStatus());
298 }
299
300 public void testNonceWithIncorrectSignatureForNumericFieldReturnsForbidden()
301 throws Exception {
302 Map responseHeaderMap = generateValidHeaders(60);
303
304 String username = "marissa";
305 String realm = (String) responseHeaderMap.get("realm");
306 String nonce = new String(Base64.encodeBase64(
307 "123456:incorrectStringPassword".getBytes()));
308 String uri = "/some_file.html";
309 String qop = (String) responseHeaderMap.get("qop");
310 String nc = "00000002";
311 String cnonce = "c822c727a648aba7";
312 String password = "koala";
313 String responseDigest = DigestProcessingFilter.generateDigest(false,
314 username, realm, password, "GET", uri, qop, nonce, nc, cnonce);
315
316
317 MockHttpServletRequest request = new MockHttpServletRequest();
318 request.addHeader("Authorization",
319 createAuthorizationHeader(username, realm, nonce, uri,
320 responseDigest, qop, nc, cnonce));
321 request.setServletPath("/some_file.html");
322
323
324 ApplicationContext ctx = new ClassPathXmlApplicationContext(
325 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
326 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
327 "digestProcessingFilter");
328
329
330 MockFilterConfig config = new MockFilterConfig();
331
332
333 MockFilterChain chain = new MockFilterChain(false);
334 MockHttpServletResponse response = new MockHttpServletResponse();
335
336
337 executeFilterInContainerSimulator(config, filter, request, response,
338 chain);
339
340 assertNull(SecurityContextHolder.getContext().getAuthentication());
341 assertEquals(401, response.getStatus());
342 }
343
344 public void testNonceWithNonNumericFirstElementReturnsForbidden()
345 throws Exception {
346 Map responseHeaderMap = generateValidHeaders(60);
347
348 String username = "marissa";
349 String realm = (String) responseHeaderMap.get("realm");
350 String nonce = new String(Base64.encodeBase64(
351 "hello:ignoredSecondElement".getBytes()));
352 String uri = "/some_file.html";
353 String qop = (String) responseHeaderMap.get("qop");
354 String nc = "00000002";
355 String cnonce = "c822c727a648aba7";
356 String password = "koala";
357 String responseDigest = DigestProcessingFilter.generateDigest(false,
358 username, realm, password, "GET", uri, qop, nonce, nc, cnonce);
359
360
361 MockHttpServletRequest request = new MockHttpServletRequest();
362 request.addHeader("Authorization",
363 createAuthorizationHeader(username, realm, nonce, uri,
364 responseDigest, qop, nc, cnonce));
365 request.setServletPath("/some_file.html");
366
367
368 ApplicationContext ctx = new ClassPathXmlApplicationContext(
369 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
370 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
371 "digestProcessingFilter");
372
373
374 MockFilterConfig config = new MockFilterConfig();
375
376
377 MockFilterChain chain = new MockFilterChain(true);
378 MockHttpServletResponse response = new MockHttpServletResponse();
379
380
381 executeFilterInContainerSimulator(config, filter, request, response,
382 chain);
383
384 assertNull(SecurityContextHolder.getContext().getAuthentication());
385 assertEquals(401, response.getStatus());
386 }
387
388 public void testNonceWithoutTwoColonSeparatedElementsReturnsForbidden()
389 throws Exception {
390 Map responseHeaderMap = generateValidHeaders(60);
391
392 String username = "marissa";
393 String realm = (String) responseHeaderMap.get("realm");
394 String nonce = new String(Base64.encodeBase64(
395 "a base 64 string without a colon".getBytes()));
396 String uri = "/some_file.html";
397 String qop = (String) responseHeaderMap.get("qop");
398 String nc = "00000002";
399 String cnonce = "c822c727a648aba7";
400 String password = "koala";
401 String responseDigest = DigestProcessingFilter.generateDigest(false,
402 username, realm, password, "GET", uri, qop, nonce, nc, cnonce);
403
404
405 MockHttpServletRequest request = new MockHttpServletRequest();
406 request.addHeader("Authorization",
407 createAuthorizationHeader(username, realm, nonce, uri,
408 responseDigest, qop, nc, cnonce));
409 request.setServletPath("/some_file.html");
410
411
412 ApplicationContext ctx = new ClassPathXmlApplicationContext(
413 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
414 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
415 "digestProcessingFilter");
416
417
418 MockFilterConfig config = new MockFilterConfig();
419
420
421 MockFilterChain chain = new MockFilterChain(true);
422 MockHttpServletResponse response = new MockHttpServletResponse();
423
424
425 executeFilterInContainerSimulator(config, filter, request, response,
426 chain);
427
428 assertNull(SecurityContextHolder.getContext().getAuthentication());
429 assertEquals(401, response.getStatus());
430 }
431
432 public void testNormalOperationWhenPasswordIsAlreadyEncoded()
433 throws Exception {
434 Map responseHeaderMap = generateValidHeaders(60);
435
436 String username = "marissa";
437 String realm = (String) responseHeaderMap.get("realm");
438 String nonce = (String) responseHeaderMap.get("nonce");
439 String uri = "/some_file.html";
440 String qop = (String) responseHeaderMap.get("qop");
441 String nc = "00000002";
442 String cnonce = "c822c727a648aba7";
443 String password = DigestProcessingFilter.encodePasswordInA1Format(username,
444 realm, "koala");
445 String responseDigest = DigestProcessingFilter.generateDigest(true,
446 username, realm, password, "GET", uri, qop, nonce, nc, cnonce);
447
448
449 MockHttpServletRequest request = new MockHttpServletRequest("GET", uri);
450 request.addHeader("Authorization",
451 createAuthorizationHeader(username, realm, nonce, uri,
452 responseDigest, qop, nc, cnonce));
453 request.setServletPath("/some_file.html");
454
455
456 ApplicationContext ctx = new ClassPathXmlApplicationContext(
457 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
458 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
459 "digestProcessingFilter");
460
461
462 MockFilterConfig config = new MockFilterConfig();
463
464
465 MockFilterChain chain = new MockFilterChain(true);
466 MockHttpServletResponse response = new MockHttpServletResponse();
467
468
469 executeFilterInContainerSimulator(config, filter, request, response,
470 chain);
471
472 assertNotNull(SecurityContextHolder.getContext().getAuthentication());
473 assertEquals("marissa",
474 ((UserDetails) SecurityContextHolder.getContext().getAuthentication()
475 .getPrincipal()).getUsername());
476 }
477
478 public void testNormalOperationWhenPasswordNotAlreadyEncoded()
479 throws Exception {
480 Map responseHeaderMap = generateValidHeaders(60);
481
482 String username = "marissa";
483 String realm = (String) responseHeaderMap.get("realm");
484 String nonce = (String) responseHeaderMap.get("nonce");
485 String uri = "/some_file.html";
486 String qop = (String) responseHeaderMap.get("qop");
487 String nc = "00000002";
488 String cnonce = "c822c727a648aba7";
489 String password = "koala";
490 String responseDigest = DigestProcessingFilter.generateDigest(false,
491 username, realm, password, "GET", uri, qop, nonce, nc, cnonce);
492
493
494 MockHttpServletRequest request = new MockHttpServletRequest("GET", uri);
495 request.addHeader("Authorization",
496 createAuthorizationHeader(username, realm, nonce, uri,
497 responseDigest, qop, nc, cnonce));
498 request.setServletPath("/some_file.html");
499
500
501 ApplicationContext ctx = new ClassPathXmlApplicationContext(
502 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
503 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
504 "digestProcessingFilter");
505
506
507 MockFilterConfig config = new MockFilterConfig();
508
509
510 MockFilterChain chain = new MockFilterChain(true);
511 MockHttpServletResponse response = new MockHttpServletResponse();
512
513
514 executeFilterInContainerSimulator(config, filter, request, response,
515 chain);
516
517 assertNotNull(SecurityContextHolder.getContext().getAuthentication());
518 assertEquals("marissa",
519 ((UserDetails) SecurityContextHolder.getContext().getAuthentication()
520 .getPrincipal()).getUsername());
521 }
522
523 public void testOtherAuthorizationSchemeIsIgnored()
524 throws Exception {
525
526 MockHttpServletRequest request = new MockHttpServletRequest();
527 request.addHeader("Authorization", "SOME_OTHER_AUTHENTICATION_SCHEME");
528 request.setServletPath("/some_file.html");
529
530
531 ApplicationContext ctx = new ClassPathXmlApplicationContext(
532 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
533 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
534 "digestProcessingFilter");
535
536
537 MockFilterConfig config = new MockFilterConfig();
538
539
540 MockFilterChain chain = new MockFilterChain(true);
541 MockHttpServletResponse response = new MockHttpServletResponse();
542
543
544 executeFilterInContainerSimulator(config, filter, request, response,
545 chain);
546
547 assertNull(SecurityContextHolder.getContext().getAuthentication());
548 }
549
550 public void testStartupDetectsMissingAuthenticationDao()
551 throws Exception {
552 try {
553 DigestProcessingFilter filter = new DigestProcessingFilter();
554 filter.setAuthenticationEntryPoint(new DigestProcessingFilterEntryPoint());
555 filter.afterPropertiesSet();
556 fail("Should have thrown IllegalArgumentException");
557 } catch (IllegalArgumentException expected) {
558 assertEquals("An AuthenticationDao is required",
559 expected.getMessage());
560 }
561 }
562
563 public void testStartupDetectsMissingAuthenticationEntryPoint()
564 throws Exception {
565 try {
566 DigestProcessingFilter filter = new DigestProcessingFilter();
567 filter.setUserDetailsService(new MockAuthenticationDao());
568 filter.afterPropertiesSet();
569 fail("Should have thrown IllegalArgumentException");
570 } catch (IllegalArgumentException expected) {
571 assertEquals("A DigestProcessingFilterEntryPoint is required",
572 expected.getMessage());
573 }
574 }
575
576 public void testSuccessLoginThenFailureLoginResultsInSessionLoosingToken()
577 throws Exception {
578 Map responseHeaderMap = generateValidHeaders(60);
579
580 String username = "marissa";
581 String realm = (String) responseHeaderMap.get("realm");
582 String nonce = (String) responseHeaderMap.get("nonce");
583 String uri = "/some_file.html";
584 String qop = (String) responseHeaderMap.get("qop");
585 String nc = "00000002";
586 String cnonce = "c822c727a648aba7";
587 String password = "koala";
588 String responseDigest = DigestProcessingFilter.generateDigest(false,
589 username, realm, password, "GET", uri, qop, nonce, nc, cnonce);
590
591
592 MockHttpServletRequest request = new MockHttpServletRequest("GET", uri);
593 request.addHeader("Authorization",
594 createAuthorizationHeader(username, realm, nonce, uri,
595 responseDigest, qop, nc, cnonce));
596 request.setServletPath("/some_file.html");
597
598
599 ApplicationContext ctx = new ClassPathXmlApplicationContext(
600 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
601 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
602 "digestProcessingFilter");
603
604
605 MockFilterConfig config = new MockFilterConfig();
606
607
608 MockFilterChain chain = new MockFilterChain(true);
609 MockHttpServletResponse response = new MockHttpServletResponse();
610
611
612 executeFilterInContainerSimulator(config, filter, request, response,
613 chain);
614
615 assertNotNull(SecurityContextHolder.getContext().getAuthentication());
616
617
618 password = "WRONG_PASSWORD";
619 responseDigest = DigestProcessingFilter.generateDigest(false, username,
620 realm, password, "GET", uri, qop, nonce, nc, cnonce);
621
622 request = new MockHttpServletRequest();
623 request.addHeader("Authorization",
624 createAuthorizationHeader(username, realm, nonce, uri,
625 responseDigest, qop, nc, cnonce));
626 executeFilterInContainerSimulator(config, filter, request, response,
627 chain);
628
629
630 assertNull(SecurityContextHolder.getContext().getAuthentication());
631 assertEquals(401, response.getStatus());
632 }
633
634 public void testWrongCnonceBasedOnDigestReturnsForbidden()
635 throws Exception {
636 Map responseHeaderMap = generateValidHeaders(60);
637
638 String username = "marissa";
639 String realm = (String) responseHeaderMap.get("realm");
640 String nonce = (String) responseHeaderMap.get("nonce");
641 String uri = "/some_file.html";
642 String qop = (String) responseHeaderMap.get("qop");
643 String nc = "00000002";
644 String cnonce = "NOT_SAME_AS_USED_FOR_DIGEST_COMPUTATION";
645 String password = "koala";
646 String responseDigest = DigestProcessingFilter.generateDigest(false,
647 username, realm, password, "GET", uri, qop, nonce, nc,
648 "DIFFERENT_CNONCE");
649
650
651 MockHttpServletRequest request = new MockHttpServletRequest("GET", uri);
652 request.addHeader("Authorization",
653 createAuthorizationHeader(username, realm, nonce, uri,
654 responseDigest, qop, nc, cnonce));
655 request.setServletPath("/some_file.html");
656
657
658 ApplicationContext ctx = new ClassPathXmlApplicationContext(
659 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
660 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
661 "digestProcessingFilter");
662
663
664 MockFilterConfig config = new MockFilterConfig();
665
666
667 MockFilterChain chain = new MockFilterChain(true);
668 MockHttpServletResponse response = new MockHttpServletResponse();
669
670
671 executeFilterInContainerSimulator(config, filter, request, response,
672 chain);
673
674 assertNull(SecurityContextHolder.getContext().getAuthentication());
675 assertEquals(401, response.getStatus());
676 }
677
678 public void testWrongDigestReturnsForbidden() throws Exception {
679 Map responseHeaderMap = generateValidHeaders(60);
680
681 String username = "marissa";
682 String realm = (String) responseHeaderMap.get("realm");
683 String nonce = (String) responseHeaderMap.get("nonce");
684 String uri = "/some_file.html";
685 String qop = (String) responseHeaderMap.get("qop");
686 String nc = "00000002";
687 String cnonce = "c822c727a648aba7";
688 String password = "WRONG_PASSWORD";
689 String responseDigest = DigestProcessingFilter.generateDigest(false,
690 username, realm, password, "GET", uri, qop, nonce, nc, cnonce);
691
692
693 MockHttpServletRequest request = new MockHttpServletRequest("GET", uri);
694 request.addHeader("Authorization",
695 createAuthorizationHeader(username, realm, nonce, uri,
696 responseDigest, qop, nc, cnonce));
697 request.setServletPath("/some_file.html");
698
699
700 ApplicationContext ctx = new ClassPathXmlApplicationContext(
701 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
702 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
703 "digestProcessingFilter");
704
705
706 MockFilterConfig config = new MockFilterConfig();
707
708
709 MockFilterChain chain = new MockFilterChain(true);
710 MockHttpServletResponse response = new MockHttpServletResponse();
711
712
713 executeFilterInContainerSimulator(config, filter, request, response,
714 chain);
715
716 assertNull(SecurityContextHolder.getContext().getAuthentication());
717 assertEquals(401, response.getStatus());
718 }
719
720 public void testWrongRealmReturnsForbidden() throws Exception {
721 Map responseHeaderMap = generateValidHeaders(60);
722
723 String username = "marissa";
724 String realm = "WRONG_REALM";
725 String nonce = (String) responseHeaderMap.get("nonce");
726 String uri = "/some_file.html";
727 String qop = (String) responseHeaderMap.get("qop");
728 String nc = "00000002";
729 String cnonce = "c822c727a648aba7";
730 String password = "koala";
731 String responseDigest = DigestProcessingFilter.generateDigest(false,
732 username, realm, password, "GET", uri, qop, nonce, nc, cnonce);
733
734
735 MockHttpServletRequest request = new MockHttpServletRequest("GET", uri);
736 request.addHeader("Authorization",
737 createAuthorizationHeader(username, realm, nonce, uri,
738 responseDigest, qop, nc, cnonce));
739 request.setServletPath("/some_file.html");
740
741
742 ApplicationContext ctx = new ClassPathXmlApplicationContext(
743 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
744 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
745 "digestProcessingFilter");
746
747
748 MockFilterConfig config = new MockFilterConfig();
749
750
751 MockFilterChain chain = new MockFilterChain(true);
752 MockHttpServletResponse response = new MockHttpServletResponse();
753
754
755 executeFilterInContainerSimulator(config, filter, request, response,
756 chain);
757
758 assertNull(SecurityContextHolder.getContext().getAuthentication());
759 assertEquals(401, response.getStatus());
760 }
761
762 public void testWrongUsernameReturnsForbidden() throws Exception {
763 Map responseHeaderMap = generateValidHeaders(60);
764
765 String username = "NOT_A_KNOWN_USER";
766 String realm = (String) responseHeaderMap.get("realm");
767 String nonce = (String) responseHeaderMap.get("nonce");
768 String uri = "/some_file.html";
769 String qop = (String) responseHeaderMap.get("qop");
770 String nc = "00000002";
771 String cnonce = "c822c727a648aba7";
772 String password = "koala";
773 String responseDigest = DigestProcessingFilter.generateDigest(false,
774 username, realm, password, "GET", uri, qop, nonce, nc, cnonce);
775
776
777 MockHttpServletRequest request = new MockHttpServletRequest("GET", uri);
778 request.addHeader("Authorization",
779 createAuthorizationHeader(username, realm, nonce, uri,
780 responseDigest, qop, nc, cnonce));
781 request.setServletPath("/some_file.html");
782
783
784 ApplicationContext ctx = new ClassPathXmlApplicationContext(
785 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
786 DigestProcessingFilter filter = (DigestProcessingFilter) ctx.getBean(
787 "digestProcessingFilter");
788
789
790 MockFilterConfig config = new MockFilterConfig();
791
792
793 MockFilterChain chain = new MockFilterChain(true);
794 MockHttpServletResponse response = new MockHttpServletResponse();
795
796
797 executeFilterInContainerSimulator(config, filter, request, response,
798 chain);
799
800 assertNull(SecurityContextHolder.getContext().getAuthentication());
801 assertEquals(401, response.getStatus());
802 }
803
804 protected void setUp() throws Exception {
805 super.setUp();
806 SecurityContextHolder.setContext(new SecurityContextImpl());
807 }
808
809 protected void tearDown() throws Exception {
810 super.tearDown();
811 SecurityContextHolder.setContext(new SecurityContextImpl());
812 }
813
814 private String createAuthorizationHeader(String username, String realm,
815 String nonce, String uri, String responseDigest, String qop, String nc,
816 String cnonce) {
817 return "Digest username=\"" + username + "\", realm=\"" + realm
818 + "\", nonce=\"" + nonce + "\", uri=\"" + uri + "\", response=\""
819 + responseDigest + "\", qop=" + qop + ", nc=" + nc + ", cnonce=\""
820 + cnonce + "\"";
821 }
822
823 private void executeFilterInContainerSimulator(FilterConfig filterConfig,
824 Filter filter, ServletRequest request, ServletResponse response,
825 FilterChain filterChain) throws ServletException, IOException {
826 filter.init(filterConfig);
827 filter.doFilter(request, response, filterChain);
828 filter.destroy();
829 }
830
831 private Map generateValidHeaders(int nonceValidityPeriod)
832 throws Exception {
833 ApplicationContext ctx = new ClassPathXmlApplicationContext(
834 "org/acegisecurity/ui/digestauth/filtertest-valid.xml");
835 DigestProcessingFilterEntryPoint ep = (DigestProcessingFilterEntryPoint) ctx
836 .getBean("digestProcessingFilterEntryPoint");
837 ep.setNonceValiditySeconds(nonceValidityPeriod);
838
839 MockHttpServletRequest request = new MockHttpServletRequest();
840 request.setRequestURI("/some_path");
841
842 MockHttpServletResponse response = new MockHttpServletResponse();
843
844 ep.commence(request, response, new DisabledException("foobar"));
845
846
847 String header = response.getHeader("WWW-Authenticate").toString()
848 .substring(7);
849 String[] headerEntries = StringUtils.commaDelimitedListToStringArray(header);
850 Map headerMap = StringSplitUtils.splitEachArrayElementAndCreateMap(headerEntries,
851 "=", "\"");
852
853 return headerMap;
854 }
855
856
857
858 private class MockAuthenticationDao implements UserDetailsService {
859 public UserDetails loadUserByUsername(String username)
860 throws UsernameNotFoundException, DataAccessException {
861 return null;
862 }
863 }
864
865 private class MockFilterChain implements FilterChain {
866 private boolean expectToProceed;
867
868 public MockFilterChain(boolean expectToProceed) {
869 this.expectToProceed = expectToProceed;
870 }
871
872 private MockFilterChain() {
873 super();
874 }
875
876 public void doFilter(ServletRequest request, ServletResponse response)
877 throws IOException, ServletException {
878 if (expectToProceed) {
879 assertTrue(true);
880 } else {
881 fail("Did not expect filter chain to proceed");
882 }
883 }
884 }
885
886 private class MockUserCache implements UserCache {
887 public UserDetails getUserFromCache(String username) {
888 return null;
889 }
890
891 public void putUserInCache(UserDetails user) {}
892
893 public void removeUserFromCache(String username) {}
894 }
895 }