浏览代码

SEC-1078: Converted WASSecurityHelper to an internal interface and added test for scenario from this issue.

Luke Taylor 16 年之前
父节点
当前提交
6bd7421a1b

+ 0 - 206
web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WASSecurityHelper.java

@@ -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);
-        }
-    }
-
-}

+ 20 - 0
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.
+ * <p>
+ * Only for internal use.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 3.0.0
+ */
+interface WASUsernameAndGroupsExtractor {
+
+    List<String> getGroupsForCurrentUser();
+
+    String getCurrentUserName();
+}

+ 25 - 37
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.
      */

+ 9 - 3
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<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);

+ 17 - 2
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);
         }

+ 65 - 0
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()));
+    }
+
+}