2
0
Эх сурвалжийг харах

SEC-1131: Applied patch for portlet upgrade

Luke Taylor 16 жил өмнө
parent
commit
7c4d54f356

+ 4 - 4
core/src/main/java/org/springframework/security/authoritymapping/SimpleMappableAttributesRetriever.java

@@ -1,6 +1,5 @@
 package org.springframework.security.authoritymapping;
 
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
@@ -25,9 +24,10 @@ public class SimpleMappableAttributesRetriever implements MappableAttributesRetr
         return mappableAttributes;
     }
 
-    public void setMappableAttributes(String[] aMappableRoles) {
-        mappableAttributes = new HashSet<String>(aMappableRoles.length);
-        mappableAttributes.addAll(Arrays.asList(aMappableRoles));
+    @SuppressWarnings("unchecked")
+    public void setMappableAttributes(Set aMappableRoles) {
+        mappableAttributes = new HashSet<String>();
+        mappableAttributes.addAll(aMappableRoles);
         mappableAttributes = Collections.unmodifiableSet(mappableAttributes);
     }
 

+ 27 - 26
core/src/test/java/org/springframework/security/authoritymapping/SimpleMappableRolesRetrieverTests.java

@@ -1,26 +1,27 @@
-package org.springframework.security.authoritymapping;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Set;
-
-import junit.framework.TestCase;
-
-/**
- *
- * @author TSARDD
- * @since 18-okt-2007
- */
-public class SimpleMappableRolesRetrieverTests extends TestCase {
-
-    public final void testGetSetMappableRoles() {
-        String[] roles = new String[] { "Role1", "Role2" };
-        SimpleMappableAttributesRetriever r = new SimpleMappableAttributesRetriever();
-        r.setMappableAttributes(roles);
-        Set<String> result = r.getMappableAttributes();
-        Collection<String> rolesColl = Arrays.asList(roles);
-        assertTrue("Role collections do not match; result: " + result + ", expected: " + rolesColl, rolesColl.containsAll(result)
-                && result.containsAll(rolesColl));
-    }
-
-}
+package org.springframework.security.authoritymapping;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
+import java.util.HashSet;
+
+import junit.framework.TestCase;
+import org.springframework.util.StringUtils;
+
+/**
+ *
+ * @author TSARDD
+ * @since 18-okt-2007
+ */
+public class SimpleMappableRolesRetrieverTests extends TestCase {
+
+    public final void testGetSetMappableRoles() {
+        Set<String> roles = StringUtils.commaDelimitedListToSet("Role1,Role2");
+        SimpleMappableAttributesRetriever r = new SimpleMappableAttributesRetriever();
+        r.setMappableAttributes(roles);
+        Set<String> result = r.getMappableAttributes();
+        assertTrue("Role collections do not match; result: " + result + ", expected: " + roles, roles.containsAll(result)
+                && result.containsAll(roles));
+    }
+
+}

+ 1 - 1
pom.xml

@@ -19,7 +19,7 @@
         <module>samples</module>
         <module>taglibs</module>
         <module>itest</module>
-        <!-- module>portlet</module -->
+        <module>portlet</module>
   </modules>
 
     <description>Spring Security</description>

+ 6 - 6
portlet/pom.xml

@@ -12,9 +12,9 @@
     <dependencies>
         <dependency>
             <groupId>org.springframework.security</groupId>
-            <artifactId>spring-security-core</artifactId>
+            <artifactId>spring-security-web</artifactId>
             <version>${project.version}</version>
-        </dependency>                
+        </dependency>
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>servlet-api</artifactId>
@@ -22,13 +22,13 @@
         <dependency>
           <groupId>javax.portlet</groupId>
           <artifactId>portlet-api</artifactId>
-          <version>1.0</version>
+          <version>2.0</version>
           <scope>provided</scope>
         </dependency>
         <dependency>
           <groupId>org.springframework</groupId>
-          <artifactId>spring-portlet</artifactId>
-          <version>2.0.8</version>
+          <artifactId>org.springframework.web.portlet</artifactId>
+          <version>${spring.version}</version>
         </dependency>
         <dependency>
           <groupId>org.springframework</groupId>
@@ -42,4 +42,4 @@
         </dependency>
     </dependencies>
 
-</project>
+</project>

+ 468 - 437
portlet/src/main/java/org/springframework/security/context/PortletSessionContextIntegrationInterceptor.java

@@ -1,437 +1,468 @@
-/*
- * Copyright 2005-2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.security.context;
-
-import java.lang.reflect.Method;
-
-import javax.portlet.ActionRequest;
-import javax.portlet.ActionResponse;
-import javax.portlet.PortletException;
-import javax.portlet.PortletRequest;
-import javax.portlet.PortletResponse;
-import javax.portlet.PortletSession;
-import javax.portlet.RenderRequest;
-import javax.portlet.RenderResponse;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.util.Assert;
-import org.springframework.util.ReflectionUtils;
-import org.springframework.web.portlet.HandlerInterceptor;
-import org.springframework.web.portlet.ModelAndView;
-
-/**
- * <p>This interceptor populates the {@link SecurityContextHolder} with information obtained from the
- * <code>PortletSession</code>.  It is applied to both <code>ActionRequest</code>s and
- * <code>RenderRequest</code>s</p>
- *
- * <p>The <code>PortletSession</code> will be queried to retrieve the <code>SecurityContext</code> that should
- * be stored against the <code>SecurityContextHolder</code> for the duration of the portlet request. At the
- * end of the request, any updates made to the <code>SecurityContextHolder</code> will be persisted back to the
- * <code>PortletSession</code> by this interceptor.</p>
- *
- * <p> If a valid <code>SecurityContext</code> cannot be obtained from the <code>PortletSession</code> for
- * whatever reason, a fresh <code>SecurityContext</code> will be created and used instead. The created object
- * will be of the instance defined by the {@link #setContext(Class)} method (which defaults to
- * {@link org.springframework.security.context.SecurityContextImpl}. </p>
- *
- * <p>A <code>PortletSession</code> may be created by this interceptor if one does not already exist.  If at the
- * end of the portlet request the <code>PortletSession</code> does not exist, one will <b>only</b> be created if
- * the current contents of the <code>SecurityContextHolder</code> are not the {@link java.lang.Object#equals}
- * to a <code>new</code> instance of {@link #context}.  This avoids needless <code>PortletSession</code> creation,
- * and automates the storage of changes made to the <code>SecurityContextHolder</code>. There is one exception to
- * this rule, that is if the {@link #forceEagerSessionCreation} property is <code>true</code>, in which case
- * sessions will always be created irrespective of normal session-minimization logic (the default is
- * <code>false</code>, as this is resource intensive and not recommended).</p>
- *
- * <p>If for whatever reason no <code>PortletSession</code> should <b>ever</b> be created, the
- * {@link #allowSessionCreation} property should be set to <code>false</code>. Only do this if you really need
- * to conserve server memory and ensure all classes using the <code>SecurityContextHolder</code> are designed to
- * have no persistence of the <code>SecurityContext</code> between web requests. Please note that if
- * {@link #forceEagerSessionCreation} is <code>true</code>, the <code>allowSessionCreation</code> must also be
- * <code>true</code> (setting it to <code>false</code> will cause a startup-time error).</p>
-
- * <p>This interceptor <b>must</b> be executed <b>before</p> any authentication processing mechanisms. These
- * mechanisms (specifically {@link org.springframework.security.ui.portlet.PortletProcessingInterceptor}) expect the
- * <code>SecurityContextHolder</code> to contain a valid <code>SecurityContext</code> by the time they execute.</p>
- *
- * <p>An important nuance to this interceptor is that (by default) the <code>SecurityContext</code> is stored
- * into the <code>APPLICATION_SCOPE</code> of the <code>PortletSession</code>.  This doesn't just mean you will be
- * sharing it with all the other portlets in your webapp (which is generally a good idea).  It also means that (if
- * you have done all the other appropriate magic), you will share this <code>SecurityContext</code> with servlets in
- * your webapp.  This is very useful if you have servlets serving images or processing AJAX calls from your portlets
- * since they can now use the {@link HttpSessionContextIntegrationFilter} to access the same <code>SecurityContext<code>
- * object from the session.  This allows these calls to be secured as well as the portlet calls.</p>
- *
- * Much of the logic of this interceptor comes from the {@link HttpSessionContextIntegrationFilter} class which
- * fills the same purpose on the servlet side.  Ben Alex and Patrick Burlson are listed as authors here because they
- * are the authors of that class and there are blocks of code that essentially identical between the two. (Making this
- * a good candidate for refactoring someday.)
- *
- * <p>Unlike <code>HttpSessionContextIntegrationFilter</code>, this interceptor does not check to see if it is
- * getting applied multiple times.  This shouldn't be a problem since the application of interceptors is under the
- * control of the Spring Portlet MVC framework and tends to be more explicit and more predictable than the application
- * of filters.  However, you should still be careful to only apply this inteceptor to your request once.</p>
- *
- * @author John A. Lewis
- * @author Ben Alex
- * @author Patrick Burleson
- * @since 2.0
- * @version $Id$
- */
-public class PortletSessionContextIntegrationInterceptor
-        implements InitializingBean, HandlerInterceptor {
-
-    //~ Static fields/initializers =====================================================================================
-
-    protected static final Log logger = LogFactory.getLog(PortletSessionContextIntegrationInterceptor.class);
-
-    public static final String SPRING_SECURITY_CONTEXT_KEY = HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY;
-
-    private static final String SESSION_EXISTED = PortletSessionContextIntegrationInterceptor.class.getName() + ".SESSION_EXISTED";
-    private static final String CONTEXT_HASHCODE = PortletSessionContextIntegrationInterceptor.class.getName() + ".CONTEXT_HASHCODE";
-
-    //~ Instance fields ================================================================================================
-
-    private Class context = SecurityContextImpl.class;
-
-    private Object contextObject;
-
-    /**
-     * Indicates if this interceptor can create a <code>PortletSession</code> if
-     * needed (sessions are always created sparingly, but setting this value to
-     * <code>false</code> will prohibit sessions from ever being created).
-     * Defaults to <code>true</code>. Do not set to <code>false</code> if
-     * you are have set {@link #forceEagerSessionCreation} to <code>true</code>,
-     * as the properties would be in conflict.
-     */
-    private boolean allowSessionCreation = true;
-
-    /**
-     * Indicates if this interceptor is required to create a <code>PortletSession</code>
-     * for every request before proceeding through the request process, even if the
-     * <code>PortletSession</code> would not ordinarily have been created. By
-     * default this is <code>false</code>, which is entirely appropriate for
-     * most circumstances as you do not want a <code>PortletSession</code>
-     * created unless the interceptor actually needs one. It is envisaged the main
-     * situation in which this property would be set to <code>true</code> is
-     * if using other interceptors that depend on a <code>PortletSession</code>
-     * already existing. This is only required in specialized cases, so leave it set to
-     * <code>false</code> unless you have an actual requirement and aware of the
-     * session creation overhead.
-     */
-    private boolean forceEagerSessionCreation = false;
-
-    /**
-     * Indicates whether the <code>SecurityContext</code> will be cloned from
-     * the <code>PortletSession</code>. The default is to simply reference
-     * (the default is <code>false</code>). The default may cause issues if
-     * concurrent threads need to have a different security identity from other
-     * threads being concurrently processed that share the same
-     * <code>PortletSession</code>. In most normal environments this does not
-     * represent an issue, as changes to the security identity in one thread is
-     * allowed to affect the security identity in other threads associated with
-     * the same <code>PortletSession</code>. For unusual cases where this is not
-     * permitted, change this value to <code>true</code> and ensure the
-     * {@link #context} is set to a <code>SecurityContext</code> that
-     * implements {@link Cloneable} and overrides the <code>clone()</code>
-     * method.
-     */
-    private boolean cloneFromPortletSession = false;
-
-    /**
-     * Indicates wether the <code>APPLICATION_SCOPE</code> mode of the
-     * <code>PortletSession</code> should be used for storing the
-     * <code>SecurityContext</code>.  The default is </code>true</code>.
-     * This allows it to be shared between the portlets in the webapp and
-     * potentially with servlets in the webapp as well. If this is set to
-     * <code>false</code>, then the <code>PORTLET_SCOPE</code> will be used
-     * instead.
-     */
-    private boolean useApplicationScopePortletSession = true;
-
-
-    //~ Constructors ===================================================================================================
-
-    public PortletSessionContextIntegrationInterceptor() throws PortletException {
-        this.contextObject = generateNewContext();
-    }
-
-    //~ Methods ========================================================================================================
-
-    public void afterPropertiesSet() throws Exception {
-
-        // check that the value of context is legal
-        if ((this.context == null) || (!SecurityContext.class.isAssignableFrom(this.context))) {
-            throw new IllegalArgumentException("context must be defined and implement SecurityContext "
-                    + "(typically use org.springframework.security.context.SecurityContextImpl; existing class is "
-                    + this.context + ")");
-        }
-
-        // check that session creation options make sense
-        if ((forceEagerSessionCreation == true) && (allowSessionCreation == false)) {
-            throw new IllegalArgumentException(
-                    "If using forceEagerSessionCreation, you must set allowSessionCreation to also be true");
-        }
-    }
-
-    public boolean preHandleAction(ActionRequest request, ActionResponse response,
-            Object handler) throws Exception {
-        // call to common preHandle method
-        return preHandle(request, response, handler);
-    }
-
-    public boolean preHandleRender(RenderRequest request, RenderResponse response,
-            Object handler) throws Exception {
-        // call to common preHandle method
-        return preHandle(request, response, handler);
-    }
-
-    public void postHandleRender(RenderRequest request, RenderResponse response,
-            Object handler, ModelAndView modelAndView) throws Exception {
-        // no-op
-    }
-
-    public void afterActionCompletion(ActionRequest request, ActionResponse response,
-            Object handler, Exception ex) throws Exception {
-        // call to common afterCompletion method
-        afterCompletion(request, response, handler, ex);
-    }
-
-    public void afterRenderCompletion(RenderRequest request, RenderResponse response,
-            Object handler, Exception ex) throws Exception {
-        // call to common afterCompletion method
-        afterCompletion(request, response, handler, ex);
-    }
-
-
-    private boolean preHandle(PortletRequest request, PortletResponse response,
-            Object handler) throws Exception {
-
-        PortletSession portletSession = null;
-        boolean portletSessionExistedAtStartOfRequest = false;
-
-        // see if the portlet session already exists (or should be eagerly created)
-        try {
-            portletSession = request.getPortletSession(forceEagerSessionCreation);
-        } catch (IllegalStateException ignored) {}
-
-        // if there is a session, then see if there is a context to bring in
-        if (portletSession != null) {
-
-            // remember that the session already existed
-            portletSessionExistedAtStartOfRequest = true;
-
-            // attempt to retrieve the context from the session
-            Object contextFromSessionObject = portletSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY, portletSessionScope());
-
-            // if we got a context then place it into the holder
-            if (contextFromSessionObject != null) {
-
-                // if we are supposed to clone it, then do so
-                if (cloneFromPortletSession) {
-                    Assert.isInstanceOf(Cloneable.class, contextFromSessionObject,
-                            "Context must implement Clonable and provide a Object.clone() method");
-                    try {
-                        Method m = contextFromSessionObject.getClass().getMethod("clone", new Class[] {});
-                        if (!m.isAccessible()) {
-                            m.setAccessible(true);
-                        }
-                        contextFromSessionObject = m.invoke(contextFromSessionObject, new Object[] {});
-                    }
-                    catch (Exception ex) {
-                        ReflectionUtils.handleReflectionException(ex);
-                    }
-                }
-
-                // if what we got is a valid context then place it into the holder, otherwise create a new one
-                if (contextFromSessionObject instanceof SecurityContext) {
-                    if (logger.isDebugEnabled())
-                        logger.debug("Obtained from SPRING_SECURITY_CONTEXT a valid SecurityContext and "
-                                + "set to SecurityContextHolder: '" + contextFromSessionObject + "'");
-                    SecurityContextHolder.setContext((SecurityContext) contextFromSessionObject);
-                } else {
-                    if (logger.isWarnEnabled())
-                        logger.warn("SPRING_SECURITY_CONTEXT did not contain a SecurityContext but contained: '"
-                                        + contextFromSessionObject
-                                        + "'; are you improperly modifying the PortletSession directly "
-                                        + "(you should always use SecurityContextHolder) or using the PortletSession attribute "
-                                        + "reserved for this class? - new SecurityContext instance associated with "
-                                        + "SecurityContextHolder");
-                    SecurityContextHolder.setContext(generateNewContext());
-                }
-
-            } else {
-
-                // there was no context in the session, so create a new context and put it in the holder
-                if (logger.isDebugEnabled())
-                    logger.debug("PortletSession returned null object for SPRING_SECURITY_CONTEXT - new "
-                            + "SecurityContext instance associated with SecurityContextHolder");
-                SecurityContextHolder.setContext(generateNewContext());
-            }
-
-        } else {
-
-            // there was no session, so create a new context and place it in the holder
-            if (logger.isDebugEnabled())
-                logger.debug("No PortletSession currently exists - new SecurityContext instance "
-                        + "associated with SecurityContextHolder");
-            SecurityContextHolder.setContext(generateNewContext());
-
-        }
-
-        // place attributes onto the request to remember if the session existed and the hashcode of the context
-        request.setAttribute(SESSION_EXISTED, new Boolean(portletSessionExistedAtStartOfRequest));
-        request.setAttribute(CONTEXT_HASHCODE, new Integer(SecurityContextHolder.getContext().hashCode()));
-
-        return true;
-    }
-
-    private void afterCompletion(PortletRequest request, PortletResponse response,
-            Object handler, Exception ex) throws Exception {
-
-        PortletSession portletSession = null;
-
-        // retrieve the attributes that remember if the session existed and the hashcode of the context
-        boolean portletSessionExistedAtStartOfRequest = ((Boolean)request.getAttribute(SESSION_EXISTED)).booleanValue();
-        int oldContextHashCode = ((Integer)request.getAttribute(CONTEXT_HASHCODE)).intValue();
-
-        // try to retrieve an existing portlet session
-        try {
-            portletSession = request.getPortletSession(false);
-        } catch (IllegalStateException ignored) {}
-
-        // if there is now no session but there was one at the beginning then it must have been invalidated
-        if ((portletSession == null) && portletSessionExistedAtStartOfRequest) {
-            if (logger.isDebugEnabled())
-                logger.debug("PortletSession is now null, but was not null at start of request; "
-                        + "session was invalidated, so do not create a new session");
-        }
-
-        // create a new portlet session if we need to
-        if ((portletSession == null) && !portletSessionExistedAtStartOfRequest) {
-
-            // if we're not allowed to create a new session, then report that
-            if (!allowSessionCreation) {
-                if (logger.isDebugEnabled())
-                    logger.debug("The PortletSession is currently null, and the "
-                            + "PortletSessionContextIntegrationInterceptor is prohibited from creating a PortletSession "
-                            + "(because the allowSessionCreation property is false) - SecurityContext thus not "
-                            + "stored for next request");
-            }
-            // if the context was changed during the request, then go ahead and create a session
-            else if (!contextObject.equals(SecurityContextHolder.getContext())) {
-                if (logger.isDebugEnabled())
-                    logger.debug("PortletSession being created as SecurityContextHolder contents are non-default");
-                try {
-                    portletSession = request.getPortletSession(true);
-                } catch (IllegalStateException ignored) {}
-            }
-            // if nothing in the context changed, then don't bother to create a session
-            else {
-                if (logger.isDebugEnabled())
-                    logger.debug("PortletSession is null, but SecurityContextHolder has not changed from default: ' "
-                            + SecurityContextHolder.getContext()
-                            + "'; not creating PortletSession or storing SecurityContextHolder contents");
-            }
-        }
-
-        // if the session exists and the context has changes, then store the context back into the session
-        if ((portletSession != null)
-            && (SecurityContextHolder.getContext().hashCode() != oldContextHashCode)) {
-            portletSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY,    SecurityContextHolder.getContext(), portletSessionScope());
-            if (logger.isDebugEnabled())
-                logger.debug("SecurityContext stored to PortletSession: '"
-                    + SecurityContextHolder.getContext() + "'");
-        }
-
-        // remove the contents of the holder
-        SecurityContextHolder.clearContext();
-        if (logger.isDebugEnabled())
-            logger.debug("SecurityContextHolder set to new context, as request processing completed");
-
-    }
-
-    /**
-     * Creates a new <code>SecurityContext</code> object.  The specific class is
-     * determined by the setting of the {@link #context} property.
-     * @return the new <code>SecurityContext</code>
-     * @throws PortletException if the creation throws an <code>InstantiationException</code> or
-     *     an <code>IllegalAccessException</code>, then this method will wrap them in a
-     *     <code>PortletException</code>
-     */
-    public SecurityContext generateNewContext() throws PortletException {
-        try {
-            return (SecurityContext) this.context.newInstance();
-        } catch (InstantiationException ie) {
-            throw new PortletException(ie);
-        } catch (IllegalAccessException iae) {
-            throw new PortletException(iae);
-        }
-    }
-
-
-    private int portletSessionScope() {
-        // return the appropriate scope setting based on our property value
-        return (this.useApplicationScopePortletSession ?
-            PortletSession.APPLICATION_SCOPE :    PortletSession.PORTLET_SCOPE);
-    }
-
-
-    public Class getContext() {
-        return context;
-    }
-
-    public void setContext(Class secureContext) {
-        this.context = secureContext;
-    }
-
-    public boolean isAllowSessionCreation() {
-        return allowSessionCreation;
-    }
-
-    public void setAllowSessionCreation(boolean allowSessionCreation) {
-        this.allowSessionCreation = allowSessionCreation;
-    }
-
-    public boolean isForceEagerSessionCreation() {
-        return forceEagerSessionCreation;
-    }
-
-    public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {
-        this.forceEagerSessionCreation = forceEagerSessionCreation;
-    }
-
-    public boolean isCloneFromPortletSession() {
-        return cloneFromPortletSession;
-    }
-
-    public void setCloneFromPortletSession(boolean cloneFromPortletSession) {
-        this.cloneFromPortletSession = cloneFromPortletSession;
-    }
-
-    public boolean isUseApplicationScopePortletSession() {
-        return useApplicationScopePortletSession;
-    }
-
-    public void setUseApplicationScopePortletSession(
-            boolean useApplicationScopePortletSession) {
-        this.useApplicationScopePortletSession = useApplicationScopePortletSession;
-    }
-
-}
+/*
+ * Copyright 2005-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.context;
+
+import java.lang.reflect.Method;
+
+import javax.portlet.*;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.web.portlet.HandlerInterceptor;
+import org.springframework.web.portlet.ModelAndView;
+import org.springframework.security.web.context.SecurityContextPersistenceFilter;
+import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
+
+/**
+ * <p>This interceptor populates the {@link SecurityContextHolder} with information obtained from the
+ * <code>PortletSession</code>.  It is applied to both <code>ActionRequest</code>s and
+ * <code>RenderRequest</code>s</p>
+ *
+ * <p>The <code>PortletSession</code> will be queried to retrieve the <code>SecurityContext</code> that should
+ * be stored against the <code>SecurityContextHolder</code> for the duration of the portlet request. At the
+ * end of the request, any updates made to the <code>SecurityContextHolder</code> will be persisted back to the
+ * <code>PortletSession</code> by this interceptor.</p>
+ *
+ * <p> If a valid <code>SecurityContext</code> cannot be obtained from the <code>PortletSession</code> for
+ * whatever reason, a fresh <code>SecurityContext</code> will be created and used instead. The created object
+ * will be of the instance defined by the {@link #setContext(Class)} method (which defaults to
+ * {@link org.springframework.security.context.SecurityContextImpl}. </p>
+ *
+ * <p>A <code>PortletSession</code> may be created by this interceptor if one does not already exist.  If at the
+ * end of the portlet request the <code>PortletSession</code> does not exist, one will <b>only</b> be created if
+ * the current contents of the <code>SecurityContextHolder</code> are not the {@link java.lang.Object#equals}
+ * to a <code>new</code> instance of {@link #context}.  This avoids needless <code>PortletSession</code> creation,
+ * and automates the storage of changes made to the <code>SecurityContextHolder</code>. There is one exception to
+ * this rule, that is if the {@link #forceEagerSessionCreation} property is <code>true</code>, in which case
+ * sessions will always be created irrespective of normal session-minimization logic (the default is
+ * <code>false</code>, as this is resource intensive and not recommended).</p>
+ *
+ * <p>If for whatever reason no <code>PortletSession</code> should <b>ever</b> be created, the
+ * {@link #allowSessionCreation} property should be set to <code>false</code>. Only do this if you really need
+ * to conserve server memory and ensure all classes using the <code>SecurityContextHolder</code> are designed to
+ * have no persistence of the <code>SecurityContext</code> between web requests. Please note that if
+ * {@link #forceEagerSessionCreation} is <code>true</code>, the <code>allowSessionCreation</code> must also be
+ * <code>true</code> (setting it to <code>false</code> will cause a startup-time error).</p>
+
+ * <p>This interceptor <b>must</b> be executed <b>before</p> any authentication processing mechanisms. These
+ * mechanisms (specifically {@link org.springframework.security.ui.portlet.PortletProcessingInterceptor}) expect the
+ * <code>SecurityContextHolder</code> to contain a valid <code>SecurityContext</code> by the time they execute.</p>
+ *
+ * <p>An important nuance to this interceptor is that (by default) the <code>SecurityContext</code> is stored
+ * into the <code>APPLICATION_SCOPE</code> of the <code>PortletSession</code>.  This doesn't just mean you will be
+ * sharing it with all the other portlets in your webapp (which is generally a good idea).  It also means that (if
+ * you have done all the other appropriate magic), you will share this <code>SecurityContext</code> with servlets in
+ * your webapp.  This is very useful if you have servlets serving images or processing AJAX calls from your portlets
+ * since they can now use the {@link SecurityContextPersistenceFilter} to access the same <code>SecurityContext<code>
+ * object from the session.  This allows these calls to be secured as well as the portlet calls.</p>
+ *
+ * Much of the logic of this interceptor comes from the {@link SecurityContextPersistenceFilter} class which
+ * fills the same purpose on the servlet side.  Ben Alex and Patrick Burlson are listed as authors here because they
+ * are the authors of that class and there are blocks of code that essentially identical between the two. (Making this
+ * a good candidate for refactoring someday.)
+ *
+ * <p>Unlike <code>HttpSessionContextIntegrationFilter</code>, this interceptor does not check to see if it is
+ * getting applied multiple times.  This shouldn't be a problem since the application of interceptors is under the
+ * control of the Spring Portlet MVC framework and tends to be more explicit and more predictable than the application
+ * of filters.  However, you should still be careful to only apply this inteceptor to your request once.</p>
+ *
+ * @author John A. Lewis
+ * @author Ben Alex
+ * @author Patrick Burleson
+ * @since 2.0
+ * @version $Id$
+ */
+public class PortletSessionContextIntegrationInterceptor
+        implements InitializingBean, HandlerInterceptor {
+
+    //~ Static fields/initializers =====================================================================================
+
+    protected static final Log logger = LogFactory.getLog(PortletSessionContextIntegrationInterceptor.class);
+
+    public static final String SPRING_SECURITY_CONTEXT_KEY = HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
+
+    private static final String SESSION_EXISTED = PortletSessionContextIntegrationInterceptor.class.getName() + ".SESSION_EXISTED";
+    private static final String CONTEXT_HASHCODE = PortletSessionContextIntegrationInterceptor.class.getName() + ".CONTEXT_HASHCODE";
+
+    //~ Instance fields ================================================================================================
+
+    private Class context = SecurityContextImpl.class;
+
+    private Object contextObject;
+
+    /**
+     * Indicates if this interceptor can create a <code>PortletSession</code> if
+     * needed (sessions are always created sparingly, but setting this value to
+     * <code>false</code> will prohibit sessions from ever being created).
+     * Defaults to <code>true</code>. Do not set to <code>false</code> if
+     * you are have set {@link #forceEagerSessionCreation} to <code>true</code>,
+     * as the properties would be in conflict.
+     */
+    private boolean allowSessionCreation = true;
+
+    /**
+     * Indicates if this interceptor is required to create a <code>PortletSession</code>
+     * for every request before proceeding through the request process, even if the
+     * <code>PortletSession</code> would not ordinarily have been created. By
+     * default this is <code>false</code>, which is entirely appropriate for
+     * most circumstances as you do not want a <code>PortletSession</code>
+     * created unless the interceptor actually needs one. It is envisaged the main
+     * situation in which this property would be set to <code>true</code> is
+     * if using other interceptors that depend on a <code>PortletSession</code>
+     * already existing. This is only required in specialized cases, so leave it set to
+     * <code>false</code> unless you have an actual requirement and aware of the
+     * session creation overhead.
+     */
+    private boolean forceEagerSessionCreation = false;
+
+    /**
+     * Indicates whether the <code>SecurityContext</code> will be cloned from
+     * the <code>PortletSession</code>. The default is to simply reference
+     * (the default is <code>false</code>). The default may cause issues if
+     * concurrent threads need to have a different security identity from other
+     * threads being concurrently processed that share the same
+     * <code>PortletSession</code>. In most normal environments this does not
+     * represent an issue, as changes to the security identity in one thread is
+     * allowed to affect the security identity in other threads associated with
+     * the same <code>PortletSession</code>. For unusual cases where this is not
+     * permitted, change this value to <code>true</code> and ensure the
+     * {@link #context} is set to a <code>SecurityContext</code> that
+     * implements {@link Cloneable} and overrides the <code>clone()</code>
+     * method.
+     */
+    private boolean cloneFromPortletSession = false;
+
+    /**
+     * Indicates wether the <code>APPLICATION_SCOPE</code> mode of the
+     * <code>PortletSession</code> should be used for storing the
+     * <code>SecurityContext</code>.  The default is </code>true</code>.
+     * This allows it to be shared between the portlets in the webapp and
+     * potentially with servlets in the webapp as well. If this is set to
+     * <code>false</code>, then the <code>PORTLET_SCOPE</code> will be used
+     * instead.
+     */
+    private boolean useApplicationScopePortletSession = true;
+
+
+    //~ Constructors ===================================================================================================
+
+    public PortletSessionContextIntegrationInterceptor() throws PortletException {
+        this.contextObject = generateNewContext();
+    }
+
+    //~ Methods ========================================================================================================
+
+    public void afterPropertiesSet() throws Exception {
+
+        // check that the value of context is legal
+        if ((this.context == null) || (!SecurityContext.class.isAssignableFrom(this.context))) {
+            throw new IllegalArgumentException("context must be defined and implement SecurityContext "
+                    + "(typically use org.springframework.security.context.SecurityContextImpl; existing class is "
+                    + this.context + ")");
+        }
+
+        // check that session creation options make sense
+        if ((forceEagerSessionCreation == true) && (allowSessionCreation == false)) {
+            throw new IllegalArgumentException(
+                    "If using forceEagerSessionCreation, you must set allowSessionCreation to also be true");
+        }
+    }
+
+    public boolean preHandleAction(ActionRequest request, ActionResponse response,
+            Object handler) throws Exception {
+        // call to common preHandle method
+        return preHandle(request, response, handler);
+    }
+
+    public boolean preHandleRender(RenderRequest request, RenderResponse response,
+            Object handler) throws Exception {
+        // call to common preHandle method
+        return preHandle(request, response, handler);
+    }
+
+    public void postHandleRender(RenderRequest request, RenderResponse response,
+            Object handler, ModelAndView modelAndView) throws Exception {
+        // no-op
+    }
+
+    public void afterActionCompletion(ActionRequest request, ActionResponse response,
+            Object handler, Exception ex) throws Exception {
+        // call to common afterCompletion method
+        afterCompletion(request, response, handler, ex);
+    }
+
+    public void afterRenderCompletion(RenderRequest request, RenderResponse response,
+            Object handler, Exception ex) throws Exception {
+        // call to common afterCompletion method
+        afterCompletion(request, response, handler, ex);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean preHandleResource(ResourceRequest request, ResourceResponse response, Object handler) throws Exception {
+        return preHandle(request, response, handler);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void postHandleResource(ResourceRequest request, ResourceResponse response, Object handler, ModelAndView modelAndView) throws Exception {
+        // no-op
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void afterResourceCompletion(ResourceRequest request, ResourceResponse response, Object handler, Exception ex) throws Exception {
+        // call to common afterCompletion method
+        afterCompletion(request, response, handler, ex);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean preHandleEvent(EventRequest request, EventResponse response, Object handler) throws Exception {
+        return preHandle(request, response, handler);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void afterEventCompletion(EventRequest request, EventResponse response, Object handler, Exception ex) throws Exception {
+        // call to common afterCompletion method
+        afterCompletion(request, response, handler, ex);
+    }
+
+    private boolean preHandle(PortletRequest request, PortletResponse response,
+            Object handler) throws Exception {
+
+        PortletSession portletSession = null;
+        boolean portletSessionExistedAtStartOfRequest = false;
+
+        // see if the portlet session already exists (or should be eagerly created)
+        try {
+            portletSession = request.getPortletSession(forceEagerSessionCreation);
+        } catch (IllegalStateException ignored) {}
+
+        // if there is a session, then see if there is a context to bring in
+        if (portletSession != null) {
+
+            // remember that the session already existed
+            portletSessionExistedAtStartOfRequest = true;
+
+            // attempt to retrieve the context from the session
+            Object contextFromSessionObject = portletSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY, portletSessionScope());
+
+            // if we got a context then place it into the holder
+            if (contextFromSessionObject != null) {
+
+                // if we are supposed to clone it, then do so
+                if (cloneFromPortletSession) {
+                    Assert.isInstanceOf(Cloneable.class, contextFromSessionObject,
+                            "Context must implement Clonable and provide a Object.clone() method");
+                    try {
+                        Method m = contextFromSessionObject.getClass().getMethod("clone", new Class[] {});
+                        if (!m.isAccessible()) {
+                            m.setAccessible(true);
+                        }
+                        contextFromSessionObject = m.invoke(contextFromSessionObject, new Object[] {});
+                    }
+                    catch (Exception ex) {
+                        ReflectionUtils.handleReflectionException(ex);
+                    }
+                }
+
+                // if what we got is a valid context then place it into the holder, otherwise create a new one
+                if (contextFromSessionObject instanceof SecurityContext) {
+                    if (logger.isDebugEnabled())
+                        logger.debug("Obtained from SPRING_SECURITY_CONTEXT a valid SecurityContext and "
+                                + "set to SecurityContextHolder: '" + contextFromSessionObject + "'");
+                    SecurityContextHolder.setContext((SecurityContext) contextFromSessionObject);
+                } else {
+                    if (logger.isWarnEnabled())
+                        logger.warn("SPRING_SECURITY_CONTEXT did not contain a SecurityContext but contained: '"
+                                        + contextFromSessionObject
+                                        + "'; are you improperly modifying the PortletSession directly "
+                                        + "(you should always use SecurityContextHolder) or using the PortletSession attribute "
+                                        + "reserved for this class? - new SecurityContext instance associated with "
+                                        + "SecurityContextHolder");
+                    SecurityContextHolder.setContext(generateNewContext());
+                }
+
+            } else {
+
+                // there was no context in the session, so create a new context and put it in the holder
+                if (logger.isDebugEnabled())
+                    logger.debug("PortletSession returned null object for SPRING_SECURITY_CONTEXT - new "
+                            + "SecurityContext instance associated with SecurityContextHolder");
+                SecurityContextHolder.setContext(generateNewContext());
+            }
+
+        } else {
+
+            // there was no session, so create a new context and place it in the holder
+            if (logger.isDebugEnabled())
+                logger.debug("No PortletSession currently exists - new SecurityContext instance "
+                        + "associated with SecurityContextHolder");
+            SecurityContextHolder.setContext(generateNewContext());
+
+        }
+
+        // place attributes onto the request to remember if the session existed and the hashcode of the context
+        request.setAttribute(SESSION_EXISTED, new Boolean(portletSessionExistedAtStartOfRequest));
+        request.setAttribute(CONTEXT_HASHCODE, new Integer(SecurityContextHolder.getContext().hashCode()));
+
+        return true;
+    }
+
+    private void afterCompletion(PortletRequest request, PortletResponse response,
+            Object handler, Exception ex) throws Exception {
+
+        PortletSession portletSession = null;
+
+        // retrieve the attributes that remember if the session existed and the hashcode of the context
+        boolean portletSessionExistedAtStartOfRequest = ((Boolean)request.getAttribute(SESSION_EXISTED)).booleanValue();
+        int oldContextHashCode = ((Integer)request.getAttribute(CONTEXT_HASHCODE)).intValue();
+
+        // try to retrieve an existing portlet session
+        try {
+            portletSession = request.getPortletSession(false);
+        } catch (IllegalStateException ignored) {}
+
+        // if there is now no session but there was one at the beginning then it must have been invalidated
+        if ((portletSession == null) && portletSessionExistedAtStartOfRequest) {
+            if (logger.isDebugEnabled())
+                logger.debug("PortletSession is now null, but was not null at start of request; "
+                        + "session was invalidated, so do not create a new session");
+        }
+
+        // create a new portlet session if we need to
+        if ((portletSession == null) && !portletSessionExistedAtStartOfRequest) {
+
+            // if we're not allowed to create a new session, then report that
+            if (!allowSessionCreation) {
+                if (logger.isDebugEnabled())
+                    logger.debug("The PortletSession is currently null, and the "
+                            + "PortletSessionContextIntegrationInterceptor is prohibited from creating a PortletSession "
+                            + "(because the allowSessionCreation property is false) - SecurityContext thus not "
+                            + "stored for next request");
+            }
+            // if the context was changed during the request, then go ahead and create a session
+            else if (!contextObject.equals(SecurityContextHolder.getContext())) {
+                if (logger.isDebugEnabled())
+                    logger.debug("PortletSession being created as SecurityContextHolder contents are non-default");
+                try {
+                    portletSession = request.getPortletSession(true);
+                } catch (IllegalStateException ignored) {}
+            }
+            // if nothing in the context changed, then don't bother to create a session
+            else {
+                if (logger.isDebugEnabled())
+                    logger.debug("PortletSession is null, but SecurityContextHolder has not changed from default: ' "
+                            + SecurityContextHolder.getContext()
+                            + "'; not creating PortletSession or storing SecurityContextHolder contents");
+            }
+        }
+
+        // if the session exists and the context has changes, then store the context back into the session
+        if ((portletSession != null)
+            && (SecurityContextHolder.getContext().hashCode() != oldContextHashCode)) {
+            portletSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY,    SecurityContextHolder.getContext(), portletSessionScope());
+            if (logger.isDebugEnabled())
+                logger.debug("SecurityContext stored to PortletSession: '"
+                    + SecurityContextHolder.getContext() + "'");
+        }
+
+        // remove the contents of the holder
+        SecurityContextHolder.clearContext();
+        if (logger.isDebugEnabled())
+            logger.debug("SecurityContextHolder set to new context, as request processing completed");
+
+    }
+
+    /**
+     * Creates a new <code>SecurityContext</code> object.  The specific class is
+     * determined by the setting of the {@link #context} property.
+     * @return the new <code>SecurityContext</code>
+     * @throws PortletException if the creation throws an <code>InstantiationException</code> or
+     *     an <code>IllegalAccessException</code>, then this method will wrap them in a
+     *     <code>PortletException</code>
+     */
+    public SecurityContext generateNewContext() throws PortletException {
+        try {
+            return (SecurityContext) this.context.newInstance();
+        } catch (InstantiationException ie) {
+            throw new PortletException(ie);
+        } catch (IllegalAccessException iae) {
+            throw new PortletException(iae);
+        }
+    }
+
+
+    private int portletSessionScope() {
+        // return the appropriate scope setting based on our property value
+        return (this.useApplicationScopePortletSession ?
+            PortletSession.APPLICATION_SCOPE :    PortletSession.PORTLET_SCOPE);
+    }
+
+
+    public Class getContext() {
+        return context;
+    }
+
+    public void setContext(Class secureContext) {
+        this.context = secureContext;
+    }
+
+    public boolean isAllowSessionCreation() {
+        return allowSessionCreation;
+    }
+
+    public void setAllowSessionCreation(boolean allowSessionCreation) {
+        this.allowSessionCreation = allowSessionCreation;
+    }
+
+    public boolean isForceEagerSessionCreation() {
+        return forceEagerSessionCreation;
+    }
+
+    public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {
+        this.forceEagerSessionCreation = forceEagerSessionCreation;
+    }
+
+    public boolean isCloneFromPortletSession() {
+        return cloneFromPortletSession;
+    }
+
+    public void setCloneFromPortletSession(boolean cloneFromPortletSession) {
+        this.cloneFromPortletSession = cloneFromPortletSession;
+    }
+
+    public boolean isUseApplicationScopePortletSession() {
+        return useApplicationScopePortletSession;
+    }
+
+    public void setUseApplicationScopePortletSession(
+            boolean useApplicationScopePortletSession) {
+        this.useApplicationScopePortletSession = useApplicationScopePortletSession;
+    }
+
+}

+ 1 - 1
portlet/src/main/java/org/springframework/security/ui/portlet/PortletPreAuthenticatedAuthenticationDetailsSource.java

@@ -6,7 +6,7 @@ import java.util.Set;
 
 import javax.portlet.PortletRequest;
 
-import org.springframework.security.ui.preauth.j2ee.AbstractPreAuthenticatedAuthenticationDetailsSource;
+import org.springframework.security.web.authentication.preauth.j2ee.AbstractPreAuthenticatedAuthenticationDetailsSource;
 
 public class PortletPreAuthenticatedAuthenticationDetailsSource extends AbstractPreAuthenticatedAuthenticationDetailsSource {
 

+ 342 - 322
portlet/src/main/java/org/springframework/security/ui/portlet/PortletProcessingInterceptor.java

@@ -1,322 +1,342 @@
-/*
- * Copyright 2005-2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.security.ui.portlet;
-
-import java.io.IOException;
-import java.security.Principal;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import javax.portlet.ActionRequest;
-import javax.portlet.ActionResponse;
-import javax.portlet.PortletRequest;
-import javax.portlet.PortletResponse;
-import javax.portlet.PortletSession;
-import javax.portlet.RenderRequest;
-import javax.portlet.RenderResponse;
-
-import org.springframework.security.Authentication;
-import org.springframework.security.AuthenticationDetailsSource;
-import org.springframework.security.AuthenticationDetailsSourceImpl;
-import org.springframework.security.AuthenticationException;
-import org.springframework.security.AuthenticationManager;
-import org.springframework.security.context.SecurityContext;
-import org.springframework.security.context.SecurityContextHolder;
-import org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationToken;
-import org.springframework.security.ui.AbstractProcessingFilter;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.util.Assert;
-import org.springframework.web.portlet.HandlerInterceptor;
-import org.springframework.web.portlet.ModelAndView;
-
-/**
- * <p>This interceptor is responsible for processing portlet authentication requests.  This
- * is the portlet equivalent of the <code>AuthenticationProcessingFilter</code> used for
- * traditional servlet-based web applications. It is applied to both <code>ActionRequest</code>s
- * and <code>RenderRequest</code>s alike.  If authentication is successful, the resulting
- * {@link Authentication} object will be placed into the <code>SecurityContext</code>, which
- * is guaranteed to have already been created by an earlier interceptor.  If authentication
- * fails, the <code>AuthenticationException</code> will be placed into the
- * <code>APPLICATION_SCOPE</code> of the <code>PortletSession</code> with the attribute defined
- * by {@link AbstractProcessingFilter#SPRING_SECURITY_LAST_EXCEPTION_KEY}.</p>
- *
- *  <p>Some portals do not properly provide the identity of the current user via the
- * <code>getRemoteUser()</code> or <code>getUserPrincipal()</code> methods of the
- * <code>PortletRequest</code>.  In these cases they sometimes make it available in the
- * <code>USER_INFO</code> map provided as one of the attributes of the request.  If this is
- * the case in your portal, you can specify a list of <code>USER_INFO</code> attributes
- * to check for the username via the <code>userNameAttributes</code> property of this bean.
- * You can also completely override the {@link #getPrincipalFromRequest(PortletRequest)}
- * and {@link #getCredentialsFromRequest(PortletRequest)} methods to suit the particular
- * behavior of your portal.</p>
- *
- * <p>This interceptor will put the <code>PortletRequest</code> object into the
- * <code>details<code> property of the <code>Authentication</code> object that is sent
- * as a request to the <code>AuthenticationManager</code>.  This is done so that the request
- * is available to classes like {@link ContainerPortletAuthoritiesPopulator} that need
- * access to information from the portlet container.  The {@link PortletAuthenticationProvider}
- * will replace this with the <code>USER_INFO</code> map in the resulting <code>Authentication</code>
- * object.</p>
- *
- * @see org.springframework.security.ui.AbstractProcessingFilter
- * @see org.springframework.security.ui.webapp.AuthenticationProcessingFilter
- * @author John A. Lewis
- * @since 2.0
- * @version $Id$
- */
-public class PortletProcessingInterceptor implements HandlerInterceptor, InitializingBean {
-
-    //~ Static fields/initializers =====================================================================================
-
-    private static final Log logger = LogFactory.getLog(PortletProcessingInterceptor.class);
-
-    //~ Instance fields ================================================================================================
-
-    private AuthenticationManager authenticationManager;
-
-    private List userNameAttributes;
-
-    private AuthenticationDetailsSource authenticationDetailsSource;
-
-    private boolean useAuthTypeAsCredentials = false;
-
-    public PortletProcessingInterceptor() {
-        authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
-        ((AuthenticationDetailsSourceImpl)authenticationDetailsSource).setClazz(PortletAuthenticationDetails.class);
-    }
-
-    //~ Methods ========================================================================================================
-
-    public void afterPropertiesSet() throws Exception {
-        Assert.notNull(authenticationManager, "An AuthenticationManager must be set");
-    }
-
-    public boolean preHandleAction(ActionRequest request, ActionResponse response,
-            Object handler) throws Exception {
-        return preHandle(request, response, handler);
-    }
-
-    public boolean preHandleRender(RenderRequest request,
-            RenderResponse response, Object handler) throws Exception {
-        return preHandle(request, response, handler);
-    }
-
-    public void postHandleRender(RenderRequest request, RenderResponse response,
-            Object handler, ModelAndView modelAndView) throws Exception {
-    }
-
-    public void afterActionCompletion(ActionRequest request, ActionResponse response,
-            Object handler, Exception ex) throws Exception {
-    }
-
-    public void afterRenderCompletion(RenderRequest request, RenderResponse response,
-            Object handler, Exception ex) throws Exception {
-    }
-
-    /**
-     * Common preHandle method for both the action and render phases of the interceptor.
-     */
-    private boolean preHandle(PortletRequest request, PortletResponse response,
-            Object handler) throws Exception {
-
-        // get the SecurityContext
-        SecurityContext ctx = SecurityContextHolder.getContext();
-
-        if (logger.isDebugEnabled())
-            logger.debug("Checking secure context token: " + ctx.getAuthentication());
-
-        // if there is no existing Authentication object, then lets create one
-        if (ctx.getAuthentication() == null) {
-
-            try {
-
-                // build the authentication request from the PortletRequest
-                PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(
-                        getPrincipalFromRequest(request),
-                        getCredentialsFromRequest(request));
-
-                // put the PortletRequest into the authentication request as the "details"
-                authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
-
-                if (logger.isDebugEnabled())
-                    logger.debug("Beginning authentication request for user '" + authRequest.getName() + "'");
-
-                onPreAuthentication(request, response);
-
-                // ask the authentication manager to authenticate the request
-                // it will throw an AuthenticationException if it fails, otherwise it succeeded
-                Authentication authResult = authenticationManager.authenticate(authRequest);
-
-                // process a successful authentication
-                if (logger.isDebugEnabled()) {
-                    logger.debug("Authentication success: " + authResult);
-                }
-
-                ctx.setAuthentication(authResult);
-                onSuccessfulAuthentication(request, response, authResult);
-
-            } catch (AuthenticationException failed) {
-                // process an unsuccessful authentication
-                if (logger.isDebugEnabled()) {
-                    logger.debug("Authentication failed - updating ContextHolder to contain null Authentication", failed);
-                }
-                ctx.setAuthentication(null);
-                request.getPortletSession().setAttribute(
-                        AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY,
-                        failed, PortletSession.APPLICATION_SCOPE);
-                onUnsuccessfulAuthentication(request, response, failed);
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * This method attempts to extract a principal from the portlet request.
-     * According to the JSR 168 spec, the <code>PortletRequest<code> should return the name
-     * of the user in the <code>getRemoteUser()</code> method.  It should also provide a
-     * <code>java.security.Principal</code> object from the <code>getUserPrincipal()</code>
-     * method.  We will first try these to come up with a valid username.
-     * <p>Unfortunately, some portals do not properly return these values for authenticated
-     * users.  So, if neither of those succeeds and if the <code>userNameAttributes</code>
-     * property has been populated, then we will search through the <code>USER_INFO<code>
-     * map from the request to see if we can find a valid username.
-     * <p>This method can be overridden by subclasses to provide special handling
-     * for portals with weak support for the JSR 168 spec.</p>
-     * @param request the portlet request object
-     * @return the determined principal object, or null if none found
-     */
-    protected Object getPrincipalFromRequest(PortletRequest request) {
-
-        // first try getRemoteUser()
-        String remoteUser = request.getRemoteUser();
-        if (remoteUser != null) {
-            return remoteUser;
-        }
-
-        // next try getUserPrincipal()
-        Principal userPrincipal = request.getUserPrincipal();
-        if (userPrincipal != null) {
-            String userPrincipalName = userPrincipal.getName();
-            if (userPrincipalName != null) {
-                return userPrincipalName;
-            }
-        }
-
-        // last try entries in USER_INFO if any attributes were defined
-        if (this.userNameAttributes != null) {
-            Map userInfo = null;
-            try {
-                userInfo = (Map)request.getAttribute(PortletRequest.USER_INFO);
-            } catch (Exception e) {
-                logger.warn("unable to retrieve USER_INFO map from portlet request", e);
-            }
-            if (userInfo != null) {
-                Iterator i = this.userNameAttributes.iterator();
-                while(i.hasNext()) {
-                    Object principal = (String)userInfo.get(i.next());
-                    if (principal != null) {
-                        return principal;
-                    }
-                }
-            }
-        }
-
-        // none found so return null
-        return null;
-    }
-
-    /**
-     * This method attempts to extract a credentials from the portlet request.
-     * We are trusting the portal framework to authenticate the user, so all
-     * we are really doing is trying to put something intelligent in here to
-     * indicate the user is authenticated.  According to the JSR 168 spec,
-     * PortletRequest.getAuthType() should return a non-null value if the
-     * user is authenticated and should be null if not authenticated. So we
-     * will use this as the credentials and the token will be trusted as
-     * authenticated if the credentials are not null.
-     * <p>This method can be overridden by subclasses to provide special handling
-     * for portals with weak support for the JSR 168 spec.  If that is done,
-     * be sure the value is non-null for authenticated users and null for
-     * non-authenticated users.</p>
-     * @param request the portlet request object
-     * @return the determined credentials object, or null if none found
-     */
-    protected Object getCredentialsFromRequest(PortletRequest request) {
-        if (useAuthTypeAsCredentials) {
-            return request.getAuthType();
-        }
-
-        return "dummy";
-    }
-
-    /**
-     * Callback for custom processing prior to the authentication attempt.
-     * @param request the portlet request to be authenticated
-     * @param response the portlet response to be authenticated
-     * @throws AuthenticationException to indicate that authentication attempt is not valid and should be terminated
-     * @throws IOException
-     */
-    protected void onPreAuthentication(PortletRequest request, PortletResponse response)
-        throws AuthenticationException, IOException {}
-
-    /**
-     * Callback for custom processing after a successful authentication attempt.
-     * @param request the portlet request that was authenticated
-     * @param response the portlet response that was authenticated
-     * @param authResult the resulting Authentication object
-     * @throws IOException
-     */
-    protected void onSuccessfulAuthentication(PortletRequest request, PortletResponse response, Authentication authResult)
-        throws IOException {}
-
-    /**
-     * Callback for custom processing after an unsuccessful authentication attempt.
-     * @param request the portlet request that failed authentication
-     * @param response the portlet response that failed authentication
-     * @param failed the AuthenticationException that occurred
-     * @throws IOException
-     */
-    protected void onUnsuccessfulAuthentication(PortletRequest request, PortletResponse response, AuthenticationException failed)
-        throws IOException {}
-
-
-    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
-        this.authenticationManager = authenticationManager;
-    }
-
-    public void setUserNameAttributes(List userNameAttributes) {
-        this.userNameAttributes = userNameAttributes;
-    }
-
-    public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
-        this.authenticationDetailsSource = authenticationDetailsSource;
-    }
-
-    /**
-     * It true, the "authType" proerty of the <tt>PortletRequest</tt> will be used as the credentials.
-     * Defaults to false.
-     *
-     * @param useAuthTypeAsCredentials
-     */
-    public void setUseAuthTypeAsCredentials(boolean useAuthTypeAsCredentials) {
-        this.useAuthTypeAsCredentials = useAuthTypeAsCredentials;
-    }
-}
+/*
+ * Copyright 2005-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.ui.portlet;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.portlet.*;
+
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationDetailsSource;
+import org.springframework.security.AuthenticationDetailsSourceImpl;
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.AuthenticationManager;
+import org.springframework.security.web.authentication.AbstractProcessingFilter;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationToken;
+import org.springframework.security.web.authentication.AbstractProcessingFilter;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+import org.springframework.web.portlet.HandlerInterceptor;
+import org.springframework.web.portlet.ModelAndView;
+
+/**
+ * <p>This interceptor is responsible for processing portlet authentication requests.  This
+ * is the portlet equivalent of the <code>AuthenticationProcessingFilter</code> used for
+ * traditional servlet-based web applications. It is applied to both <code>ActionRequest</code>s
+ * and <code>RenderRequest</code>s alike.  If authentication is successful, the resulting
+ * {@link Authentication} object will be placed into the <code>SecurityContext</code>, which
+ * is guaranteed to have already been created by an earlier interceptor.  If authentication
+ * fails, the <code>AuthenticationException</code> will be placed into the
+ * <code>APPLICATION_SCOPE</code> of the <code>PortletSession</code> with the attribute defined
+ * by {@link AbstractProcessingFilter#SPRING_SECURITY_LAST_EXCEPTION_KEY}.</p>
+ *
+ *  <p>Some portals do not properly provide the identity of the current user via the
+ * <code>getRemoteUser()</code> or <code>getUserPrincipal()</code> methods of the
+ * <code>PortletRequest</code>.  In these cases they sometimes make it available in the
+ * <code>USER_INFO</code> map provided as one of the attributes of the request.  If this is
+ * the case in your portal, you can specify a list of <code>USER_INFO</code> attributes
+ * to check for the username via the <code>userNameAttributes</code> property of this bean.
+ * You can also completely override the {@link #getPrincipalFromRequest(PortletRequest)}
+ * and {@link #getCredentialsFromRequest(PortletRequest)} methods to suit the particular
+ * behavior of your portal.</p>
+ *
+ * <p>This interceptor will put the <code>PortletRequest</code> object into the
+ * <code>details<code> property of the <code>Authentication</code> object that is sent
+ * as a request to the <code>AuthenticationManager</code>.
+ *
+ * @see org.springframework.security.web.authentication.AbstractProcessingFilter
+ * @see org.springframework.security.web.authentication.AuthenticationProcessingFilter
+ * @author John A. Lewis
+ * @since 2.0
+ * @version $Id$
+ */
+public class PortletProcessingInterceptor implements HandlerInterceptor, InitializingBean {
+
+    //~ Static fields/initializers =====================================================================================
+
+    private static final Log logger = LogFactory.getLog(PortletProcessingInterceptor.class);
+
+    //~ Instance fields ================================================================================================
+
+    private AuthenticationManager authenticationManager;
+
+    private List userNameAttributes;
+
+    private AuthenticationDetailsSource authenticationDetailsSource;
+
+    private boolean useAuthTypeAsCredentials = false;
+
+    public PortletProcessingInterceptor() {
+        authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
+        ((AuthenticationDetailsSourceImpl)authenticationDetailsSource).setClazz(PortletAuthenticationDetails.class);
+    }
+
+    //~ Methods ========================================================================================================
+
+    public void afterPropertiesSet() throws Exception {
+        Assert.notNull(authenticationManager, "An AuthenticationManager must be set");
+    }
+
+    public boolean preHandleAction(ActionRequest request, ActionResponse response,
+            Object handler) throws Exception {
+        return preHandle(request, response, handler);
+    }
+
+    public boolean preHandleRender(RenderRequest request,
+            RenderResponse response, Object handler) throws Exception {
+        return preHandle(request, response, handler);
+    }
+
+    public void postHandleRender(RenderRequest request, RenderResponse response,
+            Object handler, ModelAndView modelAndView) throws Exception {
+    }
+
+    public void afterActionCompletion(ActionRequest request, ActionResponse response,
+            Object handler, Exception ex) throws Exception {
+    }
+
+    public void afterRenderCompletion(RenderRequest request, RenderResponse response,
+            Object handler, Exception ex) throws Exception {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean preHandleResource(ResourceRequest request, ResourceResponse response, Object handler) throws Exception {
+        return preHandle(request, response, handler);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void postHandleResource(ResourceRequest request, ResourceResponse response, Object handler, ModelAndView modelAndView) throws Exception {
+    }
+
+    public void afterResourceCompletion(ResourceRequest request, ResourceResponse response, Object handler, Exception ex) throws Exception {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean preHandleEvent(EventRequest request, EventResponse response, Object handler) throws Exception {
+        return preHandle(request, response, handler);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void afterEventCompletion(EventRequest request, EventResponse response, Object handler, Exception ex) throws Exception {
+    }
+
+    /**
+     * Common preHandle method for both the action and render phases of the interceptor.
+     */
+    private boolean preHandle(PortletRequest request, PortletResponse response,
+            Object handler) throws Exception {
+
+        // get the SecurityContext
+        SecurityContext ctx = SecurityContextHolder.getContext();
+
+        if (logger.isDebugEnabled())
+            logger.debug("Checking secure context token: " + ctx.getAuthentication());
+
+        // if there is no existing Authentication object, then lets create one
+        if (ctx.getAuthentication() == null) {
+
+            try {
+
+                // build the authentication request from the PortletRequest
+                PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(
+                        getPrincipalFromRequest(request),
+                        getCredentialsFromRequest(request));
+
+                // put the PortletRequest into the authentication request as the "details"
+                authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
+
+                if (logger.isDebugEnabled())
+                    logger.debug("Beginning authentication request for user '" + authRequest.getName() + "'");
+
+                onPreAuthentication(request, response);
+
+                // ask the authentication manager to authenticate the request
+                // it will throw an AuthenticationException if it fails, otherwise it succeeded
+                Authentication authResult = authenticationManager.authenticate(authRequest);
+
+                // process a successful authentication
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Authentication success: " + authResult);
+                }
+
+                ctx.setAuthentication(authResult);
+                onSuccessfulAuthentication(request, response, authResult);
+
+            } catch (AuthenticationException failed) {
+                // process an unsuccessful authentication
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Authentication failed - updating ContextHolder to contain null Authentication", failed);
+                }
+                ctx.setAuthentication(null);
+                request.getPortletSession().setAttribute(
+                        AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY,
+                        failed, PortletSession.APPLICATION_SCOPE);
+                onUnsuccessfulAuthentication(request, response, failed);
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * This method attempts to extract a principal from the portlet request.
+     * According to the JSR 168 spec, the <code>PortletRequest<code> should return the name
+     * of the user in the <code>getRemoteUser()</code> method.  It should also provide a
+     * <code>java.security.Principal</code> object from the <code>getUserPrincipal()</code>
+     * method.  We will first try these to come up with a valid username.
+     * <p>Unfortunately, some portals do not properly return these values for authenticated
+     * users.  So, if neither of those succeeds and if the <code>userNameAttributes</code>
+     * property has been populated, then we will search through the <code>USER_INFO<code>
+     * map from the request to see if we can find a valid username.
+     * <p>This method can be overridden by subclasses to provide special handling
+     * for portals with weak support for the JSR 168 spec.</p>
+     * @param request the portlet request object
+     * @return the determined principal object, or null if none found
+     */
+    protected Object getPrincipalFromRequest(PortletRequest request) {
+
+        // first try getRemoteUser()
+        String remoteUser = request.getRemoteUser();
+        if (remoteUser != null) {
+            return remoteUser;
+        }
+
+        // next try getUserPrincipal()
+        Principal userPrincipal = request.getUserPrincipal();
+        if (userPrincipal != null) {
+            String userPrincipalName = userPrincipal.getName();
+            if (userPrincipalName != null) {
+                return userPrincipalName;
+            }
+        }
+
+        // last try entries in USER_INFO if any attributes were defined
+        if (this.userNameAttributes != null) {
+            Map userInfo = null;
+            try {
+                userInfo = (Map)request.getAttribute(PortletRequest.USER_INFO);
+            } catch (Exception e) {
+                logger.warn("unable to retrieve USER_INFO map from portlet request", e);
+            }
+            if (userInfo != null) {
+                Iterator i = this.userNameAttributes.iterator();
+                while(i.hasNext()) {
+                    Object principal = (String)userInfo.get(i.next());
+                    if (principal != null) {
+                        return principal;
+                    }
+                }
+            }
+        }
+
+        // none found so return null
+        return null;
+    }
+
+    /**
+     * This method attempts to extract a credentials from the portlet request.
+     * We are trusting the portal framework to authenticate the user, so all
+     * we are really doing is trying to put something intelligent in here to
+     * indicate the user is authenticated.  According to the JSR 168 spec,
+     * PortletRequest.getAuthType() should return a non-null value if the
+     * user is authenticated and should be null if not authenticated. So we
+     * will use this as the credentials and the token will be trusted as
+     * authenticated if the credentials are not null.
+     * <p>This method can be overridden by subclasses to provide special handling
+     * for portals with weak support for the JSR 168 spec.  If that is done,
+     * be sure the value is non-null for authenticated users and null for
+     * non-authenticated users.</p>
+     * @param request the portlet request object
+     * @return the determined credentials object, or null if none found
+     */
+    protected Object getCredentialsFromRequest(PortletRequest request) {
+        if (useAuthTypeAsCredentials) {
+            return request.getAuthType();
+        }
+
+        return "dummy";
+    }
+
+    /**
+     * Callback for custom processing prior to the authentication attempt.
+     * @param request the portlet request to be authenticated
+     * @param response the portlet response to be authenticated
+     * @throws AuthenticationException to indicate that authentication attempt is not valid and should be terminated
+     * @throws IOException
+     */
+    protected void onPreAuthentication(PortletRequest request, PortletResponse response)
+        throws AuthenticationException, IOException {}
+
+    /**
+     * Callback for custom processing after a successful authentication attempt.
+     * @param request the portlet request that was authenticated
+     * @param response the portlet response that was authenticated
+     * @param authResult the resulting Authentication object
+     * @throws IOException
+     */
+    protected void onSuccessfulAuthentication(PortletRequest request, PortletResponse response, Authentication authResult)
+        throws IOException {}
+
+    /**
+     * Callback for custom processing after an unsuccessful authentication attempt.
+     * @param request the portlet request that failed authentication
+     * @param response the portlet response that failed authentication
+     * @param failed the AuthenticationException that occurred
+     * @throws IOException
+     */
+    protected void onUnsuccessfulAuthentication(PortletRequest request, PortletResponse response, AuthenticationException failed)
+        throws IOException {}
+
+
+    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
+        this.authenticationManager = authenticationManager;
+    }
+
+    public void setUserNameAttributes(List userNameAttributes) {
+        this.userNameAttributes = userNameAttributes;
+    }
+
+    public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
+        this.authenticationDetailsSource = authenticationDetailsSource;
+    }
+
+    /**
+     * It true, the "authType" proerty of the <tt>PortletRequest</tt> will be used as the credentials.
+     * Defaults to false.
+     *
+     * @param useAuthTypeAsCredentials
+     */
+    public void setUseAuthTypeAsCredentials(boolean useAuthTypeAsCredentials) {
+        this.useAuthTypeAsCredentials = useAuthTypeAsCredentials;
+    }
+}

+ 1 - 0
portlet/src/test/java/org/springframework/security/ui/portlet/PortletProcessingInterceptorTests.java

@@ -34,6 +34,7 @@ import org.springframework.mock.web.portlet.MockRenderResponse;
 import org.springframework.security.Authentication;
 import org.springframework.security.AuthenticationManager;
 import org.springframework.security.BadCredentialsException;
+import org.springframework.security.web.authentication.AbstractProcessingFilter;
 import org.springframework.security.context.SecurityContextHolder;
 import org.springframework.security.providers.TestingAuthenticationToken;
 import org.springframework.security.providers.UsernamePasswordAuthenticationToken;

+ 1 - 1
samples/pom.xml

@@ -16,7 +16,7 @@
         <module>preauth</module>
         <module>openid</module>
         <module>ldap</module>
-        <!-- module>portlet</module -->
+        <module>portlet</module>
         <module>cas</module>        
     </modules>
     <dependencies>

+ 2 - 2
samples/portlet/src/main/java/org/springframework/web/portlet/sample/SecurityContextPortlet.java

@@ -11,11 +11,11 @@ import javax.portlet.RenderRequest;
 import javax.portlet.RenderResponse;
 
 import org.springframework.security.context.SecurityContextHolder;
-import org.springframework.security.ui.AbstractProcessingFilter;
+import org.springframework.security.web.authentication.AbstractProcessingFilter;
 
 
 /**
- * A simple portlet which prints out the contents of the current {@link SecurityContext} 
+ * A simple portlet which prints out the contents of the current {@link org.springframework.security.context.SecurityContext} 
  *  
  * @author Luke Taylor
  */

+ 2 - 2
samples/portlet/src/main/resources/portlet/securityContextPortlet.xml

@@ -38,7 +38,7 @@
                 <property name="mappableRolesRetriever">
                     <bean class="org.springframework.security.authoritymapping.SimpleMappableAttributesRetriever">
                         <property name="mappableAttributes">
-                            <list>
+                            <set>
                                 <value>tomcat</value>
                                 <value>admin</value>
                                 <value>manager</value>
@@ -47,7 +47,7 @@
                                 <value>Guest</value>
                                 <value>User</value>
                                 <value>Power User</value>
-                            </list>
+                            </set>
                         </property>                        
                     </bean>
                 </property>

+ 151 - 150
web/src/test/java/org/springframework/security/web/authentication/preauth/j2ee/J2eeBasedPreAuthenticatedWebAuthenticationDetailsSourceTests.java

@@ -1,150 +1,151 @@
-package org.springframework.security.web.authentication.preauth.j2ee;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.servlet.http.HttpServletRequest;
-
-import junit.framework.TestCase;
-
-import org.springframework.security.authoritymapping.MappableAttributesRetriever;
-import org.springframework.security.authoritymapping.Attributes2GrantedAuthoritiesMapper;
-import org.springframework.security.authoritymapping.SimpleMappableAttributesRetriever;
-import org.springframework.security.authoritymapping.SimpleAttributes2GrantedAuthoritiesMapper;
-import org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails;
-import org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource;
-import org.springframework.security.GrantedAuthority;
-
-import org.springframework.mock.web.MockHttpServletRequest;
-
-/**
- *
- * @author TSARDD
- */
-public class J2eeBasedPreAuthenticatedWebAuthenticationDetailsSourceTests extends TestCase {
-
-    public final void testAfterPropertiesSetException() {
-        J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource t = new J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource();
-        try {
-            t.afterPropertiesSet();
-            fail("AfterPropertiesSet didn't throw expected exception");
-        } catch (IllegalArgumentException expected) {
-        } catch (Exception unexpected) {
-            fail("AfterPropertiesSet throws unexpected exception");
-        }
-    }
-
-    public final void testBuildDetailsHttpServletRequestNoMappedNoUserRoles() {
-        String[] mappedRoles = new String[] {};
-        String[] roles = new String[] {};
-        String[] expectedRoles = new String[] {};
-        testDetails(mappedRoles, roles, expectedRoles);
-    }
-
-    public final void testBuildDetailsHttpServletRequestNoMappedUnmappedUserRoles() {
-        String[] mappedRoles = new String[] {};
-        String[] roles = new String[] { "Role1", "Role2" };
-        String[] expectedRoles = new String[] {};
-        testDetails(mappedRoles, roles, expectedRoles);
-    }
-
-    public final void testBuildDetailsHttpServletRequestNoUserRoles() {
-        String[] mappedRoles = new String[] { "Role1", "Role2", "Role3", "Role4" };
-        String[] roles = new String[] {};
-        String[] expectedRoles = new String[] {};
-        testDetails(mappedRoles, roles, expectedRoles);
-    }
-
-    public final void testBuildDetailsHttpServletRequestAllUserRoles() {
-        String[] mappedRoles = new String[] { "Role1", "Role2", "Role3", "Role4" };
-        String[] roles = new String[] { "Role1", "Role2", "Role3", "Role4" };
-        String[] expectedRoles = new String[] { "Role1", "Role2", "Role3", "Role4" };
-        testDetails(mappedRoles, roles, expectedRoles);
-    }
-
-    public final void testBuildDetailsHttpServletRequestUnmappedUserRoles() {
-        String[] mappedRoles = new String[] { "Role1", "Role2", "Role3", "Role4" };
-        String[] roles = new String[] { "Role1", "Role2", "Role3", "Role4", "Role5" };
-        String[] expectedRoles = new String[] { "Role1", "Role2", "Role3", "Role4" };
-        testDetails(mappedRoles, roles, expectedRoles);
-    }
-
-    public final void testBuildDetailsHttpServletRequestPartialUserRoles() {
-        String[] mappedRoles = new String[] { "Role1", "Role2", "Role3", "Role4" };
-        String[] roles = new String[] { "Role2", "Role3" };
-        String[] expectedRoles = new String[] { "Role2", "Role3" };
-        testDetails(mappedRoles, roles, expectedRoles);
-    }
-
-    public final void testBuildDetailsHttpServletRequestPartialAndUnmappedUserRoles() {
-        String[] mappedRoles = new String[] { "Role1", "Role2", "Role3", "Role4" };
-        String[] roles = new String[] { "Role2", "Role3", "Role5" };
-        String[] expectedRoles = new String[] { "Role2", "Role3" };
-        testDetails(mappedRoles, roles, expectedRoles);
-    }
-
-    private void testDetails(String[] mappedRoles, String[] userRoles, String[] expectedRoles) {
-        J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource src = getJ2eeBasedPreAuthenticatedWebAuthenticationDetailsSource(mappedRoles);
-        Object o = src.buildDetails(getRequest("testUser", userRoles));
-        assertNotNull(o);
-        assertTrue("Returned object not of type PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails, actual type: " + o.getClass(),
-                o instanceof PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails);
-        PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails details = (PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails) o;
-        List<GrantedAuthority> gas = details.getGrantedAuthorities();
-        assertNotNull("Granted authorities should not be null", gas);
-        assertEquals(expectedRoles.length, gas.size());
-
-        Collection<String> expectedRolesColl = Arrays.asList(expectedRoles);
-        Collection<String> gasRolesSet = new HashSet<String>();
-        for (int i = 0; i < gas.size(); i++) {
-            gasRolesSet.add(gas.get(i).getAuthority());
-        }
-        assertTrue("Granted Authorities do not match expected roles", expectedRolesColl.containsAll(gasRolesSet)
-                && gasRolesSet.containsAll(expectedRolesColl));
-    }
-
-    private final J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource getJ2eeBasedPreAuthenticatedWebAuthenticationDetailsSource(
-            String[] mappedRoles) {
-        J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource result = new J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource();
-        result.setMappableRolesRetriever(getMappableRolesRetriever(mappedRoles));
-        result.setUserRoles2GrantedAuthoritiesMapper(getJ2eeUserRoles2GrantedAuthoritiesMapper());
-        result.setClazz(PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails.class);
-
-        try {
-            result.afterPropertiesSet();
-        } catch (Exception expected) {
-            fail("AfterPropertiesSet throws unexpected exception");
-        }
-        return result;
-    }
-
-    private MappableAttributesRetriever getMappableRolesRetriever(String[] mappedRoles) {
-        SimpleMappableAttributesRetriever result = new SimpleMappableAttributesRetriever();
-        result.setMappableAttributes(mappedRoles);
-        return result;
-    }
-
-    private Attributes2GrantedAuthoritiesMapper getJ2eeUserRoles2GrantedAuthoritiesMapper() {
-        SimpleAttributes2GrantedAuthoritiesMapper result = new SimpleAttributes2GrantedAuthoritiesMapper();
-        result.setAddPrefixIfAlreadyExisting(false);
-        result.setConvertAttributeToLowerCase(false);
-        result.setConvertAttributeToUpperCase(false);
-        result.setAttributePrefix("");
-        return result;
-    }
-
-    private final HttpServletRequest getRequest(final String userName,final String[] aRoles)
-    {
-        MockHttpServletRequest req = new MockHttpServletRequest() {
-            private Set<String> roles = new HashSet<String>(Arrays.asList(aRoles));
-            public boolean isUserInRole(String arg0) {
-                return roles.contains(arg0);
-            }
-        };
-        req.setRemoteUser(userName);
-        return req;
-    }
-}
+package org.springframework.security.web.authentication.preauth.j2ee;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import junit.framework.TestCase;
+
+import org.springframework.security.authoritymapping.MappableAttributesRetriever;
+import org.springframework.security.authoritymapping.Attributes2GrantedAuthoritiesMapper;
+import org.springframework.security.authoritymapping.SimpleMappableAttributesRetriever;
+import org.springframework.security.authoritymapping.SimpleAttributes2GrantedAuthoritiesMapper;
+import org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails;
+import org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource;
+import org.springframework.security.GrantedAuthority;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.util.StringUtils;
+
+/**
+ *
+ * @author TSARDD
+ */
+public class J2eeBasedPreAuthenticatedWebAuthenticationDetailsSourceTests extends TestCase {
+
+    public final void testAfterPropertiesSetException() {
+        J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource t = new J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource();
+        try {
+            t.afterPropertiesSet();
+            fail("AfterPropertiesSet didn't throw expected exception");
+        } catch (IllegalArgumentException expected) {
+        } catch (Exception unexpected) {
+            fail("AfterPropertiesSet throws unexpected exception");
+        }
+    }
+
+    public final void testBuildDetailsHttpServletRequestNoMappedNoUserRoles() {
+        String[] mappedRoles = new String[] {};
+        String[] roles = new String[] {};
+        String[] expectedRoles = new String[] {};
+        testDetails(mappedRoles, roles, expectedRoles);
+    }
+
+    public final void testBuildDetailsHttpServletRequestNoMappedUnmappedUserRoles() {
+        String[] mappedRoles = new String[] {};
+        String[] roles = new String[] { "Role1", "Role2" };
+        String[] expectedRoles = new String[] {};
+        testDetails(mappedRoles, roles, expectedRoles);
+    }
+
+    public final void testBuildDetailsHttpServletRequestNoUserRoles() {
+        String[] mappedRoles = new String[] { "Role1", "Role2", "Role3", "Role4" };
+        String[] roles = new String[] {};
+        String[] expectedRoles = new String[] {};
+        testDetails(mappedRoles, roles, expectedRoles);
+    }
+
+    public final void testBuildDetailsHttpServletRequestAllUserRoles() {
+        String[] mappedRoles = new String[] { "Role1", "Role2", "Role3", "Role4" };
+        String[] roles = new String[] { "Role1", "Role2", "Role3", "Role4" };
+        String[] expectedRoles = new String[] { "Role1", "Role2", "Role3", "Role4" };
+        testDetails(mappedRoles, roles, expectedRoles);
+    }
+
+    public final void testBuildDetailsHttpServletRequestUnmappedUserRoles() {
+        String[] mappedRoles = new String[] { "Role1", "Role2", "Role3", "Role4" };
+        String[] roles = new String[] { "Role1", "Role2", "Role3", "Role4", "Role5" };
+        String[] expectedRoles = new String[] { "Role1", "Role2", "Role3", "Role4" };
+        testDetails(mappedRoles, roles, expectedRoles);
+    }
+
+    public final void testBuildDetailsHttpServletRequestPartialUserRoles() {
+        String[] mappedRoles = new String[] { "Role1", "Role2", "Role3", "Role4" };
+        String[] roles = new String[] { "Role2", "Role3" };
+        String[] expectedRoles = new String[] { "Role2", "Role3" };
+        testDetails(mappedRoles, roles, expectedRoles);
+    }
+
+    public final void testBuildDetailsHttpServletRequestPartialAndUnmappedUserRoles() {
+        String[] mappedRoles = new String[] { "Role1", "Role2", "Role3", "Role4" };
+        String[] roles = new String[] { "Role2", "Role3", "Role5" };
+        String[] expectedRoles = new String[] { "Role2", "Role3" };
+        testDetails(mappedRoles, roles, expectedRoles);
+    }
+
+    private void testDetails(String[] mappedRoles, String[] userRoles, String[] expectedRoles) {
+        J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource src = getJ2eeBasedPreAuthenticatedWebAuthenticationDetailsSource(mappedRoles);
+        Object o = src.buildDetails(getRequest("testUser", userRoles));
+        assertNotNull(o);
+        assertTrue("Returned object not of type PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails, actual type: " + o.getClass(),
+                o instanceof PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails);
+        PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails details = (PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails) o;
+        List<GrantedAuthority> gas = details.getGrantedAuthorities();
+        assertNotNull("Granted authorities should not be null", gas);
+        assertEquals(expectedRoles.length, gas.size());
+
+        Collection<String> expectedRolesColl = Arrays.asList(expectedRoles);
+        Collection<String> gasRolesSet = new HashSet<String>();
+        for (int i = 0; i < gas.size(); i++) {
+            gasRolesSet.add(gas.get(i).getAuthority());
+        }
+        assertTrue("Granted Authorities do not match expected roles", expectedRolesColl.containsAll(gasRolesSet)
+                && gasRolesSet.containsAll(expectedRolesColl));
+    }
+
+    private final J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource getJ2eeBasedPreAuthenticatedWebAuthenticationDetailsSource(
+            String[] mappedRoles) {
+        J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource result = new J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource();
+        result.setMappableRolesRetriever(getMappableRolesRetriever(mappedRoles));
+        result.setUserRoles2GrantedAuthoritiesMapper(getJ2eeUserRoles2GrantedAuthoritiesMapper());
+        result.setClazz(PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails.class);
+
+        try {
+            result.afterPropertiesSet();
+        } catch (Exception expected) {
+            fail("AfterPropertiesSet throws unexpected exception");
+        }
+        return result;
+    }
+
+    private MappableAttributesRetriever getMappableRolesRetriever(String[] mappedRoles) {
+        SimpleMappableAttributesRetriever result = new SimpleMappableAttributesRetriever();
+        result.setMappableAttributes(new HashSet<String>(Arrays.asList(mappedRoles)));
+        return result;
+    }
+
+    private Attributes2GrantedAuthoritiesMapper getJ2eeUserRoles2GrantedAuthoritiesMapper() {
+        SimpleAttributes2GrantedAuthoritiesMapper result = new SimpleAttributes2GrantedAuthoritiesMapper();
+        result.setAddPrefixIfAlreadyExisting(false);
+        result.setConvertAttributeToLowerCase(false);
+        result.setConvertAttributeToUpperCase(false);
+        result.setAttributePrefix("");
+        return result;
+    }
+
+    private final HttpServletRequest getRequest(final String userName,final String[] aRoles)
+    {
+        MockHttpServletRequest req = new MockHttpServletRequest() {
+            private Set<String> roles = new HashSet<String>(Arrays.asList(aRoles));
+            public boolean isUserInRole(String arg0) {
+                return roles.contains(arg0);
+            }
+        };
+        req.setRemoteUser(userName);
+        return req;
+    }
+}