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.providers.dao;
017:
018: import junit.framework.TestCase;
019:
020: import org.acegisecurity.AccountExpiredException;
021: import org.acegisecurity.Authentication;
022: import org.acegisecurity.AuthenticationServiceException;
023: import org.acegisecurity.BadCredentialsException;
024: import org.acegisecurity.CredentialsExpiredException;
025: import org.acegisecurity.DisabledException;
026: import org.acegisecurity.GrantedAuthority;
027: import org.acegisecurity.GrantedAuthorityImpl;
028: import org.acegisecurity.LockedException;
029:
030: import org.acegisecurity.providers.TestingAuthenticationToken;
031: import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
032: import org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache;
033: import org.acegisecurity.providers.dao.cache.NullUserCache;
034: import org.acegisecurity.providers.dao.salt.SystemWideSaltSource;
035: import org.acegisecurity.providers.encoding.ShaPasswordEncoder;
036:
037: import org.acegisecurity.userdetails.User;
038: import org.acegisecurity.userdetails.UserDetails;
039: import org.acegisecurity.userdetails.UserDetailsService;
040: import org.acegisecurity.userdetails.UsernameNotFoundException;
041:
042: import org.springframework.dao.DataAccessException;
043: import org.springframework.dao.DataRetrievalFailureException;
044:
045: import java.util.HashMap;
046: import java.util.Map;
047:
048: /**
049: * Tests {@link DaoAuthenticationProvider}.
050: *
051: * @author Ben Alex
052: * @version $Id: DaoAuthenticationProviderTests.java 1857 2007-05-24 00:47:12Z benalex $
053: */
054: public class DaoAuthenticationProviderTests extends TestCase {
055: //~ Methods ========================================================================================================
056:
057: public static void main(String[] args) {
058: junit.textui.TestRunner
059: .run(DaoAuthenticationProviderTests.class);
060: }
061:
062: public final void setUp() throws Exception {
063: super .setUp();
064: }
065:
066: public void testAuthenticateFailsForIncorrectPasswordCase() {
067: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
068: "marissa", "KOala");
069:
070: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
071: provider
072: .setUserDetailsService(new MockAuthenticationDaoUserMarissa());
073: provider.setUserCache(new MockUserCache());
074:
075: try {
076: provider.authenticate(token);
077: fail("Should have thrown BadCredentialsException");
078: } catch (BadCredentialsException expected) {
079: assertTrue(true);
080: }
081: }
082:
083: public void testReceivedBadCredentialsWhenCredentialsNotProvided() {
084: // Test related to SEC-434
085: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
086: provider
087: .setUserDetailsService(new MockAuthenticationDaoUserMarissa());
088: provider.setUserCache(new MockUserCache());
089:
090: UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
091: "marissa", null);
092: try {
093: provider.authenticate(authenticationToken); // null pointer exception
094: fail("Expected BadCredenialsException");
095: } catch (BadCredentialsException expected) {
096: assertTrue(true);
097: }
098: }
099:
100: public void testAuthenticateFailsIfAccountExpired() {
101: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
102: "peter", "opal");
103:
104: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
105: provider
106: .setUserDetailsService(new MockAuthenticationDaoUserPeterAccountExpired());
107: provider.setUserCache(new MockUserCache());
108:
109: try {
110: provider.authenticate(token);
111: fail("Should have thrown AccountExpiredException");
112: } catch (AccountExpiredException expected) {
113: assertTrue(true);
114: }
115: }
116:
117: public void testAuthenticateFailsIfAccountLocked() {
118: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
119: "peter", "opal");
120:
121: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
122: provider
123: .setUserDetailsService(new MockAuthenticationDaoUserPeterAccountLocked());
124: provider.setUserCache(new MockUserCache());
125:
126: try {
127: provider.authenticate(token);
128: fail("Should have thrown LockedException");
129: } catch (LockedException expected) {
130: assertTrue(true);
131: }
132: }
133:
134: public void testAuthenticateFailsIfCredentialsExpired() {
135: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
136: "peter", "opal");
137:
138: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
139: provider
140: .setUserDetailsService(new MockAuthenticationDaoUserPeterCredentialsExpired());
141: provider.setUserCache(new MockUserCache());
142:
143: try {
144: provider.authenticate(token);
145: fail("Should have thrown CredentialsExpiredException");
146: } catch (CredentialsExpiredException expected) {
147: assertTrue(true);
148: }
149:
150: // Check that wrong password causes BadCredentialsException, rather than CredentialsExpiredException
151: token = new UsernamePasswordAuthenticationToken("peter",
152: "wrong_password");
153:
154: try {
155: provider.authenticate(token);
156: fail("Should have thrown BadCredentialsException");
157: } catch (BadCredentialsException expected) {
158: assertTrue(true);
159: }
160: }
161:
162: public void testAuthenticateFailsIfUserDisabled() {
163: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
164: "peter", "opal");
165:
166: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
167: provider
168: .setUserDetailsService(new MockAuthenticationDaoUserPeter());
169: provider.setUserCache(new MockUserCache());
170:
171: try {
172: provider.authenticate(token);
173: fail("Should have thrown DisabledException");
174: } catch (DisabledException expected) {
175: assertTrue(true);
176: }
177: }
178:
179: public void testAuthenticateFailsWhenAuthenticationDaoHasBackendFailure() {
180: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
181: "marissa", "koala");
182:
183: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
184: provider
185: .setUserDetailsService(new MockAuthenticationDaoSimulateBackendError());
186: provider.setUserCache(new MockUserCache());
187:
188: try {
189: provider.authenticate(token);
190: fail("Should have thrown AuthenticationServiceException");
191: } catch (AuthenticationServiceException expected) {
192: assertTrue(true);
193: }
194: }
195:
196: public void testAuthenticateFailsWithEmptyUsername() {
197: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
198: null, "koala");
199:
200: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
201: provider
202: .setUserDetailsService(new MockAuthenticationDaoUserMarissa());
203: provider.setUserCache(new MockUserCache());
204:
205: try {
206: provider.authenticate(token);
207: fail("Should have thrown BadCredentialsException");
208: } catch (BadCredentialsException expected) {
209: assertTrue(true);
210: }
211: }
212:
213: public void testAuthenticateFailsWithInvalidPassword() {
214: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
215: "marissa", "INVALID_PASSWORD");
216:
217: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
218: provider
219: .setUserDetailsService(new MockAuthenticationDaoUserMarissa());
220: provider.setUserCache(new MockUserCache());
221:
222: try {
223: provider.authenticate(token);
224: fail("Should have thrown BadCredentialsException");
225: } catch (BadCredentialsException expected) {
226: assertTrue(true);
227: }
228: }
229:
230: public void testAuthenticateFailsWithInvalidUsernameAndHideUserNotFoundExceptionFalse() {
231: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
232: "INVALID_USER", "koala");
233:
234: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
235: provider.setHideUserNotFoundExceptions(false); // we want UsernameNotFoundExceptions
236: provider
237: .setUserDetailsService(new MockAuthenticationDaoUserMarissa());
238: provider.setUserCache(new MockUserCache());
239:
240: try {
241: provider.authenticate(token);
242: fail("Should have thrown UsernameNotFoundException");
243: } catch (UsernameNotFoundException expected) {
244: assertTrue(true);
245: }
246: }
247:
248: public void testAuthenticateFailsWithInvalidUsernameAndHideUserNotFoundExceptionsWithDefaultOfTrue() {
249: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
250: "INVALID_USER", "koala");
251:
252: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
253: assertTrue(provider.isHideUserNotFoundExceptions());
254: provider
255: .setUserDetailsService(new MockAuthenticationDaoUserMarissa());
256: provider.setUserCache(new MockUserCache());
257:
258: try {
259: provider.authenticate(token);
260: fail("Should have thrown BadCredentialsException");
261: } catch (BadCredentialsException expected) {
262: assertTrue(true);
263: }
264: }
265:
266: public void testAuthenticateFailsWithMixedCaseUsernameIfDefaultChanged() {
267: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
268: "MaRiSSA", "koala");
269:
270: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
271: provider
272: .setUserDetailsService(new MockAuthenticationDaoUserMarissa());
273: provider.setUserCache(new MockUserCache());
274:
275: try {
276: provider.authenticate(token);
277: fail("Should have thrown BadCredentialsException");
278: } catch (BadCredentialsException expected) {
279: assertTrue(true);
280: }
281: }
282:
283: public void testAuthenticates() {
284: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
285: "marissa", "koala");
286: token.setDetails("192.168.0.1");
287:
288: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
289: provider
290: .setUserDetailsService(new MockAuthenticationDaoUserMarissa());
291: provider.setUserCache(new MockUserCache());
292:
293: Authentication result = provider.authenticate(token);
294:
295: if (!(result instanceof UsernamePasswordAuthenticationToken)) {
296: fail("Should have returned instance of UsernamePasswordAuthenticationToken");
297: }
298:
299: UsernamePasswordAuthenticationToken castResult = (UsernamePasswordAuthenticationToken) result;
300: assertEquals(User.class, castResult.getPrincipal().getClass());
301: assertEquals("koala", castResult.getCredentials());
302: assertEquals("ROLE_ONE", castResult.getAuthorities()[0]
303: .getAuthority());
304: assertEquals("ROLE_TWO", castResult.getAuthorities()[1]
305: .getAuthority());
306: assertEquals("192.168.0.1", castResult.getDetails());
307: }
308:
309: public void testAuthenticatesASecondTime() {
310: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
311: "marissa", "koala");
312:
313: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
314: provider
315: .setUserDetailsService(new MockAuthenticationDaoUserMarissa());
316: provider.setUserCache(new MockUserCache());
317:
318: Authentication result = provider.authenticate(token);
319:
320: if (!(result instanceof UsernamePasswordAuthenticationToken)) {
321: fail("Should have returned instance of UsernamePasswordAuthenticationToken");
322: }
323:
324: // Now try to authenticate with the previous result (with its UserDetails)
325: Authentication result2 = provider.authenticate(result);
326:
327: if (!(result2 instanceof UsernamePasswordAuthenticationToken)) {
328: fail("Should have returned instance of UsernamePasswordAuthenticationToken");
329: }
330:
331: assertEquals(result.getCredentials(), result2.getCredentials());
332: }
333:
334: public void testAuthenticatesWhenASaltIsUsed() {
335: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
336: "marissa", "koala");
337:
338: SystemWideSaltSource salt = new SystemWideSaltSource();
339: salt.setSystemWideSalt("SYSTEM_SALT_VALUE");
340:
341: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
342: provider
343: .setUserDetailsService(new MockAuthenticationDaoUserMarissaWithSalt());
344: provider.setSaltSource(salt);
345: provider.setUserCache(new MockUserCache());
346:
347: Authentication result = provider.authenticate(token);
348:
349: if (!(result instanceof UsernamePasswordAuthenticationToken)) {
350: fail("Should have returned instance of UsernamePasswordAuthenticationToken");
351: }
352:
353: UsernamePasswordAuthenticationToken castResult = (UsernamePasswordAuthenticationToken) result;
354: assertEquals(User.class, castResult.getPrincipal().getClass());
355:
356: // We expect original credentials user submitted to be returned
357: assertEquals("koala", castResult.getCredentials());
358: assertEquals("ROLE_ONE", castResult.getAuthorities()[0]
359: .getAuthority());
360: assertEquals("ROLE_TWO", castResult.getAuthorities()[1]
361: .getAuthority());
362: }
363:
364: public void testAuthenticatesWithForcePrincipalAsString() {
365: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
366: "marissa", "koala");
367:
368: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
369: provider
370: .setUserDetailsService(new MockAuthenticationDaoUserMarissa());
371: provider.setUserCache(new MockUserCache());
372: provider.setForcePrincipalAsString(true);
373:
374: Authentication result = provider.authenticate(token);
375:
376: if (!(result instanceof UsernamePasswordAuthenticationToken)) {
377: fail("Should have returned instance of UsernamePasswordAuthenticationToken");
378: }
379:
380: UsernamePasswordAuthenticationToken castResult = (UsernamePasswordAuthenticationToken) result;
381: assertEquals(String.class, castResult.getPrincipal().getClass());
382: assertEquals("marissa", castResult.getPrincipal());
383: }
384:
385: public void testDetectsNullBeingReturnedFromAuthenticationDao() {
386: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
387: "marissa", "koala");
388:
389: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
390: provider
391: .setUserDetailsService(new MockAuthenticationDaoReturnsNull());
392:
393: try {
394: provider.authenticate(token);
395: fail("Should have thrown AuthenticationServiceException");
396: } catch (AuthenticationServiceException expected) {
397: assertEquals(
398: "UserDetailsService returned null, which is an interface contract violation",
399: expected.getMessage());
400: }
401: }
402:
403: public void testGettersSetters() {
404: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
405: provider.setPasswordEncoder(new ShaPasswordEncoder());
406: assertEquals(ShaPasswordEncoder.class, provider
407: .getPasswordEncoder().getClass());
408:
409: provider.setSaltSource(new SystemWideSaltSource());
410: assertEquals(SystemWideSaltSource.class, provider
411: .getSaltSource().getClass());
412:
413: provider.setUserCache(new EhCacheBasedUserCache());
414: assertEquals(EhCacheBasedUserCache.class, provider
415: .getUserCache().getClass());
416:
417: assertFalse(provider.isForcePrincipalAsString());
418: provider.setForcePrincipalAsString(true);
419: assertTrue(provider.isForcePrincipalAsString());
420: }
421:
422: public void testGoesBackToAuthenticationDaoToObtainLatestPasswordIfCachedPasswordSeemsIncorrect() {
423: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
424: "marissa", "koala");
425:
426: MockAuthenticationDaoUserMarissa authenticationDao = new MockAuthenticationDaoUserMarissa();
427: MockUserCache cache = new MockUserCache();
428: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
429: provider.setUserDetailsService(authenticationDao);
430: provider.setUserCache(cache);
431:
432: // This will work, as password still "koala"
433: provider.authenticate(token);
434:
435: // Check "marissa = koala" ended up in the cache
436: assertEquals("koala", cache.getUserFromCache("marissa")
437: .getPassword());
438:
439: // Now change the password the AuthenticationDao will return
440: authenticationDao.setPassword("easternLongNeckTurtle");
441:
442: // Now try authentication again, with the new password
443: token = new UsernamePasswordAuthenticationToken("marissa",
444: "easternLongNeckTurtle");
445: provider.authenticate(token);
446:
447: // To get this far, the new password was accepted
448: // Check the cache was updated
449: assertEquals("easternLongNeckTurtle", cache.getUserFromCache(
450: "marissa").getPassword());
451: }
452:
453: public void testStartupFailsIfNoAuthenticationDao()
454: throws Exception {
455: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
456:
457: try {
458: provider.afterPropertiesSet();
459: fail("Should have thrown IllegalArgumentException");
460: } catch (IllegalArgumentException expected) {
461: assertTrue(true);
462: }
463: }
464:
465: public void testStartupFailsIfNoUserCacheSet() throws Exception {
466: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
467: provider
468: .setUserDetailsService(new MockAuthenticationDaoUserMarissa());
469: assertEquals(NullUserCache.class, provider.getUserCache()
470: .getClass());
471: provider.setUserCache(null);
472:
473: try {
474: provider.afterPropertiesSet();
475: fail("Should have thrown IllegalArgumentException");
476: } catch (IllegalArgumentException expected) {
477: assertTrue(true);
478: }
479: }
480:
481: public void testStartupSuccess() throws Exception {
482: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
483: UserDetailsService userDetailsService = new MockAuthenticationDaoUserMarissa();
484: provider.setUserDetailsService(userDetailsService);
485: provider.setUserCache(new MockUserCache());
486: assertEquals(userDetailsService, provider
487: .getUserDetailsService());
488: provider.afterPropertiesSet();
489: assertTrue(true);
490: }
491:
492: public void testSupports() {
493: DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
494: assertTrue(provider
495: .supports(UsernamePasswordAuthenticationToken.class));
496: assertTrue(!provider.supports(TestingAuthenticationToken.class));
497: }
498:
499: //~ Inner Classes ==================================================================================================
500:
501: private class MockAuthenticationDaoReturnsNull implements
502: UserDetailsService {
503: public UserDetails loadUserByUsername(String username)
504: throws UsernameNotFoundException, DataAccessException {
505: return null;
506: }
507: }
508:
509: private class MockAuthenticationDaoSimulateBackendError implements
510: UserDetailsService {
511: public UserDetails loadUserByUsername(String username)
512: throws UsernameNotFoundException, DataAccessException {
513: throw new DataRetrievalFailureException(
514: "This mock simulator is designed to fail");
515: }
516: }
517:
518: private class MockAuthenticationDaoUserMarissa implements
519: UserDetailsService {
520: private String password = "koala";
521:
522: public UserDetails loadUserByUsername(String username)
523: throws UsernameNotFoundException, DataAccessException {
524: if ("marissa".equals(username)) {
525: return new User("marissa", password, true, true, true,
526: true, new GrantedAuthority[] {
527: new GrantedAuthorityImpl("ROLE_ONE"),
528: new GrantedAuthorityImpl("ROLE_TWO") });
529: } else {
530: throw new UsernameNotFoundException("Could not find: "
531: + username);
532: }
533: }
534:
535: public void setPassword(String password) {
536: this .password = password;
537: }
538: }
539:
540: private class MockAuthenticationDaoUserMarissaWithSalt implements
541: UserDetailsService {
542: public UserDetails loadUserByUsername(String username)
543: throws UsernameNotFoundException, DataAccessException {
544: if ("marissa".equals(username)) {
545: return new User("marissa", "koala{SYSTEM_SALT_VALUE}",
546: true, true, true, true, new GrantedAuthority[] {
547: new GrantedAuthorityImpl("ROLE_ONE"),
548: new GrantedAuthorityImpl("ROLE_TWO") });
549: } else {
550: throw new UsernameNotFoundException("Could not find: "
551: + username);
552: }
553: }
554: }
555:
556: private class MockAuthenticationDaoUserPeter implements
557: UserDetailsService {
558: public UserDetails loadUserByUsername(String username)
559: throws UsernameNotFoundException, DataAccessException {
560: if ("peter".equals(username)) {
561: return new User("peter", "opal", false, true, true,
562: true, new GrantedAuthority[] {
563: new GrantedAuthorityImpl("ROLE_ONE"),
564: new GrantedAuthorityImpl("ROLE_TWO") });
565: } else {
566: throw new UsernameNotFoundException("Could not find: "
567: + username);
568: }
569: }
570: }
571:
572: private class MockAuthenticationDaoUserPeterAccountExpired
573: implements UserDetailsService {
574: public UserDetails loadUserByUsername(String username)
575: throws UsernameNotFoundException, DataAccessException {
576: if ("peter".equals(username)) {
577: return new User("peter", "opal", true, false, true,
578: true, new GrantedAuthority[] {
579: new GrantedAuthorityImpl("ROLE_ONE"),
580: new GrantedAuthorityImpl("ROLE_TWO") });
581: } else {
582: throw new UsernameNotFoundException("Could not find: "
583: + username);
584: }
585: }
586: }
587:
588: private class MockAuthenticationDaoUserPeterAccountLocked implements
589: UserDetailsService {
590: public UserDetails loadUserByUsername(String username)
591: throws UsernameNotFoundException, DataAccessException {
592: if ("peter".equals(username)) {
593: return new User("peter", "opal", true, true, true,
594: false, new GrantedAuthority[] {
595: new GrantedAuthorityImpl("ROLE_ONE"),
596: new GrantedAuthorityImpl("ROLE_TWO") });
597: } else {
598: throw new UsernameNotFoundException("Could not find: "
599: + username);
600: }
601: }
602: }
603:
604: private class MockAuthenticationDaoUserPeterCredentialsExpired
605: implements UserDetailsService {
606: public UserDetails loadUserByUsername(String username)
607: throws UsernameNotFoundException, DataAccessException {
608: if ("peter".equals(username)) {
609: return new User("peter", "opal", true, true, false,
610: true, new GrantedAuthority[] {
611: new GrantedAuthorityImpl("ROLE_ONE"),
612: new GrantedAuthorityImpl("ROLE_TWO") });
613: } else {
614: throw new UsernameNotFoundException("Could not find: "
615: + username);
616: }
617: }
618: }
619:
620: private class MockUserCache implements UserCache {
621: private Map cache = new HashMap();
622:
623: public UserDetails getUserFromCache(String username) {
624: return (User) cache.get(username);
625: }
626:
627: public void putUserInCache(UserDetails user) {
628: cache.put(user.getUsername(), user);
629: }
630:
631: public void removeUserFromCache(String username) {
632: }
633: }
634: }
|