mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-23 12:32:13 +00:00
SEC-319: Improvements to Siteminder integration: Create its own authentication provider & reeval strategy. Note that documentation not yet complete, but code is functional, test-covered and validated in a Siteminder environment.
This commit is contained in:
parent
52a167acfa
commit
8d3a2b42d9
@ -0,0 +1,132 @@
|
|||||||
|
/* 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.providers.siteminder;
|
||||||
|
|
||||||
|
import org.acegisecurity.AccountExpiredException;
|
||||||
|
import org.acegisecurity.AuthenticationException;
|
||||||
|
import org.acegisecurity.AuthenticationServiceException;
|
||||||
|
import org.acegisecurity.CredentialsExpiredException;
|
||||||
|
import org.acegisecurity.DisabledException;
|
||||||
|
import org.acegisecurity.LockedException;
|
||||||
|
import org.acegisecurity.providers.AuthenticationProvider;
|
||||||
|
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider;
|
||||||
|
import org.acegisecurity.userdetails.UserDetails;
|
||||||
|
import org.acegisecurity.userdetails.UserDetailsService;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link AuthenticationProvider} implementation that retrieves user details from an {@link UserDetailsService}.
|
||||||
|
*
|
||||||
|
* @author Scott McCrory
|
||||||
|
* @version $Id: SiteminderAuthenticationProvider.java 1582 2006-07-15 15:18:51Z smccrory $
|
||||||
|
*/
|
||||||
|
public class SiteminderAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Our logging object
|
||||||
|
*/
|
||||||
|
private static final Log logger = LogFactory.getLog(SiteminderAuthenticationProvider.class);
|
||||||
|
|
||||||
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Our user details service (which does the real work of checking the user against a back-end user store).
|
||||||
|
*/
|
||||||
|
private UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider#additionalAuthenticationChecks(org.acegisecurity.userdetails.UserDetails, org.acegisecurity.providers.UsernamePasswordAuthenticationToken)
|
||||||
|
*/
|
||||||
|
protected void additionalAuthenticationChecks(final UserDetails user,
|
||||||
|
final UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
||||||
|
|
||||||
|
// No need for password authentication checks - we only expect one identifying string
|
||||||
|
// from the HTTP Request header (as populated by Siteminder), but we do need to see if
|
||||||
|
// the user's account is OK to let them in.
|
||||||
|
if (!user.isEnabled()) {
|
||||||
|
throw new DisabledException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled",
|
||||||
|
"Account disabled"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user.isAccountNonExpired()) {
|
||||||
|
throw new AccountExpiredException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.expired",
|
||||||
|
"Account expired"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user.isAccountNonLocked()) {
|
||||||
|
throw new LockedException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked",
|
||||||
|
"Account locked"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user.isCredentialsNonExpired()) {
|
||||||
|
throw new CredentialsExpiredException(messages.getMessage(
|
||||||
|
"AbstractUserDetailsAuthenticationProvider.credentialsExpired", "Credentials expired"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider#doAfterPropertiesSet()
|
||||||
|
*/
|
||||||
|
protected void doAfterPropertiesSet() throws Exception {
|
||||||
|
Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the user details service.
|
||||||
|
* @return The user details service.
|
||||||
|
*/
|
||||||
|
public UserDetailsService getUserDetailsService() {
|
||||||
|
return userDetailsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider#retrieveUser(java.lang.String, org.acegisecurity.providers.UsernamePasswordAuthenticationToken)
|
||||||
|
*/
|
||||||
|
protected final UserDetails retrieveUser(final String username,
|
||||||
|
final UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
||||||
|
|
||||||
|
UserDetails loadedUser;
|
||||||
|
|
||||||
|
try {
|
||||||
|
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
|
||||||
|
} catch (DataAccessException repositoryProblem) {
|
||||||
|
throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadedUser == null) {
|
||||||
|
throw new AuthenticationServiceException(
|
||||||
|
"UserDetailsService returned null, which is an interface contract violation");
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadedUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the user details service.
|
||||||
|
* @param userDetailsService The user details service.
|
||||||
|
*/
|
||||||
|
public void setUserDetailsService(final UserDetailsService userDetailsService) {
|
||||||
|
this.userDetailsService = userDetailsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
A Siteminder authentication provider.
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -15,36 +15,40 @@
|
|||||||
|
|
||||||
package org.acegisecurity.ui.webapp;
|
package org.acegisecurity.ui.webapp;
|
||||||
|
|
||||||
import org.acegisecurity.Authentication;
|
|
||||||
import org.acegisecurity.AuthenticationException;
|
|
||||||
|
|
||||||
import org.acegisecurity.context.HttpSessionContextIntegrationFilter;
|
|
||||||
import org.acegisecurity.context.SecurityContext;
|
|
||||||
|
|
||||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.acegisecurity.Authentication;
|
||||||
|
import org.acegisecurity.AuthenticationException;
|
||||||
|
import org.acegisecurity.context.HttpSessionContextIntegrationFilter;
|
||||||
|
import org.acegisecurity.context.SecurityContext;
|
||||||
|
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extends Acegi's AuthenticationProcessingFilter to pick up CA/Netegrity Siteminder headers.<P>Also provides a
|
* Extends Acegi's AuthenticationProcessingFilter to pick up CA/Netegrity Siteminder headers.
|
||||||
* backup form-based authentication and the ability set source key names.</p>
|
*
|
||||||
|
* <P>Also provides a backup form-based authentication and the ability set source key names.</p>
|
||||||
|
*
|
||||||
* <P><B>Siteminder</B> must present two <B>headers</B> to this filter, a username and password. You must set the
|
* <P><B>Siteminder</B> must present two <B>headers</B> to this filter, a username and password. You must set the
|
||||||
* header keys before this filter is used for authentication, otherwise Siteminder checks will be skipped. If the
|
* header keys before this filter is used for authentication, otherwise Siteminder checks will be skipped. If the
|
||||||
* Siteminder check is unsuccessful (i.e. if the headers are not found), then the form parameters will be checked (see
|
* Siteminder check is unsuccessful (i.e. if the headers are not found), then the form parameters will be checked (see
|
||||||
* next paragraph). This allows applications to optionally function even when their Siteminder infrastructure is
|
* next paragraph). This allows applications to optionally function even when their Siteminder infrastructure is
|
||||||
* unavailable, as is often the case during development.</p>
|
* unavailable, as is often the case during development.</p>
|
||||||
|
*
|
||||||
* <P><B>Login forms</B> must present two <B>parameters</B> to this filter: a username and password. If not
|
* <P><B>Login forms</B> must present two <B>parameters</B> to this filter: a username and password. If not
|
||||||
* specified, the parameter names to use are contained in the static fields {@link #ACEGI_SECURITY_FORM_USERNAME_KEY}
|
* specified, the parameter names to use are contained in the static fields {@link #ACEGI_SECURITY_FORM_USERNAME_KEY}
|
||||||
* and {@link #ACEGI_SECURITY_FORM_PASSWORD_KEY}.</p>
|
* and {@link #ACEGI_SECURITY_FORM_PASSWORD_KEY}.</p>
|
||||||
|
*
|
||||||
* <P><B>Do not use this class directly.</B> Instead, configure <code>web.xml</code> to use the {@link
|
* <P><B>Do not use this class directly.</B> Instead, configure <code>web.xml</code> to use the {@link
|
||||||
* org.acegisecurity.util.FilterToBeanProxy}.</p>
|
* org.acegisecurity.util.FilterToBeanProxy}.</p>
|
||||||
|
*
|
||||||
|
* @author Scott McCrory
|
||||||
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class SiteminderAuthenticationProcessingFilter extends AuthenticationProcessingFilter {
|
public class SiteminderAuthenticationProcessingFilter extends AuthenticationProcessingFilter {
|
||||||
|
|
||||||
//~ Static fields/initializers =====================================================================================
|
//~ Static fields/initializers =====================================================================================
|
||||||
|
|
||||||
/** Log instance for debugging */
|
/** Log instance for debugging */
|
||||||
@ -52,21 +56,15 @@ public class SiteminderAuthenticationProcessingFilter extends AuthenticationProc
|
|||||||
|
|
||||||
//~ Instance fields ================================================================================================
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
/** Form password request key. */
|
|
||||||
private String formPasswordParameterKey = null;
|
|
||||||
|
|
||||||
/** Form username request key. */
|
/** Form username request key. */
|
||||||
private String formUsernameParameterKey = null;
|
private String formUsernameParameterKey = null;
|
||||||
|
|
||||||
/** Siteminder password header key. */
|
|
||||||
private String siteminderPasswordHeaderKey = null;
|
|
||||||
|
|
||||||
/** Siteminder username header key. */
|
/** Siteminder username header key. */
|
||||||
private String siteminderUsernameHeaderKey = null;
|
private String siteminderUsernameHeaderKey = null;
|
||||||
|
|
||||||
//~ Constructors ===================================================================================================
|
//~ Constructors ===================================================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic constructor.
|
* Basic constructor.
|
||||||
*/
|
*/
|
||||||
public SiteminderAuthenticationProcessingFilter() {
|
public SiteminderAuthenticationProcessingFilter() {
|
||||||
@ -76,24 +74,19 @@ public class SiteminderAuthenticationProcessingFilter extends AuthenticationProc
|
|||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @see org.acegisecurity.ui.AbstractProcessingFilter#attemptAuthentication(javax.servlet.http.HttpServletRequest)
|
* @see org.acegisecurity.ui.AbstractProcessingFilter#attemptAuthentication(javax.servlet.http.HttpServletRequest)
|
||||||
*/
|
*/
|
||||||
public Authentication attemptAuthentication(HttpServletRequest request)
|
public Authentication attemptAuthentication(final HttpServletRequest request) throws AuthenticationException {
|
||||||
throws AuthenticationException {
|
|
||||||
String username = null;
|
|
||||||
String password = null;
|
|
||||||
|
|
||||||
// Check the Siteminder headers for authentication info
|
String username = null;
|
||||||
if ((siteminderUsernameHeaderKey != null) && (siteminderUsernameHeaderKey.length() > 0)
|
|
||||||
&& (siteminderPasswordHeaderKey != null) && (siteminderPasswordHeaderKey.length() > 0)) {
|
// Check the Siteminder header for identification info
|
||||||
|
if ((siteminderUsernameHeaderKey != null) && (siteminderUsernameHeaderKey.length() > 0)) {
|
||||||
username = request.getHeader(siteminderUsernameHeaderKey);
|
username = request.getHeader(siteminderUsernameHeaderKey);
|
||||||
password = request.getHeader(siteminderPasswordHeaderKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the Siteminder authentication info wasn't available, then get it
|
// If the Siteminder identification info wasn't available, then try to get it from the form
|
||||||
// from the form parameters
|
if ((username == null) || (username.length() == 0)) {
|
||||||
if ((username == null) || (username.length() == 0) || (password == null) || (password.length() == 0)) {
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Siteminder headers not found for authentication, so trying to use form values");
|
logger.debug("Siteminder headers not found for authentication, so trying to use form values");
|
||||||
}
|
}
|
||||||
@ -104,7 +97,6 @@ public class SiteminderAuthenticationProcessingFilter extends AuthenticationProc
|
|||||||
username = request.getParameter(ACEGI_SECURITY_FORM_USERNAME_KEY);
|
username = request.getParameter(ACEGI_SECURITY_FORM_USERNAME_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
password = obtainPassword(request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert username and password to upper case. This is normally not a
|
// Convert username and password to upper case. This is normally not a
|
||||||
@ -117,14 +109,9 @@ public class SiteminderAuthenticationProcessingFilter extends AuthenticationProc
|
|||||||
username = "";
|
username = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password != null) {
|
// Pass in a null password value because it isn't relevant for Siteminder.
|
||||||
password = password.toUpperCase();
|
// Of course the AuthenticationManager needs to not care!
|
||||||
} else {
|
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, null);
|
||||||
// If password is null, set to blank to avoid a NPE.
|
|
||||||
password = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
|
|
||||||
|
|
||||||
// Allow subclasses to set the "details" property
|
// Allow subclasses to set the "details" property
|
||||||
setDetails(request, authRequest);
|
setDetails(request, authRequest);
|
||||||
@ -135,15 +122,6 @@ public class SiteminderAuthenticationProcessingFilter extends AuthenticationProc
|
|||||||
return this.getAuthenticationManager().authenticate(authRequest);
|
return this.getAuthenticationManager().authenticate(authRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the form password parameter key.
|
|
||||||
*
|
|
||||||
* @return The form password parameter key.
|
|
||||||
*/
|
|
||||||
public String getFormPasswordParameterKey() {
|
|
||||||
return formPasswordParameterKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the form username parameter key.
|
* Returns the form username parameter key.
|
||||||
*
|
*
|
||||||
@ -153,15 +131,6 @@ public class SiteminderAuthenticationProcessingFilter extends AuthenticationProc
|
|||||||
return formUsernameParameterKey;
|
return formUsernameParameterKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the Siteminder password header key.
|
|
||||||
*
|
|
||||||
* @return The Siteminder password header key.
|
|
||||||
*/
|
|
||||||
public String getSiteminderPasswordHeaderKey() {
|
|
||||||
return siteminderPasswordHeaderKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Siteminder username header key.
|
* Returns the Siteminder username header key.
|
||||||
*
|
*
|
||||||
@ -172,20 +141,14 @@ public class SiteminderAuthenticationProcessingFilter extends AuthenticationProc
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overridden method to obtain different value depending on whether Siteminder or form validation is being
|
* Overridden method to always return a null (Siteminder doesn't pass on the password).
|
||||||
* performed.
|
|
||||||
*
|
*
|
||||||
* @param request so that request attributes can be retrieved
|
* @param request so that request attributes can be retrieved
|
||||||
*
|
|
||||||
* @return the password that will be presented in the <code>Authentication</code> request token to the
|
* @return the password that will be presented in the <code>Authentication</code> request token to the
|
||||||
* <code>AuthenticationManager</code>
|
* <code>AuthenticationManager</code> (null).
|
||||||
*/
|
*/
|
||||||
protected String obtainPassword(HttpServletRequest request) {
|
protected String obtainPassword(final HttpServletRequest request) {
|
||||||
if ((formPasswordParameterKey != null) && (formPasswordParameterKey.length() > 0)) {
|
return null;
|
||||||
return request.getParameter(formPasswordParameterKey);
|
|
||||||
} else {
|
|
||||||
return request.getParameter(ACEGI_SECURITY_FORM_PASSWORD_KEY);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -197,6 +160,7 @@ public class SiteminderAuthenticationProcessingFilter extends AuthenticationProc
|
|||||||
* javax.servlet.http.HttpServletResponse)
|
* javax.servlet.http.HttpServletResponse)
|
||||||
*/
|
*/
|
||||||
protected boolean requiresAuthentication(final HttpServletRequest request, final HttpServletResponse response) {
|
protected boolean requiresAuthentication(final HttpServletRequest request, final HttpServletResponse response) {
|
||||||
|
|
||||||
String uri = request.getRequestURI();
|
String uri = request.getRequestURI();
|
||||||
int pathParamIndex = uri.indexOf(';');
|
int pathParamIndex = uri.indexOf(';');
|
||||||
|
|
||||||
@ -208,8 +172,8 @@ public class SiteminderAuthenticationProcessingFilter extends AuthenticationProc
|
|||||||
//attempt authentication if j_secuity_check is present or if the getDefaultTargetUrl()
|
//attempt authentication if j_secuity_check is present or if the getDefaultTargetUrl()
|
||||||
//is present and user is not already authenticated.
|
//is present and user is not already authenticated.
|
||||||
boolean bAuthenticated = false;
|
boolean bAuthenticated = false;
|
||||||
SecurityContext context = (SecurityContext) request.getSession()
|
SecurityContext context = (SecurityContext) request.getSession().getAttribute(
|
||||||
.getAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
|
HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
|
||||||
|
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
Authentication auth = context.getAuthentication();
|
Authentication auth = context.getAuthentication();
|
||||||
@ -231,15 +195,6 @@ public class SiteminderAuthenticationProcessingFilter extends AuthenticationProc
|
|||||||
return bAttemptAuthentication;
|
return bAttemptAuthentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the form password parameter key.
|
|
||||||
*
|
|
||||||
* @param key The form password parameter key.
|
|
||||||
*/
|
|
||||||
public void setFormPasswordParameterKey(final String key) {
|
|
||||||
this.formPasswordParameterKey = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the form username parameter key.
|
* Sets the form username parameter key.
|
||||||
*
|
*
|
||||||
@ -249,15 +204,6 @@ public class SiteminderAuthenticationProcessingFilter extends AuthenticationProc
|
|||||||
this.formUsernameParameterKey = key;
|
this.formUsernameParameterKey = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the Siteminder password header key.
|
|
||||||
*
|
|
||||||
* @param key The Siteminder password header key.
|
|
||||||
*/
|
|
||||||
public void setSiteminderPasswordHeaderKey(final String key) {
|
|
||||||
this.siteminderPasswordHeaderKey = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the Siteminder username header key.
|
* Sets the Siteminder username header key.
|
||||||
*
|
*
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
Authenticates users via a standard web form and <code>HttpSession</code>.
|
Authenticates users via HTTP properties, headers and session.
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -0,0 +1,430 @@
|
|||||||
|
/* 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.providers.siteminder;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.acegisecurity.AccountExpiredException;
|
||||||
|
import org.acegisecurity.Authentication;
|
||||||
|
import org.acegisecurity.AuthenticationServiceException;
|
||||||
|
import org.acegisecurity.BadCredentialsException;
|
||||||
|
import org.acegisecurity.CredentialsExpiredException;
|
||||||
|
import org.acegisecurity.DisabledException;
|
||||||
|
import org.acegisecurity.GrantedAuthority;
|
||||||
|
import org.acegisecurity.GrantedAuthorityImpl;
|
||||||
|
import org.acegisecurity.LockedException;
|
||||||
|
import org.acegisecurity.providers.TestingAuthenticationToken;
|
||||||
|
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.acegisecurity.providers.dao.UserCache;
|
||||||
|
import org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache;
|
||||||
|
import org.acegisecurity.providers.dao.cache.NullUserCache;
|
||||||
|
import org.acegisecurity.userdetails.User;
|
||||||
|
import org.acegisecurity.userdetails.UserDetails;
|
||||||
|
import org.acegisecurity.userdetails.UserDetailsService;
|
||||||
|
import org.acegisecurity.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.dao.DataRetrievalFailureException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link SiteminderAuthenticationProvider}.
|
||||||
|
*
|
||||||
|
* @author Ben Alex
|
||||||
|
* @version $Id: SiteminderAuthenticationProviderTests.java 1582 2006-07-15 15:18:51Z smccrory $
|
||||||
|
*/
|
||||||
|
public class SiteminderAuthenticationProviderTests extends TestCase {
|
||||||
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
junit.textui.TestRunner.run(SiteminderAuthenticationProviderTests.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAuthenticateFailsIfAccountExpired() {
|
||||||
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("peter", "opal");
|
||||||
|
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
provider.setUserDetailsService(new MockUserDetailsServiceUserPeterAccountExpired());
|
||||||
|
provider.setUserCache(new MockUserCache());
|
||||||
|
|
||||||
|
try {
|
||||||
|
provider.authenticate(token);
|
||||||
|
fail("Should have thrown AccountExpiredException");
|
||||||
|
} catch (AccountExpiredException expected) {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAuthenticateFailsIfAccountLocked() {
|
||||||
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("peter", "opal");
|
||||||
|
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
provider.setUserDetailsService(new MockUserDetailsServiceUserPeterAccountLocked());
|
||||||
|
provider.setUserCache(new MockUserCache());
|
||||||
|
|
||||||
|
try {
|
||||||
|
provider.authenticate(token);
|
||||||
|
fail("Should have thrown LockedException");
|
||||||
|
} catch (LockedException expected) {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAuthenticateFailsIfCredentialsExpired() {
|
||||||
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("peter", "opal");
|
||||||
|
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
provider.setUserDetailsService(new MockUserDetailsServiceUserPeterCredentialsExpired());
|
||||||
|
provider.setUserCache(new MockUserCache());
|
||||||
|
|
||||||
|
try {
|
||||||
|
provider.authenticate(token);
|
||||||
|
fail("Should have thrown CredentialsExpiredException");
|
||||||
|
} catch (CredentialsExpiredException expected) {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAuthenticateFailsIfUserDisabled() {
|
||||||
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("peter", "opal");
|
||||||
|
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
provider.setUserDetailsService(new MockUserDetailsServiceUserPeter());
|
||||||
|
provider.setUserCache(new MockUserCache());
|
||||||
|
|
||||||
|
try {
|
||||||
|
provider.authenticate(token);
|
||||||
|
fail("Should have thrown DisabledException");
|
||||||
|
} catch (DisabledException expected) {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAuthenticateFailsWhenUserDetailsServiceHasBackendFailure() {
|
||||||
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("marissa", "koala");
|
||||||
|
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
provider.setUserDetailsService(new MockUserDetailsServiceSimulateBackendError());
|
||||||
|
provider.setUserCache(new MockUserCache());
|
||||||
|
|
||||||
|
try {
|
||||||
|
provider.authenticate(token);
|
||||||
|
fail("Should have thrown AuthenticationServiceException");
|
||||||
|
} catch (AuthenticationServiceException expected) {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAuthenticateFailsWithEmptyUsername() {
|
||||||
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, "koala");
|
||||||
|
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
provider.setUserDetailsService(new MockUserDetailsServiceUserMarissa());
|
||||||
|
provider.setUserCache(new MockUserCache());
|
||||||
|
|
||||||
|
try {
|
||||||
|
provider.authenticate(token);
|
||||||
|
fail("Should have thrown BadCredentialsException");
|
||||||
|
} catch (BadCredentialsException expected) {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAuthenticateFailsWithInvalidUsernameAndHideUserNotFoundExceptionFalse() {
|
||||||
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("INVALID_USER", "koala");
|
||||||
|
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
provider.setHideUserNotFoundExceptions(false); // we want UsernameNotFoundExceptions
|
||||||
|
provider.setUserDetailsService(new MockUserDetailsServiceUserMarissa());
|
||||||
|
provider.setUserCache(new MockUserCache());
|
||||||
|
|
||||||
|
try {
|
||||||
|
provider.authenticate(token);
|
||||||
|
fail("Should have thrown UsernameNotFoundException");
|
||||||
|
} catch (UsernameNotFoundException expected) {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAuthenticateFailsWithInvalidUsernameAndHideUserNotFoundExceptionsWithDefaultOfTrue() {
|
||||||
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("INVALID_USER", "koala");
|
||||||
|
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
assertTrue(provider.isHideUserNotFoundExceptions());
|
||||||
|
provider.setUserDetailsService(new MockUserDetailsServiceUserMarissa());
|
||||||
|
provider.setUserCache(new MockUserCache());
|
||||||
|
|
||||||
|
try {
|
||||||
|
provider.authenticate(token);
|
||||||
|
fail("Should have thrown BadCredentialsException");
|
||||||
|
} catch (BadCredentialsException expected) {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAuthenticateFailsWithMixedCaseUsernameIfDefaultChanged() {
|
||||||
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("MaRiSSA", "koala");
|
||||||
|
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
provider.setUserDetailsService(new MockUserDetailsServiceUserMarissa());
|
||||||
|
provider.setUserCache(new MockUserCache());
|
||||||
|
|
||||||
|
try {
|
||||||
|
provider.authenticate(token);
|
||||||
|
fail("Should have thrown BadCredentialsException");
|
||||||
|
} catch (BadCredentialsException expected) {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAuthenticates() {
|
||||||
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("marissa", "koala");
|
||||||
|
token.setDetails("192.168.0.1");
|
||||||
|
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
provider.setUserDetailsService(new MockUserDetailsServiceUserMarissa());
|
||||||
|
provider.setUserCache(new MockUserCache());
|
||||||
|
|
||||||
|
Authentication result = provider.authenticate(token);
|
||||||
|
|
||||||
|
if (!(result instanceof UsernamePasswordAuthenticationToken)) {
|
||||||
|
fail("Should have returned instance of UsernamePasswordAuthenticationToken");
|
||||||
|
}
|
||||||
|
|
||||||
|
UsernamePasswordAuthenticationToken castResult = (UsernamePasswordAuthenticationToken) result;
|
||||||
|
assertEquals(User.class, castResult.getPrincipal().getClass());
|
||||||
|
assertEquals("koala", castResult.getCredentials());
|
||||||
|
assertEquals("ROLE_ONE", castResult.getAuthorities()[0].getAuthority());
|
||||||
|
assertEquals("ROLE_TWO", castResult.getAuthorities()[1].getAuthority());
|
||||||
|
assertEquals("192.168.0.1", castResult.getDetails());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAuthenticatesASecondTime() {
|
||||||
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("marissa", "koala");
|
||||||
|
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
provider.setUserDetailsService(new MockUserDetailsServiceUserMarissa());
|
||||||
|
provider.setUserCache(new MockUserCache());
|
||||||
|
|
||||||
|
Authentication result = provider.authenticate(token);
|
||||||
|
|
||||||
|
if (!(result instanceof UsernamePasswordAuthenticationToken)) {
|
||||||
|
fail("Should have returned instance of UsernamePasswordAuthenticationToken");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now try to authenticate with the previous result (with its UserDetails)
|
||||||
|
Authentication result2 = provider.authenticate(result);
|
||||||
|
|
||||||
|
if (!(result2 instanceof UsernamePasswordAuthenticationToken)) {
|
||||||
|
fail("Should have returned instance of UsernamePasswordAuthenticationToken");
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(result.getCredentials(), result2.getCredentials());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAuthenticatesWithForcePrincipalAsString() {
|
||||||
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("marissa", "koala");
|
||||||
|
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
provider.setUserDetailsService(new MockUserDetailsServiceUserMarissa());
|
||||||
|
provider.setUserCache(new MockUserCache());
|
||||||
|
provider.setForcePrincipalAsString(true);
|
||||||
|
|
||||||
|
Authentication result = provider.authenticate(token);
|
||||||
|
|
||||||
|
if (!(result instanceof UsernamePasswordAuthenticationToken)) {
|
||||||
|
fail("Should have returned instance of UsernamePasswordAuthenticationToken");
|
||||||
|
}
|
||||||
|
|
||||||
|
UsernamePasswordAuthenticationToken castResult = (UsernamePasswordAuthenticationToken) result;
|
||||||
|
assertEquals(String.class, castResult.getPrincipal().getClass());
|
||||||
|
assertEquals("marissa", castResult.getPrincipal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectsNullBeingReturnedFromUserDetailsService() {
|
||||||
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("marissa", "koala");
|
||||||
|
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
provider.setUserDetailsService(new MockUserDetailsServiceReturnsNull());
|
||||||
|
|
||||||
|
try {
|
||||||
|
provider.authenticate(token);
|
||||||
|
fail("Should have thrown AuthenticationServiceException");
|
||||||
|
} catch (AuthenticationServiceException expected) {
|
||||||
|
assertEquals("UserDetailsService returned null, which is an interface contract violation", expected
|
||||||
|
.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGettersSetters() {
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
|
||||||
|
provider.setUserCache(new EhCacheBasedUserCache());
|
||||||
|
assertEquals(EhCacheBasedUserCache.class, provider.getUserCache().getClass());
|
||||||
|
|
||||||
|
assertFalse(provider.isForcePrincipalAsString());
|
||||||
|
provider.setForcePrincipalAsString(true);
|
||||||
|
assertTrue(provider.isForcePrincipalAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testStartupFailsIfNoUserDetailsService() throws Exception {
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
|
||||||
|
try {
|
||||||
|
provider.afterPropertiesSet();
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testStartupFailsIfNoUserCacheSet() throws Exception {
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
provider.setUserDetailsService(new MockUserDetailsServiceUserMarissa());
|
||||||
|
assertEquals(NullUserCache.class, provider.getUserCache().getClass());
|
||||||
|
provider.setUserCache(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
provider.afterPropertiesSet();
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testStartupSuccess() throws Exception {
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
UserDetailsService userDetailsService = new MockUserDetailsServiceUserMarissa();
|
||||||
|
provider.setUserDetailsService(userDetailsService);
|
||||||
|
provider.setUserCache(new MockUserCache());
|
||||||
|
assertEquals(userDetailsService, provider.getUserDetailsService());
|
||||||
|
provider.afterPropertiesSet();
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSupports() {
|
||||||
|
SiteminderAuthenticationProvider provider = new SiteminderAuthenticationProvider();
|
||||||
|
assertTrue(provider.supports(UsernamePasswordAuthenticationToken.class));
|
||||||
|
assertTrue(!provider.supports(TestingAuthenticationToken.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
//~ Inner Classes ==================================================================================================
|
||||||
|
|
||||||
|
private class MockUserDetailsServiceReturnsNull implements UserDetailsService {
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockUserDetailsServiceSimulateBackendError implements UserDetailsService {
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
|
||||||
|
throw new DataRetrievalFailureException("This mock simulator is designed to fail");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockUserDetailsServiceUserMarissa implements UserDetailsService {
|
||||||
|
private String password = "koala";
|
||||||
|
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
|
||||||
|
if ("marissa".equals(username)) {
|
||||||
|
return new User("marissa", password, true, true, true, true, new GrantedAuthority[] {
|
||||||
|
new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO") });
|
||||||
|
} else {
|
||||||
|
throw new UsernameNotFoundException("Could not find: " + username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockUserDetailsServiceUserMarissaWithSalt implements UserDetailsService {
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
|
||||||
|
if ("marissa".equals(username)) {
|
||||||
|
return new User("marissa", "koala{SYSTEM_SALT_VALUE}", true, true, true, true, new GrantedAuthority[] {
|
||||||
|
new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO") });
|
||||||
|
} else {
|
||||||
|
throw new UsernameNotFoundException("Could not find: " + username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockUserDetailsServiceUserPeter implements UserDetailsService {
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
|
||||||
|
if ("peter".equals(username)) {
|
||||||
|
return new User("peter", "opal", false, true, true, true, new GrantedAuthority[] {
|
||||||
|
new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO") });
|
||||||
|
} else {
|
||||||
|
throw new UsernameNotFoundException("Could not find: " + username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockUserDetailsServiceUserPeterAccountExpired implements UserDetailsService {
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
|
||||||
|
if ("peter".equals(username)) {
|
||||||
|
return new User("peter", "opal", true, false, true, true, new GrantedAuthority[] {
|
||||||
|
new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO") });
|
||||||
|
} else {
|
||||||
|
throw new UsernameNotFoundException("Could not find: " + username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockUserDetailsServiceUserPeterAccountLocked implements UserDetailsService {
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
|
||||||
|
if ("peter".equals(username)) {
|
||||||
|
return new User("peter", "opal", true, true, true, false, new GrantedAuthority[] {
|
||||||
|
new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO") });
|
||||||
|
} else {
|
||||||
|
throw new UsernameNotFoundException("Could not find: " + username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockUserDetailsServiceUserPeterCredentialsExpired implements UserDetailsService {
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
|
||||||
|
if ("peter".equals(username)) {
|
||||||
|
return new User("peter", "opal", true, true, false, true, new GrantedAuthority[] {
|
||||||
|
new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO") });
|
||||||
|
} else {
|
||||||
|
throw new UsernameNotFoundException("Could not find: " + username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockUserCache implements UserCache {
|
||||||
|
private Map cache = new HashMap();
|
||||||
|
|
||||||
|
public UserDetails getUserFromCache(String username) {
|
||||||
|
return (User) cache.get(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putUserInCache(UserDetails user) {
|
||||||
|
cache.put(user.getUsername(), user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeUserFromCache(String username) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -19,13 +19,10 @@ import junit.framework.TestCase;
|
|||||||
|
|
||||||
import org.acegisecurity.Authentication;
|
import org.acegisecurity.Authentication;
|
||||||
import org.acegisecurity.MockAuthenticationManager;
|
import org.acegisecurity.MockAuthenticationManager;
|
||||||
|
|
||||||
import org.acegisecurity.ui.WebAuthenticationDetails;
|
import org.acegisecurity.ui.WebAuthenticationDetails;
|
||||||
|
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.mock.web.MockHttpServletResponse;
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests SiteminderAuthenticationProcessingFilter.
|
* Tests SiteminderAuthenticationProcessingFilter.
|
||||||
*
|
*
|
||||||
@ -36,14 +33,14 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
|||||||
public class SiteminderAuthenticationProcessingFilterTests extends TestCase {
|
public class SiteminderAuthenticationProcessingFilterTests extends TestCase {
|
||||||
//~ Constructors ===================================================================================================
|
//~ Constructors ===================================================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic constructor.
|
* Basic constructor.
|
||||||
*/
|
*/
|
||||||
public SiteminderAuthenticationProcessingFilterTests() {
|
public SiteminderAuthenticationProcessingFilterTests() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Argument constructor.
|
* Argument constructor.
|
||||||
*
|
*
|
||||||
* @param arg0
|
* @param arg0
|
||||||
@ -92,15 +89,9 @@ public class SiteminderAuthenticationProcessingFilterTests extends TestCase {
|
|||||||
filter.setFilterProcessesUrl("foobar");
|
filter.setFilterProcessesUrl("foobar");
|
||||||
assertEquals("foobar", filter.getFilterProcessesUrl());
|
assertEquals("foobar", filter.getFilterProcessesUrl());
|
||||||
|
|
||||||
filter.setFormPasswordParameterKey("passwordParamKey");
|
|
||||||
assertEquals("passwordParamKey", filter.getFormPasswordParameterKey());
|
|
||||||
|
|
||||||
filter.setFormUsernameParameterKey("usernameParamKey");
|
filter.setFormUsernameParameterKey("usernameParamKey");
|
||||||
assertEquals("usernameParamKey", filter.getFormUsernameParameterKey());
|
assertEquals("usernameParamKey", filter.getFormUsernameParameterKey());
|
||||||
|
|
||||||
filter.setSiteminderPasswordHeaderKey("passwordHeaderKey");
|
|
||||||
assertEquals("passwordHeaderKey", filter.getSiteminderPasswordHeaderKey());
|
|
||||||
|
|
||||||
filter.setSiteminderUsernameHeaderKey("usernameHeaderKey");
|
filter.setSiteminderUsernameHeaderKey("usernameHeaderKey");
|
||||||
assertEquals("usernameHeaderKey", filter.getSiteminderUsernameHeaderKey());
|
assertEquals("usernameHeaderKey", filter.getSiteminderUsernameHeaderKey());
|
||||||
}
|
}
|
||||||
@ -131,8 +122,7 @@ public class SiteminderAuthenticationProcessingFilterTests extends TestCase {
|
|||||||
*
|
*
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public void testFormNullPasswordHandledGracefully()
|
public void testFormNullPasswordHandledGracefully() throws Exception {
|
||||||
throws Exception {
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.addParameter(SiteminderAuthenticationProcessingFilter.ACEGI_SECURITY_FORM_USERNAME_KEY, "marissa");
|
request.addParameter(SiteminderAuthenticationProcessingFilter.ACEGI_SECURITY_FORM_USERNAME_KEY, "marissa");
|
||||||
|
|
||||||
@ -151,8 +141,7 @@ public class SiteminderAuthenticationProcessingFilterTests extends TestCase {
|
|||||||
*
|
*
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public void testFormNullUsernameHandledGracefully()
|
public void testFormNullUsernameHandledGracefully() throws Exception {
|
||||||
throws Exception {
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.addParameter(SiteminderAuthenticationProcessingFilter.ACEGI_SECURITY_FORM_PASSWORD_KEY, "koala");
|
request.addParameter(SiteminderAuthenticationProcessingFilter.ACEGI_SECURITY_FORM_PASSWORD_KEY, "koala");
|
||||||
|
|
||||||
@ -186,7 +175,6 @@ public class SiteminderAuthenticationProcessingFilterTests extends TestCase {
|
|||||||
filter.setAuthenticationManager(authMgrThatGrantsAccess);
|
filter.setAuthenticationManager(authMgrThatGrantsAccess);
|
||||||
|
|
||||||
filter.setSiteminderUsernameHeaderKey("SM_USER");
|
filter.setSiteminderUsernameHeaderKey("SM_USER");
|
||||||
filter.setSiteminderPasswordHeaderKey("SM_USER");
|
|
||||||
filter.init(null);
|
filter.init(null);
|
||||||
|
|
||||||
// Requests for an unknown URL should NOT require (re)authentication
|
// Requests for an unknown URL should NOT require (re)authentication
|
||||||
@ -220,7 +208,6 @@ public class SiteminderAuthenticationProcessingFilterTests extends TestCase {
|
|||||||
SiteminderAuthenticationProcessingFilter filter = new SiteminderAuthenticationProcessingFilter();
|
SiteminderAuthenticationProcessingFilter filter = new SiteminderAuthenticationProcessingFilter();
|
||||||
filter.setAuthenticationManager(authMgr);
|
filter.setAuthenticationManager(authMgr);
|
||||||
filter.setSiteminderUsernameHeaderKey("SM_USER");
|
filter.setSiteminderUsernameHeaderKey("SM_USER");
|
||||||
filter.setSiteminderPasswordHeaderKey("SM_USER");
|
|
||||||
filter.init(null);
|
filter.init(null);
|
||||||
|
|
||||||
Authentication result = filter.attemptAuthentication(request);
|
Authentication result = filter.attemptAuthentication(request);
|
||||||
|
@ -2120,7 +2120,8 @@ if (obj instanceof UserDetails) {
|
|||||||
Associates.</para>
|
Associates.</para>
|
||||||
|
|
||||||
<para>Acegi Security provides a filter,
|
<para>Acegi Security provides a filter,
|
||||||
<literal>SiteminderAuthenticationProcessingFilter</literal>) that can
|
<literal>SiteminderAuthenticationProcessingFilter</literal> and
|
||||||
|
provider, <literal>SiteminderAuthenticationProvider</literal> that can
|
||||||
be used to process requests that have been pre-authenticated by
|
be used to process requests that have been pre-authenticated by
|
||||||
Siteminder. This filter assumes that you're using Siteminder for
|
Siteminder. This filter assumes that you're using Siteminder for
|
||||||
<emphasis>authentication</emphasis>, and that you're using Acegi
|
<emphasis>authentication</emphasis>, and that you're using Acegi
|
||||||
@ -2128,13 +2129,14 @@ if (obj instanceof UserDetails) {
|
|||||||
for <emphasis>authorization</emphasis> is not yet directly supported
|
for <emphasis>authorization</emphasis> is not yet directly supported
|
||||||
by Acegi Security.</para>
|
by Acegi Security.</para>
|
||||||
|
|
||||||
<para>In Siteminder, an agent is setup on your web server to intercept
|
<para>When using Siteminder, an agent is setup on your web server to
|
||||||
a principal's first call to your application. The agent redirect the
|
intercept a principal's first call to your application. The agent
|
||||||
web request to a single sign on login page, and then your application
|
redirects the web request to a single sign-on login page, and once
|
||||||
receives the request. Inside the HTTP headers is a header - such as
|
authenticated, your application receives the request. Inside the HTTP
|
||||||
<literal>SM_USER</literal> - which identifies the authenticated
|
request is a header - such as <literal>SM_USER</literal> - which
|
||||||
principal. Please refer to your organization's "single sign-on" group
|
identifies the authenticated principal (please refer to your
|
||||||
for header details in your particular configuration.</para>
|
organization's "single sign-on" group for header details in your
|
||||||
|
particular configuration).</para>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
<sect1 id="siteminder-config">
|
<sect1 id="siteminder-config">
|
||||||
@ -2142,9 +2144,9 @@ if (obj instanceof UserDetails) {
|
|||||||
|
|
||||||
<para>The first step in setting up Acegi Security's Siteminder support
|
<para>The first step in setting up Acegi Security's Siteminder support
|
||||||
is to define the authentication mechanism that will inspect the HTTP
|
is to define the authentication mechanism that will inspect the HTTP
|
||||||
header discussed earlier. It will then generate a
|
header discussed earlier. It will be responsible for generating a
|
||||||
<literal>UsernamePasswordAuthenticationToken</literal> that can later
|
<literal>UsernamePasswordAuthenticationToken</literal> that is later
|
||||||
on be sent to the <literal>DaoAuthenticationProvider</literal>. Let's
|
sent to the <literal>SiteminderAuthenticationProvider</literal>. Let's
|
||||||
look at an example:</para>
|
look at an example:</para>
|
||||||
|
|
||||||
<para><programlisting><bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.SiteminderAuthenticationProcessingFilter">
|
<para><programlisting><bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.SiteminderAuthenticationProcessingFilter">
|
||||||
@ -2153,51 +2155,37 @@ if (obj instanceof UserDetails) {
|
|||||||
<property name="defaultTargetUrl"><value>/security.do?method=getMainMenu</value></property>
|
<property name="defaultTargetUrl"><value>/security.do?method=getMainMenu</value></property>
|
||||||
<property name="filterProcessesUrl"><value>/j_acegi_security_check</value></property>
|
<property name="filterProcessesUrl"><value>/j_acegi_security_check</value></property>
|
||||||
<property name="siteminderUsernameHeaderKey"><value>SM_USER</value></property>
|
<property name="siteminderUsernameHeaderKey"><value>SM_USER</value></property>
|
||||||
<property name="siteminderPasswordHeaderKey"><value>SM_USER</value></property>
|
<property name="formUsernameParameterKey"><value>j_username</value></property>
|
||||||
</bean></programlisting></para>
|
</bean></programlisting></para>
|
||||||
|
|
||||||
<para>In our example above, the bean is being provided an
|
<para>In our example above, the bean is being provided an
|
||||||
<literal>AuthenticationManager</literal>, as is normally needed by
|
<literal>AuthenticationManager</literal>, as is normally needed by
|
||||||
authentication mechanisms. Several URLs are also specified, with the
|
authentication mechanisms. Several URLs are also specified, with the
|
||||||
values being self-explanatory. It's important to also specify the HTTP
|
values being self-explanatory. It's important to also specify the HTTP
|
||||||
headers that Acegi Security should inspect. Most people won't need the
|
header that Acegi Security should inspect. If you additionally want to
|
||||||
password value since Siteminder has already authenticated the user, so
|
support form-based authentication (i.e. in your development
|
||||||
it's typical to use the same header for both.</para>
|
environment where Siteminder is not installed), specify the form's
|
||||||
|
username parameter as well - just don't do this in production!</para>
|
||||||
|
|
||||||
<para>Note that you'll need a
|
<para>Note that you'll need a
|
||||||
<literal><literal>DaoAuthenticationProvider</literal></literal>
|
<literal><literal>SiteminderAuthenticationProvider</literal></literal>
|
||||||
configured against your <literal>ProviderManager</literal> in order to
|
configured against your <literal>ProviderManager</literal> in order to
|
||||||
use the Siteminder authentication mechanism. Normally a
|
use the Siteminder authentication mechanism. Normally an
|
||||||
<literal>DaoAuthenticationProvider</literal> expects the password
|
<literal>AuthenticationProvider</literal> expects the password
|
||||||
property to match what it retrieves from the
|
property to match what it retrieves from the
|
||||||
<literal>UserDetailsSource</literal>. In this case, authentication has
|
<literal>UserDetailsSource</literal>, but in this case, authentication
|
||||||
already been handled by Siteminder and you've specified the same HTTP
|
has already been handled by Siteminder, so password property is not
|
||||||
header for both username and password. As such, you must modify the
|
even relevant. This may sound like a security weakness, but remember
|
||||||
code of <literal>DaoAuthenticationProvider</literal> to simply make
|
that users have to authenticate with Siteminder before your
|
||||||
sure the username and password values match. This may sound like a
|
application ever receives the requests, so the purpose of your custom
|
||||||
security weakness, but remember that users have to authenticate with
|
<literal>UserDetailsService</literal> should simply be to build the
|
||||||
Siteminder before your application ever receives the requests, so the
|
complete <literal>Authentication</literal> object (ie with suitable
|
||||||
purpose of your custom <literal>DaoAuthenticationProvider</literal>
|
|
||||||
should simply be to build the complete
|
|
||||||
<literal>Authentication</literal> object (ie with suitable
|
|
||||||
<literal>GrantedAuthority[]</literal>s).</para>
|
<literal>GrantedAuthority[]</literal>s).</para>
|
||||||
|
|
||||||
<para>Advanced tip and word to the wise: the
|
<para>Advanced tip and word to the wise: If you additionally want to
|
||||||
<literal>SiteminderAuthenticationProcessingFilter</literal> actually
|
support form-based authentication in your development environment
|
||||||
extends <literal>AuthenticationProcessingFilter</literal> and thus
|
(where Siteminder is typically not installed), specify the form's
|
||||||
additionally supports form validation. If you configure the filter to
|
username parameter as well. Just don't do this in production!</para>
|
||||||
support both, and code your
|
|
||||||
<literal>daoAuthenticationProvider</literal> to match the username and
|
|
||||||
passwords as described above, you'll potentially defeat any security
|
|
||||||
you have in place if the web server's Siteminder agent is deactivated.
|
|
||||||
Don't do this, especially in production!</para>
|
|
||||||
|
|
||||||
<para>TODO: We should write a dedicated
|
|
||||||
<literal>AuthenticationProvider</literal> rather than require users to
|
|
||||||
modify their <literal>DaoAuthenticationProvider</literal>. Also review
|
|
||||||
the mixed use of SiteminderAuthenticationProcessingFilter, as it's
|
|
||||||
inconsistent with the rest of Acegi Security's authentication
|
|
||||||
mechanisms which are high cohesion.</para>
|
|
||||||
</sect1>
|
</sect1>
|
||||||
</chapter>
|
</chapter>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user