SEC-234: Allow pluggable AuthenticationDetailsSource strategy interface.
This commit is contained in:
parent
b1becf9277
commit
a47a342ce6
|
@ -19,7 +19,8 @@ import org.acegisecurity.Authentication;
|
|||
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
|
||||
import org.acegisecurity.ui.WebAuthenticationDetails;
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSource;
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSourceImpl;
|
||||
|
||||
import org.acegisecurity.userdetails.memory.UserAttribute;
|
||||
|
||||
|
@ -61,6 +62,7 @@ public class AnonymousProcessingFilter implements Filter, InitializingBean {
|
|||
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
|
||||
private String key;
|
||||
private UserAttribute userAttribute;
|
||||
private boolean removeAfterRequest = true;
|
||||
|
@ -96,8 +98,8 @@ public class AnonymousProcessingFilter implements Filter, InitializingBean {
|
|||
|
||||
AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key,
|
||||
userAttribute.getPassword(), userAttribute.getAuthorities());
|
||||
auth.setDetails(new WebAuthenticationDetails(
|
||||
(HttpServletRequest) request, false));
|
||||
auth.setDetails(authenticationDetailsSource.buildDetails(
|
||||
(HttpServletRequest) request));
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
@ -167,6 +169,13 @@ public class AnonymousProcessingFilter implements Filter, InitializingBean {
|
|||
return removeAfterRequest;
|
||||
}
|
||||
|
||||
public void setAuthenticationDetailsSource(
|
||||
AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource,
|
||||
"AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
|
|
@ -143,6 +143,7 @@ public abstract class AbstractProcessingFilter implements Filter,
|
|||
//~ Instance fields ========================================================
|
||||
|
||||
protected ApplicationEventPublisher eventPublisher;
|
||||
protected AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
|
||||
private AuthenticationManager authenticationManager;
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
|
||||
|
@ -371,6 +372,13 @@ public abstract class AbstractProcessingFilter implements Filter,
|
|||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
public void setAuthenticationDetailsSource(
|
||||
AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource,
|
||||
"AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
|
||||
public void setAuthenticationFailureUrl(String authenticationFailureUrl) {
|
||||
this.authenticationFailureUrl = authenticationFailureUrl;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/* Copyright 2004, 2005, 2006 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 org.acegisecurity.ui;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
|
||||
/**
|
||||
* Provides a {@link org.acegisecurity.Authentication#getDetails()} object for
|
||||
* a given web request.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface AuthenticationDetailsSource {
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Called by a class when it wishes a new authentication details instance
|
||||
* to be created.
|
||||
*
|
||||
* @param request the request object, which may be used by the
|
||||
* authentication details object
|
||||
*
|
||||
* @return a fully-configured authentication details instance
|
||||
*/
|
||||
public Object buildDetails(HttpServletRequest request);
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/* Copyright 2004, 2005, 2006 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 org.acegisecurity.ui;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
|
||||
/**
|
||||
* Base implementation of {@link AuthenticationDetailsSource}.
|
||||
*
|
||||
* <P>
|
||||
* By default will create an instance of <code>WebAuthenticationDetails</code>.
|
||||
* Any object that accepts a <code>HttpServletRequest</code> as its sole
|
||||
* constructor can be used instead of this default.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AuthenticationDetailsSourceImpl
|
||||
implements AuthenticationDetailsSource {
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private Class clazz = WebAuthenticationDetails.class;
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public Object buildDetails(HttpServletRequest request) {
|
||||
try {
|
||||
Constructor constructor = clazz.getConstructor(new Class[] {HttpServletRequest.class});
|
||||
|
||||
return constructor.newInstance(new Object[] {request});
|
||||
} catch (NoSuchMethodException ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
} catch (InvocationTargetException ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
} catch (InstantiationException ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
} catch (IllegalAccessException ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setClazz(Class clazz) {
|
||||
Assert.notNull(clazz, "Class required");
|
||||
this.clazz = clazz;
|
||||
}
|
||||
}
|
|
@ -52,15 +52,8 @@ public class WebAuthenticationDetails implements SessionIdentifierAware,
|
|||
*/
|
||||
public WebAuthenticationDetails(HttpServletRequest request) {
|
||||
this.remoteAddress = request.getRemoteAddr();
|
||||
this.sessionId = request.getSession(true).getId();
|
||||
doPopulateAdditionalInformation(request);
|
||||
}
|
||||
|
||||
public WebAuthenticationDetails(HttpServletRequest request,
|
||||
boolean forceSessionCreation) {
|
||||
this.remoteAddress = request.getRemoteAddr();
|
||||
|
||||
HttpSession session = request.getSession(forceSessionCreation);
|
||||
HttpSession session = request.getSession(false);
|
||||
this.sessionId = (session != null) ? session.getId() : null;
|
||||
|
||||
doPopulateAdditionalInformation(request);
|
||||
|
|
|
@ -23,8 +23,9 @@ import org.acegisecurity.context.SecurityContextHolder;
|
|||
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSource;
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSourceImpl;
|
||||
import org.acegisecurity.ui.AuthenticationEntryPoint;
|
||||
import org.acegisecurity.ui.WebAuthenticationDetails;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.logging.Log;
|
||||
|
@ -115,9 +116,15 @@ public class BasicProcessingFilter implements Filter, InitializingBean {
|
|||
private AuthenticationEntryPoint authenticationEntryPoint;
|
||||
private AuthenticationManager authenticationManager;
|
||||
private boolean ignoreFailure = false;
|
||||
private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(this.authenticationManager,
|
||||
"An AuthenticationManager is required");
|
||||
|
@ -168,8 +175,7 @@ public class BasicProcessingFilter implements Filter, InitializingBean {
|
|||
|| !existingAuth.isAuthenticated()) {
|
||||
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,
|
||||
password);
|
||||
authRequest.setDetails(new WebAuthenticationDetails(
|
||||
httpRequest, false));
|
||||
authRequest.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
|
||||
|
||||
Authentication authResult;
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ import org.acegisecurity.Authentication;
|
|||
import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.acegisecurity.ui.AbstractProcessingFilter;
|
||||
import org.acegisecurity.ui.WebAuthenticationDetails;
|
||||
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -105,7 +104,7 @@ public class CasProcessingFilter extends AbstractProcessingFilter {
|
|||
UsernamePasswordAuthenticationToken authRequest =
|
||||
new UsernamePasswordAuthenticationToken(username, password);
|
||||
|
||||
authRequest.setDetails(new WebAuthenticationDetails(request, false));
|
||||
authRequest.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
|
||||
|
||||
return this.getAuthenticationManager().authenticate(authRequest);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright 2004, 2005 Acegi Technology Pty Limited
|
||||
/* Copyright 2004, 2005, 2006 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.
|
||||
|
@ -15,7 +15,42 @@
|
|||
|
||||
package org.acegisecurity.ui.digestauth;
|
||||
|
||||
import org.acegisecurity.AcegiMessageSource;
|
||||
import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.AuthenticationServiceException;
|
||||
import org.acegisecurity.BadCredentialsException;
|
||||
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.acegisecurity.providers.dao.UserCache;
|
||||
import org.acegisecurity.providers.dao.cache.NullUserCache;
|
||||
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSource;
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSourceImpl;
|
||||
|
||||
import org.acegisecurity.userdetails.UserDetails;
|
||||
import org.acegisecurity.userdetails.UserDetailsService;
|
||||
import org.acegisecurity.userdetails.UsernameNotFoundException;
|
||||
|
||||
import org.acegisecurity.util.StringSplitUtils;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
|
@ -27,30 +62,6 @@ import javax.servlet.ServletResponse;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.acegisecurity.AcegiMessageSource;
|
||||
import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.AuthenticationServiceException;
|
||||
import org.acegisecurity.BadCredentialsException;
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.acegisecurity.providers.dao.UserCache;
|
||||
import org.acegisecurity.providers.dao.cache.NullUserCache;
|
||||
import org.acegisecurity.ui.WebAuthenticationDetails;
|
||||
import org.acegisecurity.userdetails.UserDetails;
|
||||
import org.acegisecurity.userdetails.UserDetailsService;
|
||||
import org.acegisecurity.userdetails.UsernameNotFoundException;
|
||||
import org.acegisecurity.util.StringSplitUtils;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Processes a HTTP request's Digest authorization headers, putting the result
|
||||
|
@ -84,9 +95,9 @@ import org.springframework.util.StringUtils;
|
|||
*
|
||||
* <p>
|
||||
* If authentication fails, an {@link
|
||||
* org.acegisecurity.ui.AuthenticationEntryPoint
|
||||
* AuthenticationEntryPoint} implementation is called. This must always be
|
||||
* {@link DigestProcessingFilterEntryPoint}, which will prompt the user to
|
||||
* org.acegisecurity.ui.AuthenticationEntryPoint AuthenticationEntryPoint}
|
||||
* implementation is called. This must always be {@link
|
||||
* DigestProcessingFilterEntryPoint}, which will prompt the user to
|
||||
* authenticate again via Digest authentication.
|
||||
* </p>
|
||||
*
|
||||
|
@ -112,10 +123,11 @@ public class DigestProcessingFilter implements Filter, InitializingBean,
|
|||
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private UserDetailsService userDetailsService;
|
||||
private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
|
||||
private DigestProcessingFilterEntryPoint authenticationEntryPoint;
|
||||
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
|
||||
private UserCache userCache = new NullUserCache();
|
||||
private UserDetailsService userDetailsService;
|
||||
private boolean passwordAlreadyEncoded = false;
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
@ -369,10 +381,11 @@ public class DigestProcessingFilter implements Filter, InitializingBean,
|
|||
+ "' with response: '" + responseDigest + "'");
|
||||
}
|
||||
|
||||
UsernamePasswordAuthenticationToken authRequest =
|
||||
new UsernamePasswordAuthenticationToken(user, user.getPassword());
|
||||
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(user,
|
||||
user.getPassword());
|
||||
|
||||
authRequest.setDetails(new WebAuthenticationDetails(httpRequest, false));
|
||||
authRequest.setDetails(authenticationDetailsSource.buildDetails(
|
||||
(HttpServletRequest) request));
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authRequest);
|
||||
}
|
||||
|
@ -405,8 +418,8 @@ public class DigestProcessingFilter implements Filter, InitializingBean,
|
|||
* <code>response</code> independently. Provided as a static method to
|
||||
* simplify the coding of user agents.
|
||||
*
|
||||
* @param passwordAlreadyEncoded true if the password argument is already encoded in
|
||||
* the correct format. False if it is plain text.
|
||||
* @param passwordAlreadyEncoded true if the password argument is already
|
||||
* encoded in the correct format. False if it is plain text.
|
||||
* @param username the user's login name.
|
||||
* @param realm the name of the realm.
|
||||
* @param password the user's password in plaintext or ready-encoded.
|
||||
|
@ -419,7 +432,8 @@ public class DigestProcessingFilter implements Filter, InitializingBean,
|
|||
*
|
||||
* @return the MD5 of the digest authentication response, encoded in hex
|
||||
*
|
||||
* @throws IllegalArgumentException if the supplied qop value is unsupported.
|
||||
* @throws IllegalArgumentException if the supplied qop value is
|
||||
* unsupported.
|
||||
*/
|
||||
public static String generateDigest(boolean passwordAlreadyEncoded,
|
||||
String username, String realm, String password, String httpMethod,
|
||||
|
@ -454,10 +468,6 @@ public class DigestProcessingFilter implements Filter, InitializingBean,
|
|||
return digestMd5;
|
||||
}
|
||||
|
||||
public UserDetailsService getUserDetailsService() {
|
||||
return userDetailsService;
|
||||
}
|
||||
|
||||
public DigestProcessingFilterEntryPoint getAuthenticationEntryPoint() {
|
||||
return authenticationEntryPoint;
|
||||
}
|
||||
|
@ -466,10 +476,17 @@ public class DigestProcessingFilter implements Filter, InitializingBean,
|
|||
return userCache;
|
||||
}
|
||||
|
||||
public UserDetailsService getUserDetailsService() {
|
||||
return userDetailsService;
|
||||
}
|
||||
|
||||
public void init(FilterConfig ignored) throws ServletException {}
|
||||
|
||||
public void setUserDetailsService(UserDetailsService authenticationDao) {
|
||||
this.userDetailsService = authenticationDao;
|
||||
public void setAuthenticationDetailsSource(
|
||||
AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource,
|
||||
"AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
|
||||
public void setAuthenticationEntryPoint(
|
||||
|
@ -488,4 +505,8 @@ public class DigestProcessingFilter implements Filter, InitializingBean,
|
|||
public void setUserCache(UserCache userCache) {
|
||||
this.userCache = userCache;
|
||||
}
|
||||
|
||||
public void setUserDetailsService(UserDetailsService authenticationDao) {
|
||||
this.userDetailsService = authenticationDao;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ import org.acegisecurity.Authentication;
|
|||
|
||||
import org.acegisecurity.providers.rememberme.RememberMeAuthenticationToken;
|
||||
|
||||
import org.acegisecurity.ui.WebAuthenticationDetails;
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSource;
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSourceImpl;
|
||||
|
||||
import org.acegisecurity.userdetails.UserDetails;
|
||||
import org.acegisecurity.userdetails.UserDetailsService;
|
||||
|
@ -116,6 +117,7 @@ public class TokenBasedRememberMeServices implements RememberMeServices,
|
|||
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
|
||||
private String key;
|
||||
private String parameter = DEFAULT_PARAMETER;
|
||||
private UserDetailsService userDetailsService;
|
||||
|
@ -232,8 +234,8 @@ public class TokenBasedRememberMeServices implements RememberMeServices,
|
|||
|
||||
RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.key,
|
||||
userDetails, userDetails.getAuthorities());
|
||||
auth.setDetails(new WebAuthenticationDetails(request,
|
||||
false));
|
||||
auth.setDetails(authenticationDetailsSource.buildDetails(
|
||||
(HttpServletRequest) request));
|
||||
|
||||
return auth;
|
||||
} else {
|
||||
|
@ -347,15 +349,23 @@ public class TokenBasedRememberMeServices implements RememberMeServices,
|
|||
return cookie;
|
||||
}
|
||||
|
||||
protected Cookie makeValidCookie(long expiryTime, String tokenValueBase64, HttpServletRequest request) {
|
||||
protected Cookie makeValidCookie(long expiryTime, String tokenValueBase64,
|
||||
HttpServletRequest request) {
|
||||
Cookie cookie = new Cookie(ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,
|
||||
tokenValueBase64);
|
||||
cookie.setMaxAge(60 * 60 * 24 * 365 * 5); // 5 years
|
||||
cookie.setPath(request.getContextPath());
|
||||
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
public void setAuthenticationDetailsSource(
|
||||
AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource,
|
||||
"AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright 2004, 2005 Acegi Technology Pty Limited
|
||||
/* Copyright 2004, 2005, 2006 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.
|
||||
|
@ -15,7 +15,45 @@
|
|||
|
||||
package org.acegisecurity.ui.switchuser;
|
||||
|
||||
import org.acegisecurity.AccountExpiredException;
|
||||
import org.acegisecurity.AcegiMessageSource;
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.AuthenticationCredentialsNotFoundException;
|
||||
import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.CredentialsExpiredException;
|
||||
import org.acegisecurity.DisabledException;
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.acegisecurity.LockedException;
|
||||
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
|
||||
import org.acegisecurity.event.authentication.AuthenticationSwitchUserEvent;
|
||||
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSource;
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSourceImpl;
|
||||
|
||||
import org.acegisecurity.userdetails.UserDetails;
|
||||
import org.acegisecurity.userdetails.UserDetailsService;
|
||||
import org.acegisecurity.userdetails.UsernameNotFoundException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
@ -29,33 +67,6 @@ import javax.servlet.ServletResponse;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.acegisecurity.AccountExpiredException;
|
||||
import org.acegisecurity.AcegiMessageSource;
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.AuthenticationCredentialsNotFoundException;
|
||||
import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.CredentialsExpiredException;
|
||||
import org.acegisecurity.DisabledException;
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.acegisecurity.LockedException;
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
import org.acegisecurity.event.authentication.AuthenticationSwitchUserEvent;
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.acegisecurity.ui.WebAuthenticationDetails;
|
||||
import org.acegisecurity.userdetails.UserDetails;
|
||||
import org.acegisecurity.userdetails.UserDetailsService;
|
||||
import org.acegisecurity.userdetails.UsernameNotFoundException;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Switch User processing filter responsible for user context switching.
|
||||
|
@ -104,7 +115,6 @@ import org.springframework.util.Assert;
|
|||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Mark St.Godard
|
||||
* @version $Id$
|
||||
*
|
||||
|
@ -124,15 +134,16 @@ public class SwitchUserProcessingFilter implements Filter, InitializingBean,
|
|||
//~ Instance fields ========================================================
|
||||
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
// ~ Instance fields
|
||||
// ========================================================
|
||||
private UserDetailsService userDetailsService;
|
||||
private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
|
||||
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
|
||||
private String exitUserUrl = "/j_acegi_exit_user";
|
||||
private String switchUserUrl = "/j_acegi_switch_user";
|
||||
private String targetUrl;
|
||||
|
||||
// ~ Instance fields
|
||||
// ========================================================
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
@ -160,369 +171,344 @@ public class SwitchUserProcessingFilter implements Filter, InitializingBean,
|
|||
Authentication current = SecurityContextHolder.getContext()
|
||||
.getAuthentication();
|
||||
|
||||
if (null == current) {
|
||||
throw new AuthenticationCredentialsNotFoundException(messages
|
||||
.getMessage("SwitchUserProcessingFilter.noCurrentUser",
|
||||
"No current user associated with this request"));
|
||||
}
|
||||
|
||||
// check to see if the current user did actual switch to another user
|
||||
// if so, get the original source user so we can switch back
|
||||
Authentication original = getSourceAuthentication(current);
|
||||
|
||||
if (original == null) {
|
||||
logger.error(
|
||||
"Could not find original user Authentication object!");
|
||||
throw new AuthenticationCredentialsNotFoundException(messages
|
||||
.getMessage(
|
||||
"SwitchUserProcessingFilter.noOriginalAuthentication",
|
||||
"Could not find original Authentication object"));
|
||||
}
|
||||
|
||||
// get the source user details
|
||||
UserDetails originalUser = null;
|
||||
Object obj = original.getPrincipal();
|
||||
|
||||
if ((obj != null) && obj instanceof UserDetails) {
|
||||
originalUser = (UserDetails) obj;
|
||||
}
|
||||
|
||||
// publish event
|
||||
if (this.eventPublisher != null) {
|
||||
eventPublisher.publishEvent(new AuthenticationSwitchUserEvent(
|
||||
current, originalUser));
|
||||
}
|
||||
|
||||
return original;
|
||||
if (null == current) {
|
||||
throw new AuthenticationCredentialsNotFoundException(messages
|
||||
.getMessage("SwitchUserProcessingFilter.noCurrentUser",
|
||||
"No current user associated with this request"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to switch to another user. If the user does not exist or
|
||||
* is not active, return null.
|
||||
*
|
||||
* @param request The http request
|
||||
*
|
||||
* @return The new <code>Authentication</code> request if
|
||||
* successfully switched to another user,
|
||||
* <code>null</code> otherwise.
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
* @throws UsernameNotFoundException If the target user is not
|
||||
* found.
|
||||
* @throws LockedException DOCUMENT ME!
|
||||
* @throws DisabledException If the target user is disabled.
|
||||
* @throws AccountExpiredException If the target user account is
|
||||
* expired.
|
||||
* @throws CredentialsExpiredException If the target user
|
||||
* credentials are expired.
|
||||
*/
|
||||
protected Authentication attemptSwitchUser(
|
||||
HttpServletRequest request) throws AuthenticationException {
|
||||
UsernamePasswordAuthenticationToken targetUserRequest = null;
|
||||
// check to see if the current user did actual switch to another user
|
||||
// if so, get the original source user so we can switch back
|
||||
Authentication original = getSourceAuthentication(current);
|
||||
|
||||
String username = request.getParameter(ACEGI_SECURITY_SWITCH_USERNAME_KEY);
|
||||
if (original == null) {
|
||||
logger.error("Could not find original user Authentication object!");
|
||||
throw new AuthenticationCredentialsNotFoundException(messages
|
||||
.getMessage("SwitchUserProcessingFilter.noOriginalAuthentication",
|
||||
"Could not find original Authentication object"));
|
||||
}
|
||||
|
||||
if (username == null) {
|
||||
username = "";
|
||||
// get the source user details
|
||||
UserDetails originalUser = null;
|
||||
Object obj = original.getPrincipal();
|
||||
|
||||
if ((obj != null) && obj instanceof UserDetails) {
|
||||
originalUser = (UserDetails) obj;
|
||||
}
|
||||
|
||||
// publish event
|
||||
if (this.eventPublisher != null) {
|
||||
eventPublisher.publishEvent(new AuthenticationSwitchUserEvent(
|
||||
current, originalUser));
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to switch to another user. If the user does not exist or is not
|
||||
* active, return null.
|
||||
*
|
||||
* @param request The http request
|
||||
*
|
||||
* @return The new <code>Authentication</code> request if successfully
|
||||
* switched to another user, <code>null</code> otherwise.
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
* @throws UsernameNotFoundException If the target user is not found.
|
||||
* @throws LockedException DOCUMENT ME!
|
||||
* @throws DisabledException If the target user is disabled.
|
||||
* @throws AccountExpiredException If the target user account is expired.
|
||||
* @throws CredentialsExpiredException If the target user credentials are
|
||||
* expired.
|
||||
*/
|
||||
protected Authentication attemptSwitchUser(HttpServletRequest request)
|
||||
throws AuthenticationException {
|
||||
UsernamePasswordAuthenticationToken targetUserRequest = null;
|
||||
|
||||
String username = request.getParameter(ACEGI_SECURITY_SWITCH_USERNAME_KEY);
|
||||
|
||||
if (username == null) {
|
||||
username = "";
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Attempt to switch to user [" + username + "]");
|
||||
}
|
||||
|
||||
// load the user by name
|
||||
UserDetails targetUser = this.userDetailsService.loadUserByUsername(username);
|
||||
|
||||
// user not found
|
||||
if (targetUser == null) {
|
||||
throw new UsernameNotFoundException(messages.getMessage(
|
||||
"SwitchUserProcessingFilter.usernameNotFound",
|
||||
new Object[] {username}, "Username {0} not found"));
|
||||
}
|
||||
|
||||
// account is expired
|
||||
if (!targetUser.isAccountNonLocked()) {
|
||||
throw new LockedException(messages.getMessage(
|
||||
"SwitchUserProcessingFilter.locked",
|
||||
"User account is locked"));
|
||||
}
|
||||
|
||||
// user is disabled
|
||||
if (!targetUser.isEnabled()) {
|
||||
throw new DisabledException(messages.getMessage(
|
||||
"SwitchUserProcessingFilter.disabled", "User is disabled"));
|
||||
}
|
||||
|
||||
// account is expired
|
||||
if (!targetUser.isAccountNonExpired()) {
|
||||
throw new AccountExpiredException(messages.getMessage(
|
||||
"SwitchUserProcessingFilter.expired",
|
||||
"User account has expired"));
|
||||
}
|
||||
|
||||
// credentials expired
|
||||
if (!targetUser.isCredentialsNonExpired()) {
|
||||
throw new CredentialsExpiredException(messages.getMessage(
|
||||
"SwitchUserProcessingFilter.credentialsExpired",
|
||||
"User credentials have expired"));
|
||||
}
|
||||
|
||||
// ok, create the switch user token
|
||||
targetUserRequest = createSwitchUserToken(request, username, targetUser);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Switch User Token [" + targetUserRequest + "]");
|
||||
}
|
||||
|
||||
// publish event
|
||||
if (this.eventPublisher != null) {
|
||||
eventPublisher.publishEvent(new AuthenticationSwitchUserEvent(
|
||||
SecurityContextHolder.getContext().getAuthentication(),
|
||||
targetUser));
|
||||
}
|
||||
|
||||
return targetUserRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a switch user token that contains an additional
|
||||
* <tt>GrantedAuthority</tt> that contains the original
|
||||
* <code>Authentication</code> object.
|
||||
*
|
||||
* @param request The http servlet request.
|
||||
* @param username The username of target user
|
||||
* @param targetUser The target user
|
||||
*
|
||||
* @return The authentication token
|
||||
*
|
||||
* @see SwitchUserGrantedAuthority
|
||||
*/
|
||||
private UsernamePasswordAuthenticationToken createSwitchUserToken(
|
||||
HttpServletRequest request, String username, UserDetails targetUser) {
|
||||
UsernamePasswordAuthenticationToken targetUserRequest;
|
||||
|
||||
// grant an additional authority that contains the original Authentication object
|
||||
// which will be used to 'exit' from the current switched user.
|
||||
Authentication currentAuth = SecurityContextHolder.getContext()
|
||||
.getAuthentication();
|
||||
GrantedAuthority switchAuthority = new SwitchUserGrantedAuthority(ROLE_PREVIOUS_ADMINISTRATOR,
|
||||
currentAuth);
|
||||
|
||||
// get the original authorities
|
||||
List orig = Arrays.asList(targetUser.getAuthorities());
|
||||
|
||||
// add the new switch user authority
|
||||
List newAuths = new ArrayList(orig);
|
||||
newAuths.add(switchAuthority);
|
||||
|
||||
GrantedAuthority[] authorities = {};
|
||||
authorities = (GrantedAuthority[]) newAuths.toArray(authorities);
|
||||
|
||||
// create the new authentication token
|
||||
targetUserRequest = new UsernamePasswordAuthenticationToken(targetUser,
|
||||
targetUser.getPassword(), authorities);
|
||||
|
||||
// set details
|
||||
targetUserRequest.setDetails(authenticationDetailsSource.buildDetails(
|
||||
(HttpServletRequest) request));
|
||||
|
||||
return targetUserRequest;
|
||||
}
|
||||
|
||||
public void destroy() {}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.Filter#doFilter
|
||||
*/
|
||||
public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
Assert.isInstanceOf(HttpServletRequest.class, request);
|
||||
Assert.isInstanceOf(HttpServletResponse.class, response);
|
||||
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
|
||||
// check for switch or exit request
|
||||
if (requiresSwitchUser(httpRequest)) {
|
||||
// if set, attempt switch and store original
|
||||
Authentication targetUser = attemptSwitchUser(httpRequest);
|
||||
|
||||
// update the current context to the new target user
|
||||
SecurityContextHolder.getContext().setAuthentication(targetUser);
|
||||
|
||||
// redirect to target url
|
||||
httpResponse.sendRedirect(httpResponse.encodeRedirectURL(httpRequest
|
||||
.getContextPath() + targetUrl));
|
||||
|
||||
return;
|
||||
} else if (requiresExitUser(httpRequest)) {
|
||||
// get the original authentication object (if exists)
|
||||
Authentication originalUser = attemptExitUser(httpRequest);
|
||||
|
||||
// update the current context back to the original user
|
||||
SecurityContextHolder.getContext().setAuthentication(originalUser);
|
||||
|
||||
// redirect to target url
|
||||
httpResponse.sendRedirect(httpResponse.encodeRedirectURL(httpRequest
|
||||
.getContextPath() + targetUrl));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the original <code>Authentication</code> object from the current
|
||||
* user's granted authorities. A successfully switched user should have a
|
||||
* <code>SwitchUserGrantedAuthority</code> that contains the original
|
||||
* source user <code>Authentication</code> object.
|
||||
*
|
||||
* @param current The current <code>Authentication</code> object
|
||||
*
|
||||
* @return The source user <code>Authentication</code> object or
|
||||
* <code>null</code> otherwise.
|
||||
*/
|
||||
private Authentication getSourceAuthentication(Authentication current) {
|
||||
Authentication original = null;
|
||||
|
||||
// iterate over granted authorities and find the 'switch user' authority
|
||||
GrantedAuthority[] authorities = current.getAuthorities();
|
||||
|
||||
for (int i = 0; i < authorities.length; i++) {
|
||||
// check for switch user type of authority
|
||||
if (authorities[i] instanceof SwitchUserGrantedAuthority) {
|
||||
original = ((SwitchUserGrantedAuthority) authorities[i])
|
||||
.getSource();
|
||||
logger.debug("Found original switch user granted authority ["
|
||||
+ original + "]");
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Attempt to switch to user [" + username + "]");
|
||||
}
|
||||
|
||||
// load the user by name
|
||||
UserDetails targetUser = this.userDetailsService
|
||||
.loadUserByUsername(username);
|
||||
|
||||
// user not found
|
||||
if (targetUser == null) {
|
||||
throw new UsernameNotFoundException(messages.getMessage(
|
||||
"SwitchUserProcessingFilter.usernameNotFound",
|
||||
new Object[] {username},
|
||||
"Username {0} not found"));
|
||||
}
|
||||
|
||||
// account is expired
|
||||
if (!targetUser.isAccountNonLocked()) {
|
||||
throw new LockedException(messages.getMessage(
|
||||
"SwitchUserProcessingFilter.locked",
|
||||
"User account is locked"));
|
||||
}
|
||||
|
||||
// user is disabled
|
||||
if (!targetUser.isEnabled()) {
|
||||
throw new DisabledException(messages.getMessage(
|
||||
"SwitchUserProcessingFilter.disabled",
|
||||
"User is disabled"));
|
||||
}
|
||||
|
||||
// account is expired
|
||||
if (!targetUser.isAccountNonExpired()) {
|
||||
throw new AccountExpiredException(messages.getMessage(
|
||||
"SwitchUserProcessingFilter.expired",
|
||||
"User account has expired"));
|
||||
}
|
||||
|
||||
// credentials expired
|
||||
if (!targetUser.isCredentialsNonExpired()) {
|
||||
throw new CredentialsExpiredException(messages
|
||||
.getMessage(
|
||||
"SwitchUserProcessingFilter.credentialsExpired",
|
||||
"User credentials have expired"));
|
||||
}
|
||||
|
||||
// ok, create the switch user token
|
||||
targetUserRequest = createSwitchUserToken(request,
|
||||
username, targetUser);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Switch User Token [" + targetUserRequest + "]");
|
||||
}
|
||||
|
||||
// publish event
|
||||
if (this.eventPublisher != null) {
|
||||
eventPublisher.publishEvent(new AuthenticationSwitchUserEvent(
|
||||
SecurityContextHolder.getContext().getAuthentication(),
|
||||
targetUser));
|
||||
}
|
||||
|
||||
return targetUserRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a switch user token that contains an additional
|
||||
* <tt>GrantedAuthority</tt> that contains the original
|
||||
* <code>Authentication</code> object.
|
||||
*
|
||||
* @param request The http servlet request.
|
||||
* @param username The username of target user
|
||||
* @param targetUser The target user
|
||||
*
|
||||
* @return The authentication token
|
||||
*
|
||||
* @see SwitchUserGrantedAuthority
|
||||
*/
|
||||
private UsernamePasswordAuthenticationToken createSwitchUserToken(
|
||||
HttpServletRequest request, String username,
|
||||
UserDetails targetUser) {
|
||||
UsernamePasswordAuthenticationToken targetUserRequest;
|
||||
return original;
|
||||
}
|
||||
|
||||
// grant an additional authority that contains the original Authentication object
|
||||
// which will be used to 'exit' from the current switched user.
|
||||
Authentication currentAuth = SecurityContextHolder.getContext()
|
||||
.getAuthentication();
|
||||
GrantedAuthority switchAuthority = new SwitchUserGrantedAuthority(ROLE_PREVIOUS_ADMINISTRATOR,
|
||||
currentAuth);
|
||||
public void init(FilterConfig ignored) throws ServletException {}
|
||||
|
||||
// get the original authorities
|
||||
List orig = Arrays.asList(targetUser.getAuthorities());
|
||||
/**
|
||||
* Checks the request URI for the presence of <tt>exitUserUrl</tt>.
|
||||
*
|
||||
* @param request The http servlet request
|
||||
*
|
||||
* @return <code>true</code> if the request requires a exit user,
|
||||
* <code>false</code> otherwise.
|
||||
*
|
||||
* @see SwitchUserProcessingFilter#exitUserUrl
|
||||
*/
|
||||
protected boolean requiresExitUser(HttpServletRequest request) {
|
||||
String uri = stripUri(request);
|
||||
|
||||
// add the new switch user authority
|
||||
List newAuths = new ArrayList(orig);
|
||||
newAuths.add(switchAuthority);
|
||||
return uri.endsWith(request.getContextPath() + exitUserUrl);
|
||||
}
|
||||
|
||||
GrantedAuthority[] authorities = {};
|
||||
authorities = (GrantedAuthority[]) newAuths.toArray(authorities);
|
||||
/**
|
||||
* Checks the request URI for the presence of <tt>switchUserUrl</tt>.
|
||||
*
|
||||
* @param request The http servlet request
|
||||
*
|
||||
* @return <code>true</code> if the request requires a switch,
|
||||
* <code>false</code> otherwise.
|
||||
*
|
||||
* @see SwitchUserProcessingFilter#switchUserUrl
|
||||
*/
|
||||
protected boolean requiresSwitchUser(HttpServletRequest request) {
|
||||
String uri = stripUri(request);
|
||||
|
||||
// create the new authentication token
|
||||
targetUserRequest = new UsernamePasswordAuthenticationToken(targetUser,
|
||||
targetUser.getPassword(), authorities);
|
||||
return uri.endsWith(request.getContextPath() + switchUserUrl);
|
||||
}
|
||||
|
||||
// set details
|
||||
targetUserRequest.setDetails(new WebAuthenticationDetails(
|
||||
request, false));
|
||||
public void setApplicationEventPublisher(
|
||||
ApplicationEventPublisher eventPublisher) throws BeansException {
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
return targetUserRequest;
|
||||
public void setAuthenticationDetailsSource(
|
||||
AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource,
|
||||
"AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL to respond to exit user processing.
|
||||
*
|
||||
* @param exitUserUrl The exit user URL.
|
||||
*/
|
||||
public void setExitUserUrl(String exitUserUrl) {
|
||||
this.exitUserUrl = exitUserUrl;
|
||||
}
|
||||
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL to respond to switch user processing.
|
||||
*
|
||||
* @param switchUserUrl The switch user URL.
|
||||
*/
|
||||
public void setSwitchUserUrl(String switchUserUrl) {
|
||||
this.switchUserUrl = switchUserUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URL to go to after a successful switch / exit user request.
|
||||
*
|
||||
* @param targetUrl The target url.
|
||||
*/
|
||||
public void setTargetUrl(String targetUrl) {
|
||||
this.targetUrl = targetUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication data access object.
|
||||
*
|
||||
* @param authenticationDao The authentication dao
|
||||
*/
|
||||
public void setUserDetailsService(UserDetailsService authenticationDao) {
|
||||
this.userDetailsService = authenticationDao;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips any content after the ';' in the request URI
|
||||
*
|
||||
* @param request The http request
|
||||
*
|
||||
* @return The stripped uri
|
||||
*/
|
||||
private static String stripUri(HttpServletRequest request) {
|
||||
String uri = request.getRequestURI();
|
||||
int idx = uri.indexOf(';');
|
||||
|
||||
if (idx > 0) {
|
||||
uri = uri.substring(0, idx);
|
||||
}
|
||||
|
||||
public void destroy() {}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.Filter#doFilter
|
||||
*/
|
||||
public void doFilter(ServletRequest request,
|
||||
ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
Assert.isInstanceOf(HttpServletRequest.class, request);
|
||||
Assert.isInstanceOf(HttpServletResponse.class, response);
|
||||
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
|
||||
// check for switch or exit request
|
||||
if (requiresSwitchUser(httpRequest)) {
|
||||
// if set, attempt switch and store original
|
||||
Authentication targetUser = attemptSwitchUser(httpRequest);
|
||||
|
||||
// update the current context to the new target user
|
||||
SecurityContextHolder.getContext()
|
||||
.setAuthentication(targetUser);
|
||||
|
||||
// redirect to target url
|
||||
httpResponse.sendRedirect(httpResponse
|
||||
.encodeRedirectURL(httpRequest
|
||||
.getContextPath() + targetUrl));
|
||||
|
||||
return;
|
||||
} else if (requiresExitUser(httpRequest)) {
|
||||
// get the original authentication object (if exists)
|
||||
Authentication originalUser = attemptExitUser(httpRequest);
|
||||
|
||||
// update the current context back to the original user
|
||||
SecurityContextHolder.getContext()
|
||||
.setAuthentication(originalUser);
|
||||
|
||||
// redirect to target url
|
||||
httpResponse.sendRedirect(httpResponse.encodeRedirectURL(
|
||||
httpRequest.getContextPath() + targetUrl));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the original <code>Authentication</code> object from
|
||||
* the current user's granted authorities. A successfully switched
|
||||
* user should have a <code>SwitchUserGrantedAuthority</code>
|
||||
* that contains the original source user <code>Authentication</code>
|
||||
* object.
|
||||
*
|
||||
* @param current The current <code>Authentication</code>
|
||||
* object
|
||||
*
|
||||
* @return The source user <code>Authentication</code>
|
||||
* object or <code>null</code> otherwise.
|
||||
*/
|
||||
private Authentication getSourceAuthentication(
|
||||
Authentication current) {
|
||||
Authentication original = null;
|
||||
|
||||
// iterate over granted authorities and find the 'switch user' authority
|
||||
GrantedAuthority[] authorities = current
|
||||
.getAuthorities();
|
||||
|
||||
for (int i = 0; i < authorities.length; i++) {
|
||||
// check for switch user type of authority
|
||||
if (authorities[i] instanceof SwitchUserGrantedAuthority) {
|
||||
original = ((SwitchUserGrantedAuthority) authorities[i])
|
||||
.getSource();
|
||||
logger.debug(
|
||||
"Found original switch user granted authority ["
|
||||
+ original + "]");
|
||||
}
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
public void init(FilterConfig ignored)
|
||||
throws ServletException {}
|
||||
|
||||
/**
|
||||
* Checks the request URI for the presence
|
||||
* of <tt>exitUserUrl</tt>.
|
||||
*
|
||||
* @param request The http servlet request
|
||||
*
|
||||
* @return <code>true</code> if the request requires a exit user,
|
||||
* <code>false</code> otherwise.
|
||||
*
|
||||
* @see SwitchUserProcessingFilter#exitUserUrl
|
||||
*/
|
||||
protected boolean requiresExitUser(
|
||||
HttpServletRequest request) {
|
||||
String uri = stripUri(request);
|
||||
|
||||
return uri.endsWith(request
|
||||
.getContextPath() + exitUserUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the request URI for the presence of <tt>switchUserUrl</tt>.
|
||||
*
|
||||
* @param request The http servlet request
|
||||
*
|
||||
* @return <code>true</code> if the request requires a switch,
|
||||
* <code>false</code> otherwise.
|
||||
*
|
||||
* @see SwitchUserProcessingFilter#switchUserUrl
|
||||
*/
|
||||
protected boolean requiresSwitchUser(
|
||||
HttpServletRequest request) {
|
||||
String uri = stripUri(request);
|
||||
|
||||
return uri.endsWith(request.getContextPath() + switchUserUrl);
|
||||
}
|
||||
|
||||
public void setApplicationEventPublisher(
|
||||
ApplicationEventPublisher eventPublisher)
|
||||
throws BeansException {
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication data access object.
|
||||
*
|
||||
* @param authenticationDao The authentication dao
|
||||
*/
|
||||
public void setUserDetailsService(
|
||||
UserDetailsService authenticationDao) {
|
||||
this.userDetailsService = authenticationDao;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL to respond to exit user processing.
|
||||
*
|
||||
* @param exitUserUrl The exit user URL.
|
||||
*/
|
||||
public void setExitUserUrl(
|
||||
String exitUserUrl) {
|
||||
this.exitUserUrl = exitUserUrl;
|
||||
}
|
||||
|
||||
public void setMessageSource(
|
||||
MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL to respond to switch user processing.
|
||||
*
|
||||
* @param switchUserUrl The switch user URL.
|
||||
*/
|
||||
public void setSwitchUserUrl(String switchUserUrl) {
|
||||
this.switchUserUrl = switchUserUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URL to go to after a successful switch / exit user
|
||||
* request.
|
||||
*
|
||||
* @param targetUrl The target url.
|
||||
*/
|
||||
public void setTargetUrl(
|
||||
String targetUrl) {
|
||||
this.targetUrl = targetUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips any content after the ';' in the request URI
|
||||
*
|
||||
* @param request The http request
|
||||
*
|
||||
* @return The stripped uri
|
||||
*/
|
||||
private static String stripUri(HttpServletRequest request) {
|
||||
String uri = request.getRequestURI();
|
||||
int idx = uri.indexOf(';');
|
||||
|
||||
if (idx > 0) {
|
||||
uri = uri.substring(0,
|
||||
idx);
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright 2004, 2005 Acegi Technology Pty Limited
|
||||
/* Copyright 2004, 2005, 2006 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,9 +17,10 @@ package org.acegisecurity.ui.webapp;
|
|||
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.AuthenticationException;
|
||||
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
|
||||
import org.acegisecurity.ui.AbstractProcessingFilter;
|
||||
import org.acegisecurity.ui.WebAuthenticationDetails;
|
||||
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -55,15 +56,6 @@ public class AuthenticationProcessingFilter extends AbstractProcessingFilter {
|
|||
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* This filter by default responds to <code>/j_acegi_security_check</code>.
|
||||
*
|
||||
* @return the default
|
||||
*/
|
||||
public String getDefaultFilterProcessesUrl() {
|
||||
return "/j_acegi_security_check";
|
||||
}
|
||||
|
||||
public Authentication attemptAuthentication(HttpServletRequest request)
|
||||
throws AuthenticationException {
|
||||
String username = obtainUsername(request);
|
||||
|
@ -84,28 +76,23 @@ public class AuthenticationProcessingFilter extends AbstractProcessingFilter {
|
|||
setDetails(request, authRequest);
|
||||
|
||||
// Place the last username attempted into HttpSession for views
|
||||
request.getSession().setAttribute(ACEGI_SECURITY_LAST_USERNAME_KEY,
|
||||
username);
|
||||
request.getSession()
|
||||
.setAttribute(ACEGI_SECURITY_LAST_USERNAME_KEY, username);
|
||||
|
||||
return this.getAuthenticationManager().authenticate(authRequest);
|
||||
}
|
||||
|
||||
public void init(FilterConfig filterConfig) throws ServletException {}
|
||||
|
||||
/**
|
||||
* Provided so that subclasses may configure what is put into the
|
||||
* authentication request's details property. The default implementation
|
||||
* simply constructs {@link WebAuthenticationDetails}.
|
||||
* This filter by default responds to <code>/j_acegi_security_check</code>.
|
||||
*
|
||||
* @param request that an authentication request is being created for
|
||||
* @param authRequest the authentication request object that should have
|
||||
* its details set
|
||||
* @return the default
|
||||
*/
|
||||
protected void setDetails(HttpServletRequest request,
|
||||
UsernamePasswordAuthenticationToken authRequest) {
|
||||
authRequest.setDetails(new WebAuthenticationDetails(request, false));
|
||||
public String getDefaultFilterProcessesUrl() {
|
||||
return "/j_acegi_security_check";
|
||||
}
|
||||
|
||||
public void init(FilterConfig filterConfig) throws ServletException {}
|
||||
|
||||
/**
|
||||
* Enables subclasses to override the composition of the password, such as
|
||||
* by including additional values and a separator.
|
||||
|
@ -141,4 +128,17 @@ public class AuthenticationProcessingFilter extends AbstractProcessingFilter {
|
|||
protected String obtainUsername(HttpServletRequest request) {
|
||||
return request.getParameter(ACEGI_SECURITY_FORM_USERNAME_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provided so that subclasses may configure what is put into the
|
||||
* authentication request's details property.
|
||||
*
|
||||
* @param request that an authentication request is being created for
|
||||
* @param authRequest the authentication request object that should have
|
||||
* its details set
|
||||
*/
|
||||
protected void setDetails(HttpServletRequest request,
|
||||
UsernamePasswordAuthenticationToken authRequest) {
|
||||
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright 2004, 2005 Acegi Technology Pty Limited
|
||||
/* Copyright 2004, 2005, 2006 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.
|
||||
|
@ -18,19 +18,24 @@ package org.acegisecurity.ui.x509;
|
|||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.AuthenticationManager;
|
||||
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
|
||||
import org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent;
|
||||
|
||||
import org.acegisecurity.providers.x509.X509AuthenticationToken;
|
||||
|
||||
import org.acegisecurity.ui.AbstractProcessingFilter;
|
||||
import org.acegisecurity.ui.WebAuthenticationDetails;
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSource;
|
||||
import org.acegisecurity.ui.AuthenticationDetailsSourceImpl;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -60,10 +65,11 @@ import javax.servlet.http.HttpServletResponse;
|
|||
*
|
||||
* <p>
|
||||
* If authentication is successful, an {@link
|
||||
* org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent} will be
|
||||
* published to the application context. No events will be published if
|
||||
* authentication was unsuccessful, because this would generally be recorded
|
||||
* via an <code>AuthenticationManager</code>-specific application event.
|
||||
* org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent}
|
||||
* will be published to the application context. No events will be published
|
||||
* if authentication was unsuccessful, because this would generally be
|
||||
* recorded via an <code>AuthenticationManager</code>-specific application
|
||||
* event.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
|
@ -76,7 +82,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
* @version $Id$
|
||||
*/
|
||||
public class X509ProcessingFilter implements Filter, InitializingBean,
|
||||
ApplicationEventPublisherAware {
|
||||
ApplicationEventPublisherAware {
|
||||
//~ Static fields/initializers =============================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(X509ProcessingFilter.class);
|
||||
|
@ -84,19 +90,11 @@ public class X509ProcessingFilter implements Filter, InitializingBean,
|
|||
//~ Instance fields ========================================================
|
||||
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher context) {
|
||||
this.eventPublisher = context;
|
||||
}
|
||||
|
||||
public void setAuthenticationManager(
|
||||
AuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(authenticationManager,
|
||||
"An AuthenticationManager must be set");
|
||||
|
@ -154,7 +152,8 @@ public class X509ProcessingFilter implements Filter, InitializingBean,
|
|||
try {
|
||||
X509AuthenticationToken authRequest = new X509AuthenticationToken(clientCertificate);
|
||||
|
||||
authRequest.setDetails(new WebAuthenticationDetails(httpRequest));
|
||||
authRequest.setDetails(authenticationDetailsSource.buildDetails(
|
||||
(HttpServletRequest) request));
|
||||
authResult = authenticationManager.authenticate(authRequest);
|
||||
successfulAuthentication(httpRequest, httpResponse, authResult);
|
||||
} catch (AuthenticationException failed) {
|
||||
|
@ -165,8 +164,39 @@ public class X509ProcessingFilter implements Filter, InitializingBean,
|
|||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private X509Certificate extractClientCertificate(HttpServletRequest request) {
|
||||
X509Certificate[] certs = (X509Certificate[]) request.getAttribute(
|
||||
"javax.servlet.request.X509Certificate");
|
||||
|
||||
if ((certs != null) && (certs.length > 0)) {
|
||||
return certs[0];
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No client certificate found in request.");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void init(FilterConfig ignored) throws ServletException {}
|
||||
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher context) {
|
||||
this.eventPublisher = context;
|
||||
}
|
||||
|
||||
public void setAuthenticationDetailsSource(
|
||||
AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource,
|
||||
"AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
|
||||
public void setAuthenticationManager(
|
||||
AuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the <code>Authentication</code> instance returned by the
|
||||
* authentication manager into the secure context.
|
||||
|
@ -206,25 +236,12 @@ public class X509ProcessingFilter implements Filter, InitializingBean,
|
|||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Updated SecurityContextHolder to contain null Authentication");
|
||||
logger.debug(
|
||||
"Updated SecurityContextHolder to contain null Authentication");
|
||||
}
|
||||
|
||||
request.getSession().setAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY,
|
||||
request.getSession()
|
||||
.setAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY,
|
||||
failed);
|
||||
}
|
||||
|
||||
private X509Certificate extractClientCertificate(HttpServletRequest request) {
|
||||
X509Certificate[] certs = (X509Certificate[]) request.getAttribute(
|
||||
"javax.servlet.request.X509Certificate");
|
||||
|
||||
if ((certs != null) && (certs.length > 0)) {
|
||||
return certs[0];
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No client certificate found in request.");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright 2004, 2005 Acegi Technology Pty Limited
|
||||
/* Copyright 2004, 2005, 2006 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.
|
||||
|
@ -18,8 +18,11 @@ package org.acegisecurity.concurrent;
|
|||
import junit.framework.TestCase;
|
||||
|
||||
import org.acegisecurity.Authentication;
|
||||
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
|
||||
import org.acegisecurity.ui.WebAuthenticationDetails;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
|
||||
|
@ -33,6 +36,23 @@ import org.springframework.mock.web.MockHttpSession;
|
|||
public class ConcurrentSessionControllerImplTests extends TestCase {
|
||||
//~ Methods ================================================================
|
||||
|
||||
private Authentication createAuthentication(String user, String password) {
|
||||
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user,
|
||||
password);
|
||||
auth.setDetails(createWebDetails(auth));
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
private WebAuthenticationDetails createWebDetails(Authentication auth) {
|
||||
MockHttpSession session = new MockHttpSession();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setSession(session);
|
||||
request.setUserPrincipal(auth);
|
||||
|
||||
return new WebAuthenticationDetails(request);
|
||||
}
|
||||
|
||||
public void testLifecycle() throws Exception {
|
||||
// Build a test fixture
|
||||
ConcurrentSessionControllerImpl sc = new ConcurrentSessionControllerImpl();
|
||||
|
@ -103,21 +123,4 @@ public class ConcurrentSessionControllerImplTests extends TestCase {
|
|||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
private Authentication createAuthentication(String user, String password) {
|
||||
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user,
|
||||
password);
|
||||
auth.setDetails(createWebDetails(auth));
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
private WebAuthenticationDetails createWebDetails(Authentication auth) {
|
||||
MockHttpSession session = new MockHttpSession();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setSession(session);
|
||||
request.setUserPrincipal(auth);
|
||||
|
||||
return new WebAuthenticationDetails(request, false);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue