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