1
0
mirror of synced 2026-08-05 09:47:05 +00:00

SEC-477: Added preauthenticated websphere contribution.

This commit is contained in:
Luke Taylor
2008-03-27 14:25:17 +00:00
parent 608e8d3610
commit 350a626587
6 changed files with 493 additions and 0 deletions
@@ -0,0 +1,55 @@
package org.springframework.security.ui.preauth;
import java.util.Arrays;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.MutableGrantedAuthoritiesContainer;
import org.springframework.security.ui.AuthenticationDetails;
import org.springframework.util.Assert;
/**
* This AuthenticationDetails implementation allows for storing a list of
* pre-authenticated Granted Authorities.
*
* @author Ruud Senden
* @since 2.0
*/
public class PreAuthenticatedGrantedAuthoritiesAuthenticationDetails extends AuthenticationDetails implements
MutableGrantedAuthoritiesContainer {
public static final long serialVersionUID = 1L;
private GrantedAuthority[] preAuthenticatedGrantedAuthorities = null;
public PreAuthenticatedGrantedAuthoritiesAuthenticationDetails(Object context) {
super(context);
}
/**
* @return The String representation of this object.
*/
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(super.toString() + "; ");
sb.append("preAuthenticatedGrantedAuthorities: " + Arrays.asList(preAuthenticatedGrantedAuthorities));
return sb.toString();
}
/**
*
* @see org.springframework.security.GrantedAuthoritiesContainer#getGrantedAuthorities()
*/
public GrantedAuthority[] getGrantedAuthorities() {
Assert.notNull(preAuthenticatedGrantedAuthorities, "Pre-authenticated granted authorities have not been set");
GrantedAuthority[] result = new GrantedAuthority[preAuthenticatedGrantedAuthorities.length];
System.arraycopy(preAuthenticatedGrantedAuthorities, 0, result, 0, result.length);
return result;
}
/**
* @see org.springframework.security.MutableGrantedAuthoritiesContainer#setGrantedAuthorities()
*/
public void setGrantedAuthorities(GrantedAuthority[] aJ2eeBasedGrantedAuthorities) {
this.preAuthenticatedGrantedAuthorities = new GrantedAuthority[aJ2eeBasedGrantedAuthorities.length];
System.arraycopy(aJ2eeBasedGrantedAuthorities, 0, preAuthenticatedGrantedAuthorities, 0, preAuthenticatedGrantedAuthorities.length);
}
}
@@ -0,0 +1,194 @@
package org.springframework.security.ui.preauth.websphere;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import javax.security.auth.Subject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* WebSphere Security helper class to allow retrieval of the current username and groups.
* <p>
* See Spring Security JIRA SEC-477.
*
* @author Ruud Senden
* @author Stephane Manciot
* @since 2.0
*/
final class WASSecurityHelper {
private static final Log logger = LogFactory.getLog(WASSecurityHelper.class);
private static final String USER_REGISTRY = "UserRegistry";
private static Method getRunAsSubject = null;
private static Method getWSCredentialFromSubject = null;
private static Method getGroupsForUser = null;
private static Method getSecurityName = null;
/**
* Get the security name for the given subject.
*
* @param subject
* The subject for which to retrieve the security name
* @return String the security name for the given subject
*/
private static final String getSecurityName(final Subject subject) {
if (logger.isDebugEnabled()) {
logger.debug("Determining Websphere security name for subject " + subject);
}
String userSecurityName = null;
if (subject != null) {
Object credential = invokeMethod(getWSCredentialFromSubjectMethod(),null,new Object[]{subject});
if (credential != null) {
userSecurityName = (String)invokeMethod(getSecurityNameMethod(),credential,null);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Websphere security name is " + userSecurityName + " for subject " + subject);
}
return userSecurityName;
}
/**
* Get the current RunAs subject.
*
* @return Subject the current RunAs subject
*/
private static final Subject getRunAsSubject() {
logger.debug("Retrieving WebSphere RunAs subject");
// get Subject: WSSubject.getCallerSubject ();
return (Subject) invokeMethod(getRunAsSubjectMethod(), null, new Object[] {});
}
/**
* Get the WebSphere group names for the given subject.
*
* @param subject
* The subject for which to retrieve the WebSphere group names
* @return the WebSphere group names for the given subject
*/
private static final String[] getWebSphereGroups(final Subject subject) {
return getWebSphereGroups(getSecurityName(subject));
}
/**
* Get the WebSphere group names for the given security name.
*
* @param securityName
* The securityname for which to retrieve the WebSphere group names
* @return the WebSphere group names for the given security name
*/
private static final String[] getWebSphereGroups(final String securityName) {
Context ic = null;
try {
// TODO: Cache UserRegistry object
ic = new InitialContext();
Object objRef = ic.lookup(USER_REGISTRY);
Object userReg = PortableRemoteObject.narrow(objRef, Class.forName ("com.ibm.websphere.security.UserRegistry"));
if (logger.isDebugEnabled()) {
logger.debug("Determining WebSphere groups for user " + securityName + " using WebSphere UserRegistry " + userReg);
}
final Collection groups = (Collection) invokeMethod(getGroupsForUserMethod(), userReg, new Object[]{ securityName });
if (logger.isDebugEnabled()) {
logger.debug("Groups for user " + securityName + ": " + groups.toString());
}
String[] result = new String[groups.size()];
return (String[]) groups.toArray(result);
} catch (Exception e) {
logger.error("Exception occured while looking up groups for user", e);
throw new RuntimeException("Exception occured while looking up groups for user", e);
} finally {
try {
ic.close();
} catch (NamingException e) {
logger.debug("Exception occured while closing context", e);
}
}
}
/**
* @return
*/
public static final String[] getGroupsForCurrentUser() {
return getWebSphereGroups(getRunAsSubject());
}
public static final String getCurrentUserName() {
return getSecurityName(getRunAsSubject());
}
private static final Object invokeMethod(Method method, Object instance, Object[] args)
{
try {
return method.invoke(instance,args);
} catch (IllegalArgumentException e) {
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+ Arrays.asList(args)+")",e);
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
} catch (IllegalAccessException e) {
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
} catch (InvocationTargetException e) {
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
}
}
private static final Method getMethod(String className, String methodName, String[] parameterTypeNames) {
try {
Class c = Class.forName(className);
final int len = parameterTypeNames.length;
Class[] parameterTypes = new Class[len];
for (int i = 0; i < len; i++) {
parameterTypes[i] = Class.forName(parameterTypeNames[i]);
}
return c.getDeclaredMethod(methodName, parameterTypes);
} catch (ClassNotFoundException e) {
logger.error("Required class"+className+" not found");
throw new RuntimeException("Required class"+className+" not found",e);
} catch (NoSuchMethodException e) {
logger.error("Required method "+methodName+" with parameter types ("+ Arrays.asList(parameterTypeNames) +") not found on class "+className);
throw new RuntimeException("Required class"+className+" not found",e);
}
}
private static final Method getRunAsSubjectMethod() {
if (getRunAsSubject == null) {
getRunAsSubject = getMethod("com.ibm.websphere.security.auth.WSSubject", "getRunAsSubject", new String[] {});
}
return getRunAsSubject;
}
private static final Method getWSCredentialFromSubjectMethod() {
if (getWSCredentialFromSubject == null) {
getWSCredentialFromSubject = getMethod("com.ibm.ws.security.auth.SubjectHelper", "getWSCredentialFromSubject",
new String[] { "javax.security.auth.Subject" });
}
return getWSCredentialFromSubject;
}
private static final Method getGroupsForUserMethod() {
if (getGroupsForUser == null) {
getGroupsForUser = getMethod("com.ibm.websphere.security.UserRegistry", "getGroupsForUser", new String[] { "java.lang.String" });
}
return getGroupsForUser;
}
private static final Method getSecurityNameMethod() {
if (getSecurityName == null) {
getSecurityName = getMethod("com.ibm.websphere.security.cred.WSCredential", "getSecurityName", new String[] {});
}
return getSecurityName;
}
}
@@ -0,0 +1,96 @@
package org.springframework.security.ui.preauth.websphere;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.Authentication;
import org.springframework.security.AuthenticationManager;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.security.ui.AuthenticationDetailsSource;
import org.springframework.util.Assert;
/**
* This method interceptor can be used in front of arbitrary Spring beans to make a Spring SecurityContext
* available to the bean, based on the current WebSphere credentials.
*
* @author Ruud Senden
* @since 1.0
*/
public class WebSphere2SpringSecurityPropagationInterceptor implements MethodInterceptor {
private static final Log LOG = LogFactory.getLog(WebSphere2SpringSecurityPropagationInterceptor.class);
private AuthenticationManager authenticationManager = null;
private AuthenticationDetailsSource authenticationDetailsSource = new WebSpherePreAuthenticatedAuthenticationDetailsSource();
/**
* Authenticate with Spring Security based on WebSphere credentials before proceeding with method
* invocation, and clean up the Spring Security Context after method invocation finishes.
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
try {
LOG.debug("Performing Spring Security authentication with WebSphere credentials");
authenticateSpringSecurityWithWASCredentials(this);
LOG.debug("Proceeding with method invocation");
return methodInvocation.proceed();
} finally {
LOG.debug("Clearing Spring Security security context");
clearSpringSecurityContext();
}
}
/**
* Retrieve the current WebSphere credentials and authenticate them with Spring Security
* using the pre-authenticated authentication provider.
* @param aContext The context to use for building the authentication details.
*/
private final void authenticateSpringSecurityWithWASCredentials(Object aContext)
{
Assert.notNull(authenticationManager);
Assert.notNull(authenticationDetailsSource);
String userName = WASSecurityHelper.getCurrentUserName();
if (LOG.isDebugEnabled()) { LOG.debug("Creating authentication request for user "+userName); }
PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(userName,null);
authRequest.setDetails(authenticationDetailsSource.buildDetails(null));
if (LOG.isDebugEnabled()) { LOG.debug("Authentication request for user "+userName+": "+authRequest); }
Authentication authResponse = authenticationManager.authenticate(authRequest);
if (LOG.isDebugEnabled()) { LOG.debug("Authentication response for user "+userName+": "+authResponse); }
SecurityContextHolder.getContext().setAuthentication(authResponse);
}
/**
* Clear the Spring Security Context
*/
private final void clearSpringSecurityContext()
{
SecurityContextHolder.clearContext();
}
/**
* @return Returns the authenticationManager.
*/
public AuthenticationManager getAuthenticationManager() {
return authenticationManager;
}
/**
* @param authenticationManager The authenticationManager to set.
*/
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
/**
* @return Returns the authenticationDetailsSource.
*/
public AuthenticationDetailsSource getAuthenticationDetailsSource() {
return authenticationDetailsSource;
}
/**
* @param authenticationDetailsSource The authenticationDetailsSource to set.
*/
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
this.authenticationDetailsSource = authenticationDetailsSource;
}
}
@@ -0,0 +1,85 @@
package org.springframework.security.ui.preauth.websphere;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.MutableGrantedAuthoritiesContainer;
import org.springframework.security.authoritymapping.Attributes2GrantedAuthoritiesMapper;
import org.springframework.security.authoritymapping.SimpleAttributes2GrantedAuthoritiesMapper;
import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
import org.springframework.security.ui.preauth.PreAuthenticatedGrantedAuthoritiesAuthenticationDetails;
import org.springframework.util.Assert;
/**
* This AuthenticationDetailsSource implementation, when configured with a MutableGrantedAuthoritiesContainer,
* will set the pre-authenticated granted authorities based on the WebSphere groups for the current WebSphere
* user, mapped using the configured Attributes2GrantedAuthoritiesMapper.
*
* By default, this class is configured to build instances of the
* PreAuthenticatedGrantedAuthoritiesAuthenticationDetails class.
*
* @author Ruud Senden
*/
public class WebSpherePreAuthenticatedAuthenticationDetailsSource extends AuthenticationDetailsSourceImpl implements InitializingBean {
private static final Log LOG = LogFactory.getLog(WebSpherePreAuthenticatedAuthenticationDetailsSource.class);
private Attributes2GrantedAuthoritiesMapper webSphereGroups2GrantedAuthoritiesMapper = new SimpleAttributes2GrantedAuthoritiesMapper();
/**
* Public constructor which overrides the default AuthenticationDetails
* class to be used.
*/
public WebSpherePreAuthenticatedAuthenticationDetailsSource() {
super.setClazz(PreAuthenticatedGrantedAuthoritiesAuthenticationDetails.class);
}
/**
* Check that all required properties have been set.
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(webSphereGroups2GrantedAuthoritiesMapper, "WebSphere groups to granted authorities mapper not set");
}
/**
* Build the authentication details object. If the speficied authentication
* details class implements the PreAuthenticatedGrantedAuthoritiesSetter, a
* list of pre-authenticated Granted Authorities will be set based on the
* WebSphere groups for the current user.
*
* @see org.springframework.security.ui.AuthenticationDetailsSource#buildDetails(Object)
*/
public Object buildDetails(Object context) {
Object result = super.buildDetails(context);
if (result instanceof MutableGrantedAuthoritiesContainer) {
((MutableGrantedAuthoritiesContainer) result)
.setGrantedAuthorities(getWebSphereGroupsBasedGrantedAuthorities());
}
return result;
}
/**
* Get a list of Granted Authorities based on the current user's WebSphere groups.
*
* @return GrantedAuthority[] mapped from the user's WebSphere groups.
*/
private GrantedAuthority[] getWebSphereGroupsBasedGrantedAuthorities() {
String[] webSphereGroups = WASSecurityHelper.getGroupsForCurrentUser();
GrantedAuthority[] userGas = webSphereGroups2GrantedAuthoritiesMapper.getGrantedAuthorities(webSphereGroups);
if (LOG.isDebugEnabled()) {
LOG.debug("WebSphere groups [" + ArrayUtils.toString(webSphereGroups) + "] mapped to Granted Authorities: ["
+ ArrayUtils.toString(userGas) + "]");
}
return userGas;
}
/**
* @param mapper
* The Attributes2GrantedAuthoritiesMapper to use
*/
public void setWebSphereGroups2GrantedAuthoritiesMapper(Attributes2GrantedAuthoritiesMapper mapper) {
webSphereGroups2GrantedAuthoritiesMapper = mapper;
}
}
@@ -0,0 +1,39 @@
package org.springframework.security.ui.preauth.websphere;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.ui.preauth.AbstractPreAuthenticatedProcessingFilter;
/**
* This AbstractPreAuthenticatedProcessingFilter implementation is based on
* WebSphere authentication. It will use the WebSphere RunAs user principal name
* as the pre-authenticated principal.
*
* @author Ruud Senden
* @since 2.0
*/
public class WebSpherePreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter {
/**
* Return the WebSphere user name.
*/
protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest) {
Object principal = WASSecurityHelper.getCurrentUserName();
if (logger.isDebugEnabled()) {
logger.debug("PreAuthenticated WebSphere principal: " + principal);
}
return principal;
}
/**
* For J2EE container-based authentication there is no generic way to
* retrieve the credentials, as such this method returns a fixed dummy
* value.
*/
protected Object getPreAuthenticatedCredentials(HttpServletRequest httpRequest) {
return "N/A";
}
public int getOrder() {
return 0;
}
}
@@ -0,0 +1,24 @@
package org.springframework.security.ui.preauth.websphere;
import org.springframework.security.ui.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails;
/**
* This AuthenticationDetailsSource implementation, when configured with a MutableGrantedAuthoritiesContainer,
* will set the pre-authenticated granted authorities based on the WebSphere groups for the current WebSphere
* user, mapped using the configured Attributes2GrantedAuthoritiesMapper.
*
* By default, this class is configured to build instances of the
* PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails class.
*
* @author Ruud Senden
*/
public class WebSpherePreAuthenticatedWebAuthenticationDetailsSource extends WebSpherePreAuthenticatedAuthenticationDetailsSource {
/**
* Public constructor which overrides the default AuthenticationDetails
* class to be used.
*/
public WebSpherePreAuthenticatedWebAuthenticationDetailsSource() {
super();
super.setClazz(PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails.class);
}
}