SEC-477: Added preauthenticated websphere contribution.
This commit is contained in:
+55
@@ -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);
|
||||
}
|
||||
}
|
||||
Executable
+194
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+96
@@ -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;
|
||||
}
|
||||
}
|
||||
+85
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+39
@@ -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;
|
||||
}
|
||||
}
|
||||
+24
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user