Committing ConcurrentSessionController feature and tests. Documentation is needed.
This commit is contained in:
parent
edd3fcc72c
commit
44397bb05d
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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. <b>This class will not function properly
|
||||||
|
* without a {@link net.sf.acegisecurity.ui.session.HttpSessionEventPublisher}
|
||||||
|
* configured in web.xml.</b>
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* 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 Ben Alex
|
||||||
* @author Wesley Hall
|
* @author Wesley Hall
|
||||||
|
* @author Ray Krueger
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
|
*
|
||||||
|
* @see ConcurrentSessionController
|
||||||
*/
|
*/
|
||||||
public class ProviderManager extends AbstractAuthenticationManager
|
public class ProviderManager extends AbstractAuthenticationManager
|
||||||
implements InitializingBean {
|
implements InitializingBean {
|
||||||
|
@ -44,6 +49,7 @@ public class ProviderManager extends AbstractAuthenticationManager
|
||||||
|
|
||||||
//~ Instance fields ========================================================
|
//~ Instance fields ========================================================
|
||||||
|
|
||||||
|
private ConcurrentSessionController sessionController = new NullConcurrentSessionController();
|
||||||
private List providers;
|
private List providers;
|
||||||
|
|
||||||
//~ Methods ================================================================
|
//~ Methods ================================================================
|
||||||
|
@ -82,6 +88,29 @@ public class ProviderManager extends AbstractAuthenticationManager
|
||||||
return this.providers;
|
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 {
|
public void afterPropertiesSet() throws Exception {
|
||||||
checkIfValidList(this.providers);
|
checkIfValidList(this.providers);
|
||||||
}
|
}
|
||||||
|
@ -117,6 +146,8 @@ public class ProviderManager extends AbstractAuthenticationManager
|
||||||
|
|
||||||
Class toTest = authentication.getClass();
|
Class toTest = authentication.getClass();
|
||||||
|
|
||||||
|
sessionController.beforeAuthentication(authentication);
|
||||||
|
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
AuthenticationProvider provider = (AuthenticationProvider) iter
|
AuthenticationProvider provider = (AuthenticationProvider) iter
|
||||||
.next();
|
.next();
|
||||||
|
@ -128,6 +159,8 @@ public class ProviderManager extends AbstractAuthenticationManager
|
||||||
Authentication result = provider.authenticate(authentication);
|
Authentication result = provider.authenticate(authentication);
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
|
sessionController.afterAuthentication(authentication, result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 junit.framework.TestCase;
|
||||||
|
|
||||||
import net.sf.acegisecurity.Authentication;
|
import net.sf.acegisecurity.*;
|
||||||
import net.sf.acegisecurity.AuthenticationException;
|
|
||||||
import net.sf.acegisecurity.AuthenticationServiceException;
|
|
||||||
import net.sf.acegisecurity.GrantedAuthority;
|
|
||||||
import net.sf.acegisecurity.GrantedAuthorityImpl;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
@ -110,6 +106,19 @@ public class ProviderManagerTests extends TestCase {
|
||||||
assertEquals("ROLE_TWO", castResult.getAuthorities()[1].getAuthority());
|
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()
|
public void testStartupFailsIfProviderListDoesNotContainingProviders()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
List providers = new Vector();
|
List providers = new Vector();
|
||||||
|
|
|
@ -26,7 +26,11 @@ applications:
|
||||||
SecureContext (and its implementation). These classes were moved as part of refactorings aimed at
|
SecureContext (and its implementation). These classes were moved as part of refactorings aimed at
|
||||||
improving the simplicity of the project's design.<br><br></li>
|
improving the simplicity of the project's design.<br><br></li>
|
||||||
|
|
||||||
<li>The JaasAuthenticationCallbackHandler interface has had it's setAuthentication method removed. The handle method now takes both the Callback and Authentication objects as arguments.<br><br></li>
|
<li>If you wish to use the new ConcurrentSessionController you must declare the HttpSessionEventPublisher context listener in your
|
||||||
|
web.xml<br><br></li>
|
||||||
|
|
||||||
|
<li>The JaasAuthenticationCallbackHandler interface has had it's setAuthentication method removed.
|
||||||
|
The handle method now takes both the Callback and Authentication objects as arguments.<br><br></li>
|
||||||
|
|
||||||
<li>Added AuthenticationException to the AutenticationEntryPoint.commence method signature.<br><br></li>
|
<li>Added AuthenticationException to the AutenticationEntryPoint.commence method signature.<br><br></li>
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,11 @@
|
||||||
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
|
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
|
||||||
</listener>
|
</listener>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
The HttpSessionEventPublisher will publish
|
||||||
|
HttpSessionCreatedEvent and HttpSessionDestroyedEvent
|
||||||
|
to the WebApplicationContext
|
||||||
|
-->
|
||||||
<listener>
|
<listener>
|
||||||
<listener-class>net.sf.acegisecurity.ui.session.HttpSessionEventPublisher</listener-class>
|
<listener-class>net.sf.acegisecurity.ui.session.HttpSessionEventPublisher</listener-class>
|
||||||
</listener>
|
</listener>
|
||||||
|
|
Loading…
Reference in New Issue