diff --git a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java index 7702220078..581e3dd20d 100644 --- a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java +++ b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java @@ -356,8 +356,10 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, } private UserDetails getUserFromBackend(String username) { + UserDetails loadedUser; + try { - return this.authenticationDao.loadUserByUsername(username); + loadedUser = this.authenticationDao.loadUserByUsername(username); } catch (UsernameNotFoundException notFound) { if (hideUserNotFoundExceptions) { throw new BadCredentialsException("Bad credentials presented"); @@ -368,5 +370,12 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, throw new AuthenticationServiceException(repositoryProblem .getMessage(), repositoryProblem); } + + if (loadedUser == null) { + throw new AuthenticationServiceException( + "AuthenticationDao returned null, which is an interface contract violation"); + } + + return loadedUser; } } diff --git a/core/src/main/java/org/acegisecurity/userdetails/UserDetailsService.java b/core/src/main/java/org/acegisecurity/userdetails/UserDetailsService.java index 732446f958..6bbf9d0558 100644 --- a/core/src/main/java/org/acegisecurity/userdetails/UserDetailsService.java +++ b/core/src/main/java/org/acegisecurity/userdetails/UserDetailsService.java @@ -45,7 +45,7 @@ public interface AuthenticationDao { * @param username the username presented to the {@link * DaoAuthenticationProvider} * - * @return a fully populated user record + * @return a fully populated user record (never null) * * @throws UsernameNotFoundException if the user could not be found or the * user has no GrantedAuthority diff --git a/core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationProviderTests.java b/core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationProviderTests.java index 39f38e4093..5a624a89a1 100644 --- a/core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationProviderTests.java +++ b/core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationProviderTests.java @@ -286,6 +286,22 @@ public class DaoAuthenticationProviderTests extends TestCase { assertEquals("marissa", castResult.getPrincipal()); } + public void testDetectsNullBeingReturnedFromAuthenticationDao() { + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("marissa", + "koala"); + + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setAuthenticationDao(new MockAuthenticationDaoReturnsNull()); + + try { + provider.authenticate(token); + fail("Should have thrown AuthenticationServiceException"); + } catch (AuthenticationServiceException expected) { + assertEquals("AuthenticationDao returned null, which is an interface contract violation", + expected.getMessage()); + } + } + public void testGettersSetters() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setPasswordEncoder(new ShaPasswordEncoder()); @@ -384,6 +400,13 @@ public class DaoAuthenticationProviderTests extends TestCase { //~ Inner Classes ========================================================== + private class MockAuthenticationDaoReturnsNull implements AuthenticationDao { + public UserDetails loadUserByUsername(String username) + throws UsernameNotFoundException, DataAccessException { + return null; + } + } + private class MockAuthenticationDaoSimulateBackendError implements AuthenticationDao { public UserDetails loadUserByUsername(String username) diff --git a/doc/xdocs/changes.xml b/doc/xdocs/changes.xml index e99d98824a..a5a74769bb 100644 --- a/doc/xdocs/changes.xml +++ b/doc/xdocs/changes.xml @@ -53,6 +53,7 @@ Improved JaasAuthenticationProvider startup error detection Refactored EH-CACHE implementations to use Spring IoC defined caches instead AbstractProcessingFilter now has various hook methods to assist subclasses + DaoAuthenticationProvider better detects AuthenticationDao interface violations Fixed ambiguous column references in JdbcDaoImpl default query Fixed AbstractProcessingFilter to use removeAttribute (JRun compatibility) Fixed GrantedAuthorityEffectiveAclResolver support of UserDetails principals