diff --git a/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WASSecurityHelper.java b/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WASSecurityHelper.java deleted file mode 100755 index f2b8f1a61c..0000000000 --- a/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WASSecurityHelper.java +++ /dev/null @@ -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. - *

- * 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); - } - } - -} diff --git a/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WASUsernameAndGroupsExtractor.java b/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WASUsernameAndGroupsExtractor.java new file mode 100644 index 0000000000..14677db22b --- /dev/null +++ b/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WASUsernameAndGroupsExtractor.java @@ -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. + *

+ * Only for internal use. + * + * @author Luke Taylor + * @version $Id$ + * @since 3.0.0 + */ +interface WASUsernameAndGroupsExtractor { + + List getGroupsForCurrentUser(); + + String getCurrentUserName(); +} diff --git a/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WebSphere2SpringSecurityPropagationInterceptor.java b/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WebSphere2SpringSecurityPropagationInterceptor.java index 39fc532df2..5ffe1b4733 100755 --- a/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WebSphere2SpringSecurityPropagationInterceptor.java +++ b/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WebSphere2SpringSecurityPropagationInterceptor.java @@ -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. */ diff --git a/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WebSpherePreAuthenticatedAuthenticationDetailsSource.java b/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WebSpherePreAuthenticatedAuthenticationDetailsSource.java index 59b110511f..56bc633695 100755 --- a/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WebSpherePreAuthenticatedAuthenticationDetailsSource.java +++ b/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WebSpherePreAuthenticatedAuthenticationDetailsSource.java @@ -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 getWebSphereGroupsBasedGrantedAuthorities() { - List webSphereGroups = Arrays.asList(WASSecurityHelper.getGroupsForCurrentUser()); + List webSphereGroups = wasHelper.getGroupsForCurrentUser(); List userGas = webSphereGroups2GrantedAuthoritiesMapper.getGrantedAuthorities(webSphereGroups); if (logger.isDebugEnabled()) { logger.debug("WebSphere groups: " + webSphereGroups + " mapped to Granted Authorities: " + userGas); diff --git a/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WebSpherePreAuthenticatedProcessingFilter.java b/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WebSpherePreAuthenticatedProcessingFilter.java index db6d94ade2..8dde713bd9 100755 --- a/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WebSpherePreAuthenticatedProcessingFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WebSpherePreAuthenticatedProcessingFilter.java @@ -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); } diff --git a/web/src/test/java/org/springframework/security/web/authentication/preauth/websphere/WebSphere2SpringSecurityPropagationInterceptorTests.java b/web/src/test/java/org/springframework/security/web/authentication/preauth/websphere/WebSphere2SpringSecurityPropagationInterceptorTests.java new file mode 100644 index 0000000000..5cdf1adec0 --- /dev/null +++ b/web/src/test/java/org/springframework/security/web/authentication/preauth/websphere/WebSphere2SpringSecurityPropagationInterceptorTests.java @@ -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())); + } + +}