diff --git a/core/src/main/java/org/acegisecurity/providers/ConcurrentLoginException.java b/core/src/main/java/org/acegisecurity/providers/ConcurrentLoginException.java new file mode 100644 index 0000000000..f7a2381616 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/ConcurrentLoginException.java @@ -0,0 +1,33 @@ +/* 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.AuthenticationException; + + +/** + * Thrown by the ConcurrentSessionController when the number of sessions + * allowed is attempting to be exceeded. + * + * @author Ray Krueger + */ +public class ConcurrentLoginException extends AuthenticationException { + //~ Constructors =========================================================== + + public ConcurrentLoginException(String msg) { + super(msg); + } +} diff --git a/core/src/main/java/org/acegisecurity/providers/ConcurrentSessionController.java b/core/src/main/java/org/acegisecurity/providers/ConcurrentSessionController.java new file mode 100644 index 0000000000..0bdcd86e7c --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/ConcurrentSessionController.java @@ -0,0 +1,36 @@ +/* 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 net.sf.acegisecurity.AuthenticationException; + + +/** + * See: {@link ConcurrentSessionControllerImpl} + * + * @author Ray Krueger + * @see ConcurrentSessionControllerImpl + */ +public interface ConcurrentSessionController { + //~ Methods ================================================================ + + void afterAuthentication(Authentication initialAuth, Authentication result) + throws AuthenticationException; + + void beforeAuthentication(Authentication initialAuth) + throws AuthenticationException; +} diff --git a/core/src/main/java/org/acegisecurity/providers/ConcurrentSessionControllerImpl.java b/core/src/main/java/org/acegisecurity/providers/ConcurrentSessionControllerImpl.java new file mode 100644 index 0000000000..a56a1e20bf --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/ConcurrentSessionControllerImpl.java @@ -0,0 +1,290 @@ +/* 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 net.sf.acegisecurity.AuthenticationTrustResolver; +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.context.ApplicationEvent; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.HttpSession; + + +/** + * Used by the {@link ProviderManager} to track Authentications and their + * respective sessions. A given user is allowed {@link #setMaxSessions(int)} + * sessions. If they attempt to exceed that ammount a {@link + * ConcurrentLoginException} will be thrown. The + * ConcurrentSessionControllerImpl class will listen for {@link + * HttpSessionDestroyedEvent}s in the ApplicationContext to remove a session + * from the internal tracking. This class will not function properly + * without a {@link net.sf.acegisecurity.ui.session.HttpSessionEventPublisher} + * configured in web.xml. + * + * @author Ray Krueger + * @author Ben Alex + */ +public class ConcurrentSessionControllerImpl + implements ConcurrentSessionController { + //~ Instance fields ======================================================== + + protected Map principalsToSessions = new HashMap(); + protected Map sessionsToPrincipals = new HashMap(); + protected Set sessionSet = new HashSet(); + private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl(); + private int maxSessions = 1; + + //~ Methods ================================================================ + + /** + * 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 + * sessions + * + * @param maxSessions + */ + public void setMaxSessions(int maxSessions) { + this.maxSessions = maxSessions; + } + + /** + * The maximum sessions per user. + * + * @return int + */ + public int getMaxSessions() { + return maxSessions; + } + + /** + * The trustResolver to use for determining Anonymous users and ignoring + * them. Defaults to {@link AuthenticationTrustResolverImpl} + * + * @param trustResolver + */ + public void setTrustResolver(AuthenticationTrustResolver trustResolver) { + this.trustResolver = trustResolver; + } + + /** + * Get the configured AuthenticationTrustResolver + * + * @return The configured AuthenticationTrustResolver or {@link + * AuthenticationTrustResolverImpl} by default. + */ + public AuthenticationTrustResolver getTrustResolver() { + return trustResolver; + } + + /** + * Called by the {@link ProviderManager} after receiving a response from a + * configured AuthenticationProvider. + * + * @param request Used to retieve the {@link WebAuthenticationDetails} + * @param response Used to store the sessionId for the current Principal + * + * @see #determineSessionPrincipal(net.sf.acegisecurity.Authentication) + */ + public void afterAuthentication(Authentication request, + Authentication response) { + enforceConcurrentLogins(response); + + if (request.getDetails() instanceof WebAuthenticationDetails) { + String sessionId = ((WebAuthenticationDetails) request.getDetails()) + .getSessionId(); + addSession(determineSessionPrincipal(response), sessionId); + } + } + + /** + * Called by the {@link ProviderManager} before iterating the configured + * {@link AuthenticationProvider}s + * + * @param request The Authentication in question + * + * @throws ConcurrentLoginException if the user has already met the {@link + * #setMaxSessions(int)} + */ + public void beforeAuthentication(Authentication request) + throws ConcurrentLoginException { + enforceConcurrentLogins(request); + } + + /** + * Checks for {@link HttpSessionDestroyedEvent}s and calls {@link + * #removeSession(String)} for the destoyed HttpSessions id. + * + * @param event + */ + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof HttpSessionDestroyedEvent) { + String sessionId = ((HttpSession) event.getSource()).getId(); + removeSession(sessionId); + } + } + + /** + * Compares the sessionIds stored for the given principal to determine if + * the given sessionId is new or existing. + * + * @param principal The principal in question + * @param sessionId The new or existing sessionId + * + * @return true if it's the same as a session already in use, false if it + * is a new session + */ + protected boolean isActiveSession(Object principal, String sessionId) { + Set sessions = (Set) principalsToSessions.get(principal); + + if (sessions == null) { + return false; + } + + return sessions.contains(sessionId); + } + + /** + * Updates internal maps with the sessionId for the given principal. Can be + * overridden by subclasses to provide a specialized means of principal + * -> session tracking. + * + * @param principal + * @param sessionId + */ + protected void addSession(Object principal, String sessionId) { + Set sessions = (Set) principalsToSessions.get(principal); + + if (sessions == null) { + sessions = new HashSet(); + principalsToSessions.put(principal, sessions); + } + + sessions.add(sessionId); + sessionsToPrincipals.put(sessionId, principal); + } + + /** + * Counts the number of sessions in use by the given principal + * + * @param principal The principal object + * + * @return 0 if there are no sessions, > if there are any + */ + protected int countSessions(Object principal) { + Set set = (Set) principalsToSessions.get(principal); + + if (set == null) { + return 0; + } + + return set.size(); + } + + /** + * Checks to see if the Authentication principal is of type UserDetails. If + * it is then the {@link net.sf.acegisecurity.UserDetails#getUsername()} + * is returned. Otherwise Authentication.getPrincipal().toString() is + * returned. Subclasses can override this method to provide a more + * specific implementation. + * + * @param auth The Authentication in question + * + * @return The principal to be used as the key against sessions + */ + protected Object determineSessionPrincipal(Authentication auth) { + if (auth.getPrincipal() instanceof UserDetails) { + return ((UserDetails) auth.getPrincipal()).getUsername(); + } else { + return auth.getPrincipal().toString(); + } + } + + /** + * Called by both the beforeAuthentication and afterAuthentication methods. + * Anonymous requests as determined by the configured {@link + * AuthenticationTrustResolver} are ignored. If the details are + * WebAuthenticationDetails, get the sessionId and and the principal off + * of the authentication using the {@link + * #determineSessionPrincipal(net.sf.acegisecurity.Authentication)} + * method. Uses the sessionId and principal to determine if the session + * is new, and if the user is already at the maxSessions value. Subclasses + * may override for more specific functionality + * + * @param request Authentication being evaluated + * + * @throws ConcurrentLoginException If the session is new, and the user is + * already at maxSessions + */ + protected void enforceConcurrentLogins(Authentication request) + throws ConcurrentLoginException { + //If the max is less than 1, sessions are unlimited + if (maxSessions < 1) { + return; + } + + //If it is an anonymous user, ignore them + if (trustResolver.isAnonymous(request)) { + return; + } + + if (request.getDetails() instanceof WebAuthenticationDetails) { + String sessionId = ((WebAuthenticationDetails) request.getDetails()) + .getSessionId(); + + Object principal = determineSessionPrincipal(request); + + if (!isActiveSession(principal, sessionId)) { + if (maxSessions == countSessions(principal)) { + //The user is AT their max, toss them out + throw new ConcurrentLoginException(principal + + " has reached the maximum concurrent logins"); + } + } + } + } + + /** + * Remove the given sessionId from storage. Used by {@link + * #onApplicationEvent(org.springframework.context.ApplicationEvent)} for + * HttpSessionDestroyedEvent + * + * @param sessionId + */ + protected void removeSession(String sessionId) { + // find out which principal is associated with this sessionId + Object associatedPrincipal = sessionsToPrincipals.get(sessionId); + + if (associatedPrincipal != null) { + Set sessions = (Set) principalsToSessions.get(associatedPrincipal); + sessions.remove(sessionId); + + if (sessions.isEmpty()) { + principalsToSessions.remove(associatedPrincipal); + } + + sessionsToPrincipals.remove(sessionId); + } + } +} diff --git a/core/src/main/java/org/acegisecurity/providers/NullConcurrentSessionController.java b/core/src/main/java/org/acegisecurity/providers/NullConcurrentSessionController.java new file mode 100644 index 0000000000..e84e8b6f4c --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/NullConcurrentSessionController.java @@ -0,0 +1,41 @@ +/* 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 net.sf.acegisecurity.AuthenticationException; + + +/** + * Do nothing implementation of ConcurrentSessionController. This is the {@link ProviderManager} default + * + * @author Ray Krueger + * @see ConcurrentSessionControllerImpl + */ +public class NullConcurrentSessionController + implements ConcurrentSessionController { + //~ Methods ================================================================ + + public void afterAuthentication(Authentication initialAuth, + Authentication result) throws AuthenticationException { + //Do nothing + } + + public void beforeAuthentication(Authentication initialAuth) + throws AuthenticationException { + //Do nothing + } +} diff --git a/core/src/main/java/org/acegisecurity/providers/ProviderManager.java b/core/src/main/java/org/acegisecurity/providers/ProviderManager.java index f1e9b75472..ef01b7445b 100644 --- a/core/src/main/java/org/acegisecurity/providers/ProviderManager.java +++ b/core/src/main/java/org/acegisecurity/providers/ProviderManager.java @@ -1,4 +1,4 @@ -/* Copyright 2004 Acegi Technology Pty Limited +/* 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. @@ -30,11 +30,16 @@ import java.util.List; /** * Iterates an {@link Authentication} request through a list of {@link - * AuthenticationProvider}s. + * AuthenticationProvider}s. Can optionally be configured with a {@link + * ConcurrentSessionController} to limit the number of sessions a user can + * have. * * @author Ben Alex * @author Wesley Hall + * @author Ray Krueger * @version $Id$ + * + * @see ConcurrentSessionController */ public class ProviderManager extends AbstractAuthenticationManager implements InitializingBean { @@ -44,6 +49,7 @@ public class ProviderManager extends AbstractAuthenticationManager //~ Instance fields ======================================================== + private ConcurrentSessionController sessionController = new NullConcurrentSessionController(); private List providers; //~ Methods ================================================================ @@ -82,6 +88,29 @@ public class ProviderManager extends AbstractAuthenticationManager return this.providers; } + /** + * Set the {@link ConcurrentSessionController} to be used for limiting + * user's sessions. The {@link NullConcurrentSessionController} is used + * by default + * + * @param sessionController {@link ConcurrentSessionController} + */ + public void setSessionController( + ConcurrentSessionController sessionController) { + this.sessionController = sessionController; + } + + /** + * The configured {@link ConcurrentSessionController} is returned or the + * {@link NullConcurrentSessionController} if a specific one has not been + * set. + * + * @return{@link ConcurrentSessionController} instance + */ + public ConcurrentSessionController getSessionController() { + return sessionController; + } + public void afterPropertiesSet() throws Exception { checkIfValidList(this.providers); } @@ -117,6 +146,8 @@ public class ProviderManager extends AbstractAuthenticationManager Class toTest = authentication.getClass(); + sessionController.beforeAuthentication(authentication); + while (iter.hasNext()) { AuthenticationProvider provider = (AuthenticationProvider) iter .next(); @@ -128,6 +159,8 @@ public class ProviderManager extends AbstractAuthenticationManager Authentication result = provider.authenticate(authentication); if (result != null) { + sessionController.afterAuthentication(authentication, result); + return result; } } diff --git a/core/src/test/java/org/acegisecurity/providers/ConcurrentSessionControllerImplTests.java b/core/src/test/java/org/acegisecurity/providers/ConcurrentSessionControllerImplTests.java new file mode 100644 index 0000000000..0ffba888e4 --- /dev/null +++ b/core/src/test/java/org/acegisecurity/providers/ConcurrentSessionControllerImplTests.java @@ -0,0 +1,244 @@ +/* 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 junit.framework.TestCase; +import net.sf.acegisecurity.*; +import net.sf.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; +import net.sf.acegisecurity.providers.dao.User; +import net.sf.acegisecurity.ui.WebAuthenticationDetails; +import net.sf.acegisecurity.ui.session.HttpSessionCreatedEvent; +import net.sf.acegisecurity.ui.session.HttpSessionDestroyedEvent; + +import java.security.Principal; + + +/** + * Tests for {@link ConcurrentSessionControllerImpl} + * + * @author Ray Krueger + */ +public class ConcurrentSessionControllerImplTests extends TestCase { + //~ Instance fields ======================================================== + + ConcurrentSessionControllerImpl target = new ConcurrentSessionControllerImpl(); + + //~ Methods ================================================================ + + public void testBumpCoverage() throws Exception { + target.onApplicationEvent(new HttpSessionCreatedEvent(new MockHttpSession())); + } + + public void testEnforcementKnownGood() throws Exception { + Authentication auth = createAuthentication("user", "password", "session"); + target.beforeAuthentication(auth); + target.afterAuthentication(auth, auth); + } + + public void testEnforcementMultipleSessions() throws Exception { + target.setMaxSessions(5); + + Authentication auth = null; + + for (int i = 0; i < 5; i++) { + auth = createAuthentication("user", "password", String.valueOf(i)); + target.beforeAuthentication(auth); + target.afterAuthentication(auth, auth); + } + + try { + auth = createAuthentication("user", "password", "lastsession"); + target.beforeAuthentication(auth); + fail("Only allowed 5 sessions, this should have thrown a ConcurrentLoginException"); + } catch (ConcurrentLoginException e) { + assertTrue(e.getMessage().startsWith(auth.getPrincipal().toString())); + } + } + + public void testEnforcementSingleSession() throws Exception { + target.setMaxSessions(1); + + Authentication auth = createAuthentication("user", "password", + "session1"); + + target.beforeAuthentication(auth); + target.afterAuthentication(auth, auth); + + try { + target.beforeAuthentication(createAuthentication("user", + "password", "session2")); + fail("Only allowed 1 session, this should have thrown a ConcurrentLoginException"); + } catch (ConcurrentLoginException e) { + } + } + + public void testEnforcementUnlimitedSameSession() throws Exception { + target.setMaxSessions(1); + + for (int i = 0; i < 100; i++) { + Authentication auth = createAuthentication("user", "password", + "samesession"); + target.beforeAuthentication(auth); + target.afterAuthentication(auth, auth); + } + } + + public void testEnforcementUnlimitedSessions() throws Exception { + target.setMaxSessions(0); + + for (int i = 0; i < 100; i++) { + Authentication auth = createAuthentication("user", "password", + String.valueOf(i)); + target.beforeAuthentication(auth); + target.afterAuthentication(auth, auth); + } + } + + public void testEventHandler() throws Exception { + target.setMaxSessions(1); + + UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("user", + "password"); + MockHttpSession session = new MockHttpSession(); + MockHttpServletRequest request = new MockHttpServletRequest(auth, + session); + auth.setDetails(new WebAuthenticationDetails(request)); + + target.beforeAuthentication(auth); + target.afterAuthentication(auth, auth); + + target.onApplicationEvent(new HttpSessionDestroyedEvent(session)); + + Authentication different = createAuthentication("user", "password", + "differentsession"); + target.beforeAuthentication(different); + target.afterAuthentication(different, different); + } + + public void testNonWebDetails() throws Exception { + UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("asdf", + "asdf"); + auth.setDetails("Hi there"); + target.beforeAuthentication(auth); + target.afterAuthentication(auth, auth); + } + + public void testPrincipals() throws Exception { + target.setMaxSessions(1); + + final UserDetails user = new User("user", "password", true, true, true, + new GrantedAuthority[0]); + final UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, + "password", user.getAuthorities()); + auth.setDetails(createWebDetails(auth, "session1")); + + target.beforeAuthentication(auth); + target.afterAuthentication(auth, auth); + + try { + UsernamePasswordAuthenticationToken otherAuth = new UsernamePasswordAuthenticationToken(new Principal() { + public String getName() { + return "user"; + } + + public String toString() { + return getName(); + } + }, "password"); + + otherAuth.setDetails(createWebDetails(otherAuth, "session2")); + target.beforeAuthentication(otherAuth); + fail("Same principal, different principal type, different session should have thrown ConcurrentLoginException"); + } catch (ConcurrentLoginException e) { + } + } + + public void testSetMax() throws Exception { + target.setMaxSessions(1); + assertEquals(1, target.getMaxSessions()); + + target.setMaxSessions(2); + assertEquals(2, target.getMaxSessions()); + } + + public void testSetTrustManager() throws Exception { + assertNotNull("There is supposed to be a default AuthenticationTrustResolver", + target.getTrustResolver()); + + AuthenticationTrustResolverImpl impl = new AuthenticationTrustResolverImpl(); + target.setTrustResolver(impl); + assertEquals(impl, target.getTrustResolver()); + } + + public void testUtilityMethods() throws Exception { + Object key = new Object(); + + target.addSession(key, "1"); + target.addSession(key, "2"); + target.addSession(key, "3"); + + target.removeSession("2"); + + assertFalse(target.isActiveSession(key, "2")); + assertTrue(target.isActiveSession(key, "1")); + assertTrue(target.isActiveSession(key, "3")); + + assertNull(target.sessionsToPrincipals.get("2")); + + assertEquals(2, target.countSessions(key)); + target.addSession(key, "2"); + assertEquals(3, target.countSessions(key)); + + target.addSession(key, "2"); + target.addSession(key, "2"); + assertEquals(3, target.countSessions(key)); + + assertTrue(target.isActiveSession(key, "1")); + assertTrue(target.isActiveSession(key, "2")); + assertTrue(target.isActiveSession(key, "3")); + + assertFalse(target.isActiveSession(key, "nope")); + + assertFalse(target.isActiveSession(new Object(), "1")); + assertFalse(target.isActiveSession(new Object(), "1")); + + target.removeSession("nothing to see here"); + } + + private Authentication createAuthentication(String user, String password, + String sessionId) { + UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, + password); + auth.setDetails(createWebDetails(auth, sessionId)); + + return auth; + } + + private WebAuthenticationDetails createWebDetails(Authentication auth, + String sessionId) { + MockHttpSession session = new MockHttpSession(sessionId); + MockHttpServletRequest request = new MockHttpServletRequest(auth, + session); + + return new WebAuthenticationDetails(request); + } + + public void testAnonymous() throws Exception { + AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken("blah", "anon", new GrantedAuthority[]{new GrantedAuthorityImpl("ROLE_ANON")}); + target.beforeAuthentication(auth); + target.afterAuthentication(auth, auth); + } +} diff --git a/core/src/test/java/org/acegisecurity/providers/ProviderManagerTests.java b/core/src/test/java/org/acegisecurity/providers/ProviderManagerTests.java index a8c8a119af..2b7fcdf13f 100644 --- a/core/src/test/java/org/acegisecurity/providers/ProviderManagerTests.java +++ b/core/src/test/java/org/acegisecurity/providers/ProviderManagerTests.java @@ -1,4 +1,4 @@ -/* Copyright 2004 Acegi Technology Pty Limited +/* 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. @@ -17,11 +17,7 @@ package net.sf.acegisecurity.providers; import junit.framework.TestCase; -import net.sf.acegisecurity.Authentication; -import net.sf.acegisecurity.AuthenticationException; -import net.sf.acegisecurity.AuthenticationServiceException; -import net.sf.acegisecurity.GrantedAuthority; -import net.sf.acegisecurity.GrantedAuthorityImpl; +import net.sf.acegisecurity.*; import java.util.List; import java.util.Vector; @@ -110,6 +106,19 @@ public class ProviderManagerTests extends TestCase { assertEquals("ROLE_TWO", castResult.getAuthorities()[1].getAuthority()); } + public void testConcurrentSessionControllerConfiguration() + throws Exception { + ProviderManager target = new ProviderManager(); + + //The NullConcurrentSessionController should be the default + assertNotNull(target.getSessionController()); + assertTrue(target.getSessionController() instanceof NullConcurrentSessionController); + + ConcurrentSessionControllerImpl impl = new ConcurrentSessionControllerImpl(); + target.setSessionController(impl); + assertEquals(impl, target.getSessionController()); + } + public void testStartupFailsIfProviderListDoesNotContainingProviders() throws Exception { List providers = new Vector(); diff --git a/doc/xdocs/upgrade/upgrade-070-080.html b/doc/xdocs/upgrade/upgrade-070-080.html index 9e403fe189..d51d7a4650 100644 --- a/doc/xdocs/upgrade/upgrade-070-080.html +++ b/doc/xdocs/upgrade/upgrade-070-080.html @@ -26,7 +26,11 @@ applications: SecureContext (and its implementation). These classes were moved as part of refactorings aimed at improving the simplicity of the project's design.

-
  • The JaasAuthenticationCallbackHandler interface has had it's setAuthentication method removed. The handle method now takes both the Callback and Authentication objects as arguments.

  • +
  • If you wish to use the new ConcurrentSessionController you must declare the HttpSessionEventPublisher context listener in your + web.xml

  • + +
  • The JaasAuthenticationCallbackHandler interface has had it's setAuthentication method removed. + The handle method now takes both the Callback and Authentication objects as arguments.

  • Added AuthenticationException to the AutenticationEntryPoint.commence method signature.

  • diff --git a/samples/contacts/src/main/webapp/filter/WEB-INF/web.xml b/samples/contacts/src/main/webapp/filter/WEB-INF/web.xml index e713b052b8..db9692cbe5 100644 --- a/samples/contacts/src/main/webapp/filter/WEB-INF/web.xml +++ b/samples/contacts/src/main/webapp/filter/WEB-INF/web.xml @@ -58,6 +58,11 @@ org.springframework.web.util.Log4jConfigListener + net.sf.acegisecurity.ui.session.HttpSessionEventPublisher