diff --git a/contributors.txt b/contributors.txt index f94d1cda79..b26ca86a12 100644 --- a/contributors.txt +++ b/contributors.txt @@ -30,7 +30,8 @@ contributions to the Acegi Security System for Spring project: * Ray Krueger is a current member of the development team. -* Karel Miarka contributed a fix for EH-CACHE NPEs. +* Karel Miarka contributed a fix for EH-CACHE NPEs and additional event + handling for DaoAuthenticationProvider. * Anyone else I've forgotten (please let me know so I can correct this). 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 153ea4242d..0fa0968fdb 100644 --- a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java +++ b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java @@ -20,12 +20,14 @@ import net.sf.acegisecurity.AuthenticationException; import net.sf.acegisecurity.AuthenticationServiceException; import net.sf.acegisecurity.BadCredentialsException; import net.sf.acegisecurity.DisabledException; +import net.sf.acegisecurity.GrantedAuthority; import net.sf.acegisecurity.UserDetails; import net.sf.acegisecurity.providers.AuthenticationProvider; import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; import net.sf.acegisecurity.providers.dao.cache.NullUserCache; import net.sf.acegisecurity.providers.dao.event.AuthenticationFailureDisabledEvent; import net.sf.acegisecurity.providers.dao.event.AuthenticationFailurePasswordEvent; +import net.sf.acegisecurity.providers.dao.event.AuthenticationFailureUsernameNotFoundEvent; import net.sf.acegisecurity.providers.dao.event.AuthenticationSuccessEvent; import net.sf.acegisecurity.providers.encoding.PasswordEncoder; import net.sf.acegisecurity.providers.encoding.PlaintextPasswordEncoder; @@ -190,7 +192,19 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, if (user == null) { cacheWasUsed = false; - user = getUserFromBackend(username); + + try { + user = getUserFromBackend(username); + } catch (BadCredentialsException ex) { + if (this.context != null) { + context.publishEvent(new AuthenticationFailureUsernameNotFoundEvent( + authentication, + new User(username, "*****", false, + new GrantedAuthority[0]))); + } + + throw ex; + } } if (!user.isEnabled()) { diff --git a/core/src/main/java/org/acegisecurity/providers/dao/event/AuthenticationFailureUsernameNotFoundEvent.java b/core/src/main/java/org/acegisecurity/providers/dao/event/AuthenticationFailureUsernameNotFoundEvent.java new file mode 100644 index 0000000000..0a276f5411 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/dao/event/AuthenticationFailureUsernameNotFoundEvent.java @@ -0,0 +1,40 @@ +/* Copyright 2004 Acegi Technology Pty Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sf.acegisecurity.providers.dao.event; + +import net.sf.acegisecurity.Authentication; +import net.sf.acegisecurity.UserDetails; + + +/** + * Application event which indicates authentication failure due to nonexistent + * username. AuthenticationFailureUsernameNotFoundEvent.getUser() + * returns an instance of User, where the username is filled by + * the String provided at login attempt. The other properties are + * set to non-null values without any meaning. + * + * @author Karel Miarka + */ +public class AuthenticationFailureUsernameNotFoundEvent + extends AuthenticationEvent { + //~ Constructors =========================================================== + + // ~ Constructors =========================================================== + public AuthenticationFailureUsernameNotFoundEvent( + Authentication authentication, UserDetails user) { + super(authentication, user); + } +} diff --git a/core/src/main/java/org/acegisecurity/providers/dao/event/LoggerListener.java b/core/src/main/java/org/acegisecurity/providers/dao/event/LoggerListener.java index ec386415ec..a1bfa3622e 100644 --- a/core/src/main/java/org/acegisecurity/providers/dao/event/LoggerListener.java +++ b/core/src/main/java/org/acegisecurity/providers/dao/event/LoggerListener.java @@ -45,7 +45,8 @@ public class LoggerListener implements ApplicationListener { AuthenticationFailurePasswordEvent authEvent = (AuthenticationFailurePasswordEvent) event; if (logger.isWarnEnabled()) { - logger.warn("Authentication failed due to incorrect password for user: " + logger.warn( + "Authentication failed due to incorrect password for user: " + authEvent.getUser().getUsername() + "; details: " + authEvent.getAuthentication().getDetails()); } @@ -62,6 +63,17 @@ public class LoggerListener implements ApplicationListener { } } + if (event instanceof AuthenticationFailureUsernameNotFoundEvent) { + AuthenticationFailureUsernameNotFoundEvent authEvent = (AuthenticationFailureUsernameNotFoundEvent) event; + + if (logger.isWarnEnabled()) { + logger.warn( + "Authentication failed due to nonexistent username: " + + authEvent.getUser().getUsername() + "; details: " + + authEvent.getAuthentication().getDetails()); + } + } + if (event instanceof AuthenticationSuccessEvent) { AuthenticationSuccessEvent authEvent = (AuthenticationSuccessEvent) event; diff --git a/docs/reference/src/index.xml b/docs/reference/src/index.xml index 0212aa604e..ace73552f5 100644 --- a/docs/reference/src/index.xml +++ b/docs/reference/src/index.xml @@ -1018,22 +1018,31 @@ AuthenticationFailureDisabledEvent is published when an authentication request is unsuccessful because - the returned User is disabled. This is normally - the case when an account is locked. + the returned UserDetails is disabled. This is + normally the case when an account is locked. + + + + AuthenticationFailureUsernameNotFoundEvent + is published when an authentication request is unsuccessful + because the AuthenticationDao could not locate + the UserDetails. AuthenticationFailurePasswordEvent is published when an authentication request is unsuccessful because the presented password did not match that in the - User. + UserDetails. Each event contains two objects: the Authentication object that represented the authentication request, and the UserDetails object - that was found in response to the authentication request. The + that was found in response to the authentication request (clearly the + latter will be a dummy object in the case of + AuthenticationFailureUsernameNotFoundEvent). The Authentication interface provides a getDetails() method which often includes information that event consumers may find useful (eg the TCP/IP @@ -1150,8 +1159,7 @@ JaasAuthenticationProvider The JaasAuthenticationProvider attempts to - authenticate a user’s principal and credentials through JAAS. - + authenticate a user’s principal and credentials through JAAS. Let’s assume we have a JAAS login configuration file, /WEB-INF/login.conf, with the following