From 10c1926385ba51bf0790ffe5e2076217cb6f252c Mon Sep 17 00:00:00 2001 From: Ray Krueger Date: Fri, 25 Mar 2005 00:53:46 +0000 Subject: [PATCH] Added the ConcurrentSessionViolationEvent that will be published by the ConcurrentSessionControllerImpl before throwing the ConcurrentSessionViolationException --- .../ConcurrentSessionControllerImpl.java | 37 ++++++++++++++-- .../ConcurrentSessionViolationEvent.java | 42 +++++++++++++++++++ .../ConcurrentSessionControllerImplTests.java | 8 ++++ 3 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/org/acegisecurity/providers/ConcurrentSessionViolationEvent.java diff --git a/core/src/main/java/org/acegisecurity/providers/ConcurrentSessionControllerImpl.java b/core/src/main/java/org/acegisecurity/providers/ConcurrentSessionControllerImpl.java index b3a9104545..1244ef6bee 100644 --- a/core/src/main/java/org/acegisecurity/providers/ConcurrentSessionControllerImpl.java +++ b/core/src/main/java/org/acegisecurity/providers/ConcurrentSessionControllerImpl.java @@ -21,6 +21,9 @@ import net.sf.acegisecurity.AuthenticationTrustResolverImpl; import net.sf.acegisecurity.UserDetails; import net.sf.acegisecurity.ui.WebAuthenticationDetails; import net.sf.acegisecurity.ui.session.HttpSessionDestroyedEvent; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; @@ -46,17 +49,28 @@ import java.util.Set; * @author Ben Alex */ public class ConcurrentSessionControllerImpl - implements ConcurrentSessionController, ApplicationListener { + implements ConcurrentSessionController, ApplicationListener, + ApplicationContextAware { //~ Instance fields ======================================================== protected Map principalsToSessions = new HashMap(); protected Map sessionsToPrincipals = new HashMap(); protected Set sessionSet = new HashSet(); + private ApplicationContext applicationContext; private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl(); private int maxSessions = 1; //~ Methods ================================================================ + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } + + public ApplicationContext getApplicationContext() { + return applicationContext; + } + /** * Set the maximum number of sessions a user is allowed to have, defaults * to 1. Setting this to anything less than 1 will allow unlimited @@ -103,10 +117,12 @@ public class ConcurrentSessionControllerImpl * * @param request Used to retieve the {@link WebAuthenticationDetails} * @param response Used to store the sessionId for the current Principal + * @throws ConcurrentLoginException If the user is already logged in the + * maximum number of times * @see #determineSessionPrincipal(net.sf.acegisecurity.Authentication) */ public void afterAuthentication(Authentication request, - Authentication response) { + Authentication response) throws ConcurrentLoginException { enforceConcurrentLogins(response); if (request.getDetails() instanceof WebAuthenticationDetails) { @@ -121,8 +137,8 @@ public class ConcurrentSessionControllerImpl * {@link AuthenticationProvider}s * * @param request The Authentication in question - * @throws ConcurrentLoginException if the user has already met the {@link - * #setMaxSessions(int)} + * @throws ConcurrentLoginException If the user is already logged in the + * maximum number of times #setMaxSessions(int)} */ public void beforeAuthentication(Authentication request) throws ConcurrentLoginException { @@ -250,6 +266,9 @@ public class ConcurrentSessionControllerImpl if (!isActiveSession(principal, sessionId)) { if (maxSessions == countSessions(principal)) { + //Publish the event + publishViolationEvent(request); + //The user is AT their max, toss them out throw new ConcurrentLoginException(principal + " has reached the maximum concurrent logins"); @@ -258,6 +277,16 @@ public class ConcurrentSessionControllerImpl } } + /** + * Publish the even to the application context. + * The default action is to publish a new {@link ConcurrentSessionViolationEvent} + * + * @param auth The authentication object that caused the violation + */ + protected void publishViolationEvent(Authentication auth) { + getApplicationContext().publishEvent(new ConcurrentSessionViolationEvent(auth)); + } + /** * Remove the given sessionId from storage. Used by {@link * #onApplicationEvent(org.springframework.context.ApplicationEvent)} for diff --git a/core/src/main/java/org/acegisecurity/providers/ConcurrentSessionViolationEvent.java b/core/src/main/java/org/acegisecurity/providers/ConcurrentSessionViolationEvent.java new file mode 100644 index 0000000000..dd55dc3bb0 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/ConcurrentSessionViolationEvent.java @@ -0,0 +1,42 @@ +/* Copyright 2004, 2005 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; + +import net.sf.acegisecurity.Authentication; + +import org.springframework.context.ApplicationEvent; + + +/** + * Published by the ConcurrentSessionControllerImpl to notify the application + * context that a user has attempted to login more than the maximum times + * allowed by the {@link ConcurrentSessionControllerImpl#setMaxSessions(int)} + * + * @author Ray Krueger + */ +public class ConcurrentSessionViolationEvent extends ApplicationEvent { + //~ Constructors =========================================================== + + public ConcurrentSessionViolationEvent(Authentication auth) { + super(auth); + } + + //~ Methods ================================================================ + + public Authentication getAuthentication() { + return (Authentication) getSource(); + } +} diff --git a/core/src/test/java/org/acegisecurity/providers/ConcurrentSessionControllerImplTests.java b/core/src/test/java/org/acegisecurity/providers/ConcurrentSessionControllerImplTests.java index d1d459489c..28539dfd57 100644 --- a/core/src/test/java/org/acegisecurity/providers/ConcurrentSessionControllerImplTests.java +++ b/core/src/test/java/org/acegisecurity/providers/ConcurrentSessionControllerImplTests.java @@ -141,6 +141,14 @@ public class ConcurrentSessionControllerImplTests extends TestCase { target.afterAuthentication(different, different); } + public void testEventObject() { + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("user", + "password"); + ConcurrentSessionViolationEvent ev = new ConcurrentSessionViolationEvent(token); + assertEquals("The token that went in should be the token that comes out", + token, ev.getAuthentication()); + } + public void testImplementsApplicationListener() throws Exception { assertTrue("This class must implement ApplicationListener, and at one point it didn't.", target instanceof ApplicationListener);