1
0
mirror of synced 2026-08-31 22:46:02 +00:00

Add event capabilities.

This commit is contained in:
Ben Alex
2004-05-24 00:09:27 +00:00
parent 42ccbfbad7
commit d5c14142d1
11 changed files with 564 additions and 7 deletions
@@ -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;
* <code>UsernamePasswordAuthenticationToken</code>. This avoids complications
* if the user changes their password during the session.
* </p>
*
* <P>
* 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.
* </p>
*
* @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());
}
@@ -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 a <code>net.sf.acegisecurity.provider.dao</code> application
* event.
*
* <P>
* 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.
* </p>
*
* <P>
* The <code>ApplicationEvent</code>'s <code>source</code> will be the
* <code>Authentication</code> object.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public abstract class AuthenticationEvent extends ApplicationEvent {
//~ Instance fields ========================================================
private User user;
//~ Constructors ===========================================================
public AuthenticationEvent(Authentication authentication, User user) {
super(authentication);
// No need to check authentication isn't null, as done by super
if (user == null) {
throw new IllegalArgumentException("User is required");
}
this.user = user;
}
//~ Methods ================================================================
/**
* Getters for the <code>Authentication</code> request that caused the
* event. Also available from <code>super.getSource()</code>.
*
* @return the authentication request
*/
public Authentication getAuthentication() {
return (Authentication) super.getSource();
}
/**
* Getter for the <code>User</code> related to the
* <code>Authentication</code> attempt.
*
* @return the user
*/
public User getUser() {
return user;
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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.
*
* <P>
* All authentication failures are logged at the warning level, whilst
* authentication successes are logged at the information level.
* </p>
*
* @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());
}
}
}
}
@@ -0,0 +1,9 @@
<html>
<body>
Enables events to be published to the Spring application context.
<P>The <code>DaoAuthenticationProvider</code> automatically publishes
events to the application context. These events are received by all
registered Spring <code>ApplicationListener</code>s.</P>
</body>
</html>
@@ -0,0 +1,105 @@
/* 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 junit.framework.TestCase;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.GrantedAuthorityImpl;
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import net.sf.acegisecurity.providers.dao.User;
/**
* Tests {@link AuthenticationEvent} and its subclasses.
*
* @author Ben Alex
* @version $Id$
*/
public class AuthenticationEventTests extends TestCase {
//~ Methods ================================================================
public final void setUp() throws Exception {
super.setUp();
}
public static void main(String[] args) {
junit.textui.TestRunner.run(AuthenticationEventTests.class);
}
public void testDisabledEvent() {
Authentication auth = getAuthentication();
User user = getUser();
AuthenticationFailureDisabledEvent event = new AuthenticationFailureDisabledEvent(auth,
user);
assertEquals(auth, event.getAuthentication());
assertEquals(user, event.getUser());
}
public void testPasswordEvent() {
Authentication auth = getAuthentication();
User user = getUser();
AuthenticationFailurePasswordEvent event = new AuthenticationFailurePasswordEvent(auth,
user);
assertEquals(auth, event.getAuthentication());
assertEquals(user, event.getUser());
}
public void testRejectsNullAuthentication() {
try {
AuthenticationFailureDisabledEvent event = new AuthenticationFailureDisabledEvent(null,
getUser());
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
public void testRejectsNullUser() {
try {
AuthenticationFailureDisabledEvent event = new AuthenticationFailureDisabledEvent(getAuthentication(),
null);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
public void testSuccessEvent() {
Authentication auth = getAuthentication();
User user = getUser();
AuthenticationSuccessEvent event = new AuthenticationSuccessEvent(auth,
user);
assertEquals(auth, event.getAuthentication());
assertEquals(user, event.getUser());
}
private Authentication getAuthentication() {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken("Principal",
"Credentials");
authentication.setDetails("127.0.0.1");
return authentication;
}
private User getUser() {
User user = new User("foo", "bar", true,
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_FOOBAR")});
return user;
}
}
@@ -0,0 +1,82 @@
/* 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 junit.framework.TestCase;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.GrantedAuthorityImpl;
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import net.sf.acegisecurity.providers.dao.User;
/**
* Tests {@link LoggerListener}.
*
* @author Ben Alex
* @version $Id$
*/
public class LoggerListenerTests extends TestCase {
//~ Methods ================================================================
public final void setUp() throws Exception {
super.setUp();
}
public static void main(String[] args) {
junit.textui.TestRunner.run(LoggerListenerTests.class);
}
public void testLogsDisabledEvents() {
AuthenticationFailureDisabledEvent event = new AuthenticationFailureDisabledEvent(getAuthentication(),
getUser());
LoggerListener listener = new LoggerListener();
listener.onApplicationEvent(event);
assertTrue(true);
}
public void testLogsPasswordEvents() {
AuthenticationFailurePasswordEvent event = new AuthenticationFailurePasswordEvent(getAuthentication(),
getUser());
LoggerListener listener = new LoggerListener();
listener.onApplicationEvent(event);
assertTrue(true);
}
public void testLogsSuccessEvents() {
AuthenticationSuccessEvent event = new AuthenticationSuccessEvent(getAuthentication(),
getUser());
LoggerListener listener = new LoggerListener();
listener.onApplicationEvent(event);
assertTrue(true);
}
private Authentication getAuthentication() {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken("Principal",
"Credentials");
authentication.setDetails("127.0.0.1");
return authentication;
}
private User getUser() {
User user = new User("foo", "bar", true,
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_FOOBAR")});
return user;
}
}