SEC-1078: Converted WASSecurityHelper to an internal interface and added test for scenario from this issue.
This commit is contained in:
parent
e45f6914ee
commit
6bd7421a1b
|
@ -1,206 +0,0 @@
|
|||
package org.springframework.security.web.authentication.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 getGroupsForUser = null;
|
||||
|
||||
private static Method getSecurityName = null;
|
||||
|
||||
// SEC-803
|
||||
private static Class<?> wsCredentialClass = 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) {
|
||||
// SEC-803
|
||||
Object credential = subject.getPublicCredentials(getWSCredentialClass()).iterator().next();
|
||||
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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
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 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;
|
||||
}
|
||||
|
||||
// SEC-803
|
||||
private static final Class<?> getWSCredentialClass() {
|
||||
if (wsCredentialClass == null) {
|
||||
wsCredentialClass = getClass("com.ibm.websphere.security.cred.WSCredential");
|
||||
}
|
||||
return wsCredentialClass;
|
||||
}
|
||||
|
||||
private static final Class<?> getClass(String className) {
|
||||
try {
|
||||
return Class.forName(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
logger.error("Required class " + className + " not found");
|
||||
throw new RuntimeException("Required class " + className + " not found",e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package org.springframework.security.web.authentication.preauth.websphere;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides indirection between classes using websphere and the actual container interaction,
|
||||
* allowing for easier unit testing.
|
||||
* <p>
|
||||
* Only for internal use.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface WASUsernameAndGroupsExtractor {
|
||||
|
||||
List<String> getGroupsForCurrentUser();
|
||||
|
||||
String getCurrentUserName();
|
||||
}
|
|
@ -14,15 +14,24 @@ 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 static final Log logger = LogFactory.getLog(WebSphere2SpringSecurityPropagationInterceptor.class);
|
||||
private AuthenticationManager authenticationManager = null;
|
||||
private AuthenticationDetailsSource authenticationDetailsSource = new WebSpherePreAuthenticatedAuthenticationDetailsSource();
|
||||
|
||||
private final WASUsernameAndGroupsExtractor wasHelper;
|
||||
|
||||
public WebSphere2SpringSecurityPropagationInterceptor() {
|
||||
this(new DefaultWASUsernameAndGroupsExtractor());
|
||||
}
|
||||
|
||||
WebSphere2SpringSecurityPropagationInterceptor(WASUsernameAndGroupsExtractor wasHelper) {
|
||||
this.wasHelper = wasHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate with Spring Security based on WebSphere credentials before proceeding with method
|
||||
* invocation, and clean up the Spring Security Context after method invocation finishes.
|
||||
|
@ -30,63 +39,42 @@ public class WebSphere2SpringSecurityPropagationInterceptor implements MethodInt
|
|||
*/
|
||||
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
|
||||
try {
|
||||
LOG.debug("Performing Spring Security authentication with WebSphere credentials");
|
||||
logger.debug("Performing Spring Security authentication with WebSphere credentials");
|
||||
authenticateSpringSecurityWithWASCredentials(this);
|
||||
LOG.debug("Proceeding with method invocation");
|
||||
logger.debug("Proceeding with method invocation");
|
||||
return methodInvocation.proceed();
|
||||
} finally {
|
||||
LOG.debug("Clearing Spring Security security context");
|
||||
clearSpringSecurityContext();
|
||||
logger.debug("Clearing Spring Security security context");
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
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);
|
||||
|
||||
String userName = wasHelper.getCurrentUserName();
|
||||
if (logger.isDebugEnabled()) { logger.debug("Creating authentication request for user "+userName); }
|
||||
PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(userName, "N/A");
|
||||
authRequest.setDetails(authenticationDetailsSource.buildDetails(null));
|
||||
if (LOG.isDebugEnabled()) { LOG.debug("Authentication request for user "+userName+": "+authRequest); }
|
||||
if (logger.isDebugEnabled()) { logger.debug("Authentication request for user "+userName+": "+authRequest); }
|
||||
Authentication authResponse = authenticationManager.authenticate(authRequest);
|
||||
if (LOG.isDebugEnabled()) { LOG.debug("Authentication response for user "+userName+": "+authResponse); }
|
||||
if (logger.isDebugEnabled()) { logger.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.
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.springframework.security.web.authentication.preauth.websphere;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
@ -29,12 +28,19 @@ public class WebSpherePreAuthenticatedAuthenticationDetailsSource extends Authen
|
|||
|
||||
private Attributes2GrantedAuthoritiesMapper webSphereGroups2GrantedAuthoritiesMapper = new SimpleAttributes2GrantedAuthoritiesMapper();
|
||||
|
||||
private final WASUsernameAndGroupsExtractor wasHelper;
|
||||
|
||||
/**
|
||||
* Public constructor which overrides the default AuthenticationDetails
|
||||
* class to be used.
|
||||
*/
|
||||
public WebSpherePreAuthenticatedAuthenticationDetailsSource() {
|
||||
this(new DefaultWASUsernameAndGroupsExtractor());
|
||||
}
|
||||
|
||||
WebSpherePreAuthenticatedAuthenticationDetailsSource(WASUsernameAndGroupsExtractor wasHelper) {
|
||||
super.setClazz(PreAuthenticatedGrantedAuthoritiesAuthenticationDetails.class);
|
||||
this.wasHelper = wasHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,10 +70,10 @@ public class WebSpherePreAuthenticatedAuthenticationDetailsSource extends Authen
|
|||
/**
|
||||
* Get a list of Granted Authorities based on the current user's WebSphere groups.
|
||||
*
|
||||
* @return GrantedAuthority[] mapped from the user's WebSphere groups.
|
||||
* @return authorities mapped from the user's WebSphere groups.
|
||||
*/
|
||||
private List<GrantedAuthority> getWebSphereGroupsBasedGrantedAuthorities() {
|
||||
List<String> webSphereGroups = Arrays.asList(WASSecurityHelper.getGroupsForCurrentUser());
|
||||
List<String> webSphereGroups = wasHelper.getGroupsForCurrentUser();
|
||||
List<GrantedAuthority> userGas = webSphereGroups2GrantedAuthoritiesMapper.getGrantedAuthorities(webSphereGroups);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("WebSphere groups: " + webSphereGroups + " mapped to Granted Authorities: " + userGas);
|
||||
|
|
|
@ -6,18 +6,33 @@ import org.springframework.security.web.authentication.preauth.AbstractPreAuthen
|
|||
|
||||
/**
|
||||
* This AbstractPreAuthenticatedProcessingFilter implementation is based on
|
||||
* WebSphere authentication. It will use the WebSphere RunAs user principal name
|
||||
* 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 {
|
||||
private final WASUsernameAndGroupsExtractor wasHelper;
|
||||
|
||||
/**
|
||||
* Public constructor which overrides the default AuthenticationDetails
|
||||
* class to be used.
|
||||
*/
|
||||
public WebSpherePreAuthenticatedProcessingFilter() {
|
||||
this(new DefaultWASUsernameAndGroupsExtractor());
|
||||
}
|
||||
|
||||
WebSpherePreAuthenticatedProcessingFilter(WASUsernameAndGroupsExtractor wasHelper) {
|
||||
this.wasHelper = wasHelper;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the WebSphere user name.
|
||||
*/
|
||||
protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest) {
|
||||
Object principal = WASSecurityHelper.getCurrentUserName();
|
||||
Object principal = wasHelper.getCurrentUserName();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("PreAuthenticated WebSphere principal: " + principal);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package org.springframework.security.web.authentication.preauth.websphere;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.preauth.PreAuthenticatedAuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsChecker;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 3.0
|
||||
*/
|
||||
public class WebSphere2SpringSecurityPropagationInterceptorTests {
|
||||
|
||||
@After
|
||||
public void clearContext() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
/** SEC-1078 */
|
||||
@Test
|
||||
public void createdAuthenticationTokenIsAcceptableToPreauthProvider () throws Throwable {
|
||||
WASUsernameAndGroupsExtractor helper = mock(WASUsernameAndGroupsExtractor.class);
|
||||
when(helper.getCurrentUserName()).thenReturn("joe");
|
||||
WebSphere2SpringSecurityPropagationInterceptor interceptor =
|
||||
new WebSphere2SpringSecurityPropagationInterceptor(helper);
|
||||
|
||||
final SecurityContext context = new SecurityContextImpl();
|
||||
|
||||
interceptor.setAuthenticationManager(new AuthenticationManager() {
|
||||
public Authentication authenticate(Authentication authentication) {
|
||||
// Store the auth object
|
||||
context.setAuthentication(authentication);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
interceptor.setAuthenticationDetailsSource(mock(AuthenticationDetailsSource.class));
|
||||
interceptor.invoke(mock(MethodInvocation.class));
|
||||
|
||||
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
|
||||
AuthenticationUserDetailsService uds = mock(AuthenticationUserDetailsService.class);
|
||||
UserDetails user = mock(UserDetails.class);
|
||||
when(user.getAuthorities()).thenReturn(AuthorityUtils.createAuthorityList("SOME_ROLE"));
|
||||
when(uds.loadUserDetails(any(Authentication.class))).thenReturn(user);
|
||||
provider.setPreAuthenticatedUserDetailsService(uds);
|
||||
provider.setUserDetailsChecker(mock(UserDetailsChecker.class));
|
||||
|
||||
assertNotNull(provider.authenticate(context.getAuthentication()));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue