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 947cb61f37..288ce23910 100644
--- a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java
+++ b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java
@@ -22,11 +22,18 @@ import net.sf.acegisecurity.BadCredentialsException;
import net.sf.acegisecurity.DisabledException;
import net.sf.acegisecurity.providers.AuthenticationProvider;
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+import net.sf.acegisecurity.providers.dao.event.AuthenticationFailureDisabledEvent;
+import net.sf.acegisecurity.providers.dao.event.AuthenticationFailurePasswordEvent;
+import net.sf.acegisecurity.providers.dao.event.AuthenticationSuccessEvent;
import net.sf.acegisecurity.providers.encoding.PasswordEncoder;
import net.sf.acegisecurity.providers.encoding.PlaintextPasswordEncoder;
+import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
import org.springframework.dao.DataAccessException;
import java.util.Date;
@@ -56,14 +63,23 @@ import java.util.Date;
* UsernamePasswordAuthenticationToken. This avoids complications
* if the user changes their password during the session.
*
+ * If an application context is detected (which is automatically the case when + * the bean is started within a Spring container), application events will be + * published to the context. See {@link + * net.sf.acegisecurity.providers.dao.event.AuthenticationEvent} for further + * information. + *
* * @author Ben Alex * @version $Id$ */ public class DaoAuthenticationProvider implements AuthenticationProvider, - InitializingBean { + InitializingBean, ApplicationContextAware { //~ Instance fields ======================================================== + private ApplicationContext ctx; private AuthenticationDao authenticationDao; private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder(); private SaltSource saltSource; @@ -72,6 +88,11 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, //~ Methods ================================================================ + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.ctx = applicationContext; + } + public void setAuthenticationDao(AuthenticationDao authenticationDao) { this.authenticationDao = authenticationDao; } @@ -175,6 +196,15 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, throw new AuthenticationServiceException(repositoryProblem .getMessage(), repositoryProblem); } + + if (!user.isEnabled()) { + if (this.ctx != null) { + ctx.publishEvent(new AuthenticationFailureDisabledEvent( + authentication, user)); + } + + throw new DisabledException("User is disabled"); + } if (!(authentication instanceof DaoAuthenticationToken)) { // Must validate credentials, as this is not simply a token refresh @@ -186,17 +216,22 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, if (!passwordEncoder.isPasswordValid(user.getPassword(), authentication.getCredentials().toString(), salt)) { + if (this.ctx != null) { + ctx.publishEvent(new AuthenticationFailurePasswordEvent( + authentication, user)); + } + throw new BadCredentialsException("Bad credentials presented"); } } - if (!user.isEnabled()) { - throw new DisabledException("User is disabled"); - } - Date expiry = new Date(new Date().getTime() + this.getRefreshTokenInterval()); + if (this.ctx != null) { + ctx.publishEvent(new AuthenticationSuccessEvent(authentication, user)); + } + return new DaoAuthenticationToken(this.getKey(), expiry, user.getUsername(), user.getPassword(), user.getAuthorities()); } diff --git a/core/src/main/java/org/acegisecurity/providers/dao/event/AuthenticationEvent.java b/core/src/main/java/org/acegisecurity/providers/dao/event/AuthenticationEvent.java new file mode 100644 index 0000000000..2fd5fc28c9 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/dao/event/AuthenticationEvent.java @@ -0,0 +1,83 @@ +/* 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.providers.dao.User; + +import org.springframework.context.ApplicationEvent; + + +/** + * Represents anet.sf.acegisecurity.provider.dao application
+ * event.
+ *
+ * + * Subclasses exist for different types of authentication events. All + * authentication events relate to a particular {@link User} and are caused by + * a particular {@link Authentication} object. This is intended to permit + * logging of successful and unsuccessful login attempts, and facilitate the + * locking of accounts. + *
+ * + *
+ * The ApplicationEvent's source will be the
+ * Authentication object.
+ *
Authentication request that caused the
+ * event. Also available from super.getSource().
+ *
+ * @return the authentication request
+ */
+ public Authentication getAuthentication() {
+ return (Authentication) super.getSource();
+ }
+
+ /**
+ * Getter for the User related to the
+ * Authentication attempt.
+ *
+ * @return the user
+ */
+ public User getUser() {
+ return user;
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/providers/dao/event/AuthenticationFailureDisabledEvent.java b/core/src/main/java/org/acegisecurity/providers/dao/event/AuthenticationFailureDisabledEvent.java
new file mode 100644
index 0000000000..7fda5fa7b8
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/dao/event/AuthenticationFailureDisabledEvent.java
@@ -0,0 +1,36 @@
+/* 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.providers.dao.User;
+
+
+/**
+ * Application event which indicates authentication failure due to the user's
+ * account being locked.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AuthenticationFailureDisabledEvent extends AuthenticationEvent {
+ //~ Constructors ===========================================================
+
+ public AuthenticationFailureDisabledEvent(Authentication authentication,
+ User user) {
+ super(authentication, user);
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/providers/dao/event/AuthenticationFailurePasswordEvent.java b/core/src/main/java/org/acegisecurity/providers/dao/event/AuthenticationFailurePasswordEvent.java
new file mode 100644
index 0000000000..ec65c5a212
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/dao/event/AuthenticationFailurePasswordEvent.java
@@ -0,0 +1,36 @@
+/* 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.providers.dao.User;
+
+
+/**
+ * Application event which indicates authentication failure due to invalid
+ * password.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AuthenticationFailurePasswordEvent extends AuthenticationEvent {
+ //~ Constructors ===========================================================
+
+ public AuthenticationFailurePasswordEvent(Authentication authentication,
+ User user) {
+ super(authentication, user);
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/providers/dao/event/AuthenticationSuccessEvent.java b/core/src/main/java/org/acegisecurity/providers/dao/event/AuthenticationSuccessEvent.java
new file mode 100644
index 0000000000..d379778361
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/dao/event/AuthenticationSuccessEvent.java
@@ -0,0 +1,34 @@
+/* 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.providers.dao.User;
+
+
+/**
+ * Application event which indicates successful authentication.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AuthenticationSuccessEvent extends AuthenticationEvent {
+ //~ Constructors ===========================================================
+
+ public AuthenticationSuccessEvent(Authentication authentication, User 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
new file mode 100644
index 0000000000..ec386415ec
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/dao/event/LoggerListener.java
@@ -0,0 +1,75 @@
+/* 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 org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+
+
+/**
+ * Outputs authentication-related application events to Commons Logging.
+ *
+ * + * All authentication failures are logged at the warning level, whilst + * authentication successes are logged at the information level. + *
+ * + * @author Ben Alex + * @version $Id$ + */ +public class LoggerListener implements ApplicationListener { + //~ Static fields/initializers ============================================= + + private static final Log logger = LogFactory.getLog(LoggerListener.class); + + //~ Methods ================================================================ + + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof AuthenticationFailurePasswordEvent) { + AuthenticationFailurePasswordEvent authEvent = (AuthenticationFailurePasswordEvent) event; + + if (logger.isWarnEnabled()) { + logger.warn("Authentication failed due to incorrect password for user: " + + authEvent.getUser().getUsername() + "; details: " + + authEvent.getAuthentication().getDetails()); + } + } + + if (event instanceof AuthenticationFailureDisabledEvent) { + AuthenticationFailureDisabledEvent authEvent = (AuthenticationFailureDisabledEvent) event; + + if (logger.isWarnEnabled()) { + logger.warn( + "Authentication failed due to account being disabled for user: " + + authEvent.getUser().getUsername() + "; details: " + + authEvent.getAuthentication().getDetails()); + } + } + + if (event instanceof AuthenticationSuccessEvent) { + AuthenticationSuccessEvent authEvent = (AuthenticationSuccessEvent) event; + + if (logger.isInfoEnabled()) { + logger.info("Authentication success for user: " + + authEvent.getUser().getUsername() + "; details: " + + authEvent.getAuthentication().getDetails()); + } + } + } +} diff --git a/core/src/main/java/org/acegisecurity/providers/dao/event/package.html b/core/src/main/java/org/acegisecurity/providers/dao/event/package.html new file mode 100644 index 0000000000..07dac2ec55 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/dao/event/package.html @@ -0,0 +1,9 @@ + + +Enables events to be published to the Spring application context. + +The DaoAuthenticationProvider automatically publishes
+events to the application context. These events are received by all
+registered Spring ApplicationListeners.