Browse Source

SEC-576: Committed pre-autheticated contribution. Still has to be more thoroughly reviewed.

Luke Taylor 17 years ago
parent
commit
c8b9f24038
32 changed files with 2116 additions and 0 deletions
  1. 6 0
      core/pom.xml
  2. 90 0
      core/src/main/java/org/springframework/security/providers/preauth/PreAuthenticatedAuthenticationProvider.java
  3. 70 0
      core/src/main/java/org/springframework/security/providers/preauth/PreAuthenticatedAuthenticationToken.java
  4. 15 0
      core/src/main/java/org/springframework/security/providers/preauth/PreAuthenticatedGrantedAuthoritiesRetriever.java
  5. 18 0
      core/src/main/java/org/springframework/security/providers/preauth/PreAuthenticatedGrantedAuthoritiesSetter.java
  6. 44 0
      core/src/main/java/org/springframework/security/providers/preauth/PreAuthenticatedGrantedAuthoritiesUserDetailsService.java
  7. 24 0
      core/src/main/java/org/springframework/security/providers/preauth/PreAuthenticatedUserDetailsService.java
  8. 45 0
      core/src/main/java/org/springframework/security/providers/preauth/UserDetailsByNameServiceWrapper.java
  9. 16 0
      core/src/main/java/org/springframework/security/rolemapping/MappableRolesRetriever.java
  10. 22 0
      core/src/main/java/org/springframework/security/rolemapping/Roles2GrantedAuthoritiesMapper.java
  11. 30 0
      core/src/main/java/org/springframework/security/rolemapping/SimpleMappableRolesRetriever.java
  12. 105 0
      core/src/main/java/org/springframework/security/rolemapping/SimpleRoles2GrantedAuthoritiesMapper.java
  13. 181 0
      core/src/main/java/org/springframework/security/rolemapping/XmlMappableRolesRetriever.java
  14. 176 0
      core/src/main/java/org/springframework/security/ui/preauth/AbstractPreAuthenticatedProcessingFilter.java
  15. 58 0
      core/src/main/java/org/springframework/security/ui/preauth/PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails.java
  16. 66 0
      core/src/main/java/org/springframework/security/ui/preauth/PreAuthenticatedProcesingFilterEntryPoint.java
  17. 101 0
      core/src/main/java/org/springframework/security/ui/preauth/j2ee/J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.java
  18. 36 0
      core/src/main/java/org/springframework/security/ui/preauth/j2ee/J2eePreAuthenticatedProcessingFilter.java
  19. 52 0
      core/src/main/java/org/springframework/security/ui/preauth/j2ee/WebXmlMappableRolesRetriever.java
  20. 102 0
      core/src/test/java/org/springframework/security/providers/preauth/PreAuthenticatedAuthenticationProviderTests.java
  21. 57 0
      core/src/test/java/org/springframework/security/providers/preauth/PreAuthenticatedAuthenticationTokenTests.java
  22. 79 0
      core/src/test/java/org/springframework/security/providers/preauth/PreAuthenticatedGrantedAuthoritiesUserDetailsServiceTests.java
  23. 51 0
      core/src/test/java/org/springframework/security/providers/preauth/UserDetailsByNameServiceWrapperTests.java
  24. 26 0
      core/src/test/java/org/springframework/security/rolemapping/SimpleMappableRolesRetrieverTests.java
  25. 121 0
      core/src/test/java/org/springframework/security/rolemapping/SimpleRoles2GrantedAuthoritiesMapperTests.java
  26. 100 0
      core/src/test/java/org/springframework/security/rolemapping/XmlMappableRolesRetrieverTests.java
  27. 72 0
      core/src/test/java/org/springframework/security/ui/preauth/PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetailsTests.java
  28. 42 0
      core/src/test/java/org/springframework/security/ui/preauth/PreAuthenticatedProcesingFilterEntryPointTests.java
  29. 79 0
      core/src/test/java/org/springframework/security/ui/preauth/PreAuthenticatedProcessingFilterTests.java
  30. 149 0
      core/src/test/java/org/springframework/security/ui/preauth/j2ee/J2eeBasedPreAuthenticatedWebAuthenticationDetailsSourceTests.java
  31. 49 0
      core/src/test/java/org/springframework/security/ui/preauth/j2ee/J2eePreAuthenticatedProcessingFilterTests.java
  32. 34 0
      core/src/test/java/org/springframework/security/ui/preauth/j2ee/WebXmlJ2eeDefinedRolesRetrieverTests.java

+ 6 - 0
core/pom.xml

@@ -82,6 +82,12 @@
             <version>1.2</version>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>jaxen</groupId>
+            <artifactId>jaxen</artifactId>
+            <version>1.1.1</version>
+            <optional>true</optional>
+        </dependency>        
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>jsp-api</artifactId>

+ 90 - 0
core/src/main/java/org/springframework/security/providers/preauth/PreAuthenticatedAuthenticationProvider.java

@@ -0,0 +1,90 @@
+package org.springframework.security.providers.preauth;
+
+import org.springframework.security.providers.AuthenticationProvider;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.userdetails.UserDetails;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.core.Ordered;
+import org.springframework.util.Assert;
+
+/**
+ * <p>
+ * Processes a pre-authenticated authentication request. The request will
+ * typically originate from a {@link AbstractPreAuthenticatedProcessingFilter}
+ * subclass.
+ * </p>
+ * 
+ * <p>
+ * This authentication provider will not perform any checks on authentication
+ * requests, as they should already be pre- authenticated. However, the
+ * PreAuthenticatedUserDetailsService implementation may still throw for exampe
+ * a UsernameNotFoundException.
+ * </p>
+ */
+public class PreAuthenticatedAuthenticationProvider implements AuthenticationProvider, InitializingBean, Ordered {
+	private static final Log LOG = LogFactory.getLog(PreAuthenticatedAuthenticationProvider.class);
+
+	private PreAuthenticatedUserDetailsService preAuthenticatedUserDetailsService = null;
+
+	private int order = -1; // default: same as non-ordered
+
+	/**
+	 * Check whether all required properties have been set.
+	 */
+	public void afterPropertiesSet() {
+		Assert.notNull(preAuthenticatedUserDetailsService, "A PreAuthenticatedUserDetailsService must be set");
+	}
+
+	/**
+	 * Authenticate the given PreAuthenticatedAuthenticationToken.
+	 */
+	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+		if (!supports(authentication.getClass())) {
+			return null;
+		}
+
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("PreAuthenticated authentication request: " + authentication);
+		}
+
+		UserDetails ud = preAuthenticatedUserDetailsService.getUserDetails((PreAuthenticatedAuthenticationToken) authentication);
+		if (ud == null) {
+			return null;
+		}
+		PreAuthenticatedAuthenticationToken result = new PreAuthenticatedAuthenticationToken(ud, authentication.getCredentials(), ud
+				.getAuthorities());
+		result.setDetails(authentication.getDetails());
+
+		return result;
+
+	}
+
+	/**
+	 * Indicate that this provider only supports
+	 * PreAuthenticatedAuthenticationToken (sub)classes.
+	 */
+	public boolean supports(Class authentication) {
+		return PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication);
+	}
+
+	/**
+	 * Set the PreAuthenticatedUserDetailsServices to be used.
+	 * 
+	 * @param aPreAuthenticatedUserDetailsService
+	 */
+	public void setPreAuthenticatedUserDetailsService(PreAuthenticatedUserDetailsService aPreAuthenticatedUserDetailsService) {
+		this.preAuthenticatedUserDetailsService = aPreAuthenticatedUserDetailsService;
+	}
+
+	public int getOrder() {
+		return order;
+	}
+
+	public void setOrder(int i) {
+		order = i;
+	}
+}

+ 70 - 0
core/src/main/java/org/springframework/security/providers/preauth/PreAuthenticatedAuthenticationToken.java

@@ -0,0 +1,70 @@
+package org.springframework.security.providers.preauth;
+
+import org.springframework.security.providers.AbstractAuthenticationToken;
+import org.springframework.security.GrantedAuthority;
+
+
+/**
+ * {@link org.springframework.security.Authentication} implementation for pre-authenticated
+ * authentication.
+ */
+public class PreAuthenticatedAuthenticationToken extends AbstractAuthenticationToken {
+	private static final long serialVersionUID = 1L;
+
+	private Object principal;
+
+	private Object credentials;
+
+	/**
+	 * Constructor used for an authentication request. The {@link
+	 * org.springframework.security.Authentication#isAuthenticated()} will return
+	 * <code>false</code>.
+	 * 
+	 * @TODO Should we have only a single credentials parameter here? For
+	 *       example for X509 the certificate is used as credentials, while
+	 *       currently a J2EE username is specified as a principal but could as
+	 *       well be set as credentials.
+	 * 
+	 * @param aPrincipal
+	 *            The pre-authenticated principal
+	 * @param aCredentials
+	 *            The pre-authenticated credentials
+	 */
+	public PreAuthenticatedAuthenticationToken(Object aPrincipal, Object aCredentials) {
+		super(null);
+		this.principal = aPrincipal;
+		this.credentials = aCredentials;
+	}
+
+	/**
+	 * Constructor used for an authentication response. The {@link
+	 * org.springframework.security.Authentication#isAuthenticated()} will return
+	 * <code>true</code>.
+	 * 
+	 * @param aPrincipal
+	 *            The authenticated principal
+	 * @param anAuthorities
+	 *            The granted authorities
+	 */
+	public PreAuthenticatedAuthenticationToken(Object aPrincipal, Object aCredentials, GrantedAuthority[] anAuthorities) {
+		super(anAuthorities);
+		this.principal = aPrincipal;
+		this.credentials = aCredentials;
+		setAuthenticated(true);
+	}
+
+	/**
+	 * Get the credentials
+	 */
+	public Object getCredentials() {
+		return this.credentials;
+	}
+
+	/**
+	 * Get the principal
+	 */
+	public Object getPrincipal() {
+		return this.principal;
+	}
+
+}

+ 15 - 0
core/src/main/java/org/springframework/security/providers/preauth/PreAuthenticatedGrantedAuthoritiesRetriever.java

@@ -0,0 +1,15 @@
+package org.springframework.security.providers.preauth;
+
+import org.springframework.security.GrantedAuthority;
+
+
+/**
+ * Interface that allows for retrieval of a list of pre-authenticated Granted
+ * Authorities.
+ */
+public interface PreAuthenticatedGrantedAuthoritiesRetriever {
+	/**
+	 * @return GrantedAuthority[] List of pre-authenticated GrantedAuthorities
+	 */
+	public GrantedAuthority[] getPreAuthenticatedGrantedAuthorities();
+}

+ 18 - 0
core/src/main/java/org/springframework/security/providers/preauth/PreAuthenticatedGrantedAuthoritiesSetter.java

@@ -0,0 +1,18 @@
+package org.springframework.security.providers.preauth;
+
+import org.springframework.security.GrantedAuthority;
+
+/**
+ * Counterpart of PreAuthenticatedGrantedAuthoritiesRetriever that allows
+ * setting a list of pre-authenticated GrantedAuthorities. This interface is not
+ * actually being used by the PreAuthenticatedAuthenticationProvider or one of
+ * its related classes, but may be useful for classes that also implement
+ * PreAuthenticatedGrantedAuthoritiesRetriever.
+ */
+public interface PreAuthenticatedGrantedAuthoritiesSetter {
+	/**
+	 * @param aPreAuthenticatedGrantedAuthorities
+	 *            The pre-authenticated GrantedAuthority[] to set
+	 */
+	public void setPreAuthenticatedGrantedAuthorities(GrantedAuthority[] aPreAuthenticatedGrantedAuthorities);
+}

+ 44 - 0
core/src/main/java/org/springframework/security/providers/preauth/PreAuthenticatedGrantedAuthoritiesUserDetailsService.java

@@ -0,0 +1,44 @@
+package org.springframework.security.providers.preauth;
+
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.User;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.AuthenticationException;
+
+import org.springframework.util.Assert;
+
+/**
+ * <p>
+ * This PreAuthenticatedUserDetailsService implementation creates a UserDetails
+ * object based solely on the information contained in the given
+ * PreAuthenticatedAuthenticationToken. The user name is set to the name as
+ * returned by PreAuthenticatedAuthenticationToken.getName(), the password is
+ * set to a fixed dummy value (it will not be used by the
+ * PreAuthenticatedAuthenticationProvider anyway), and the Granted Authorities
+ * are retrieved from the details object as returned by
+ * PreAuthenticatedAuthenticationToken.getDetails().
+ * </p>
+ * 
+ * <p>
+ * The details object as returned by
+ * PreAuthenticatedAuthenticationToken.getDetails() must implement the
+ * PreAuthenticatedGrantedAuthoritiesRetriever interface for this implementation
+ * to work.
+ * </p>
+ */
+public class PreAuthenticatedGrantedAuthoritiesUserDetailsService implements PreAuthenticatedUserDetailsService {
+	/**
+	 * Get a UserDetails object based on the user name contained in the given
+	 * token, and the GrantedAuthorities as returned by the
+	 * PreAuthenticatedGrantedAuthoritiesRetriever implementation as returned by
+	 * the token.getDetails() method.
+	 */
+	public UserDetails getUserDetails(PreAuthenticatedAuthenticationToken token) throws AuthenticationException {
+		Assert.notNull(token.getDetails());
+		Assert.isInstanceOf(PreAuthenticatedGrantedAuthoritiesRetriever.class, token.getDetails());
+		GrantedAuthority[] preAuthenticatedGrantedAuthorities = ((PreAuthenticatedGrantedAuthoritiesRetriever) token.getDetails())
+				.getPreAuthenticatedGrantedAuthorities();
+		UserDetails ud = new User(token.getName(), "N/A", true, true, true, true, preAuthenticatedGrantedAuthorities);
+		return ud;
+	}
+}

+ 24 - 0
core/src/main/java/org/springframework/security/providers/preauth/PreAuthenticatedUserDetailsService.java

@@ -0,0 +1,24 @@
+package org.springframework.security.providers.preauth;
+
+import org.springframework.security.userdetails.UsernameNotFoundException;
+import org.springframework.security.userdetails.UserDetails;
+
+
+/**
+ * Interface that allows for retrieving a UserDetails object based on a
+ * PreAuthenticatedAuthenticationToken object.
+ */
+public interface PreAuthenticatedUserDetailsService {
+
+	/**
+	 * 
+	 * @param aPreAuthenticatedAuthenticationToken
+	 *            The pre-authenticated authentication token
+	 * @return UserDetails for the given authentication token.
+	 * @throws UsernameNotFoundException
+	 *             if no user details can be found for the given authentication
+	 *             token
+	 */
+	public UserDetails getUserDetails(PreAuthenticatedAuthenticationToken aPreAuthenticatedAuthenticationToken)
+			throws UsernameNotFoundException;
+}

+ 45 - 0
core/src/main/java/org/springframework/security/providers/preauth/UserDetailsByNameServiceWrapper.java

@@ -0,0 +1,45 @@
+package org.springframework.security.providers.preauth;
+
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.UserDetailsService;
+import org.springframework.security.userdetails.UsernameNotFoundException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.dao.DataAccessException;
+import org.springframework.util.Assert;
+
+/**
+ * This implementation for PreAuthenticatedUserDetailsService wraps a regular
+ * Acegi UserDetailsService implementation, to retrieve a UserDetails object
+ * based on the user name contained in a PreAuthenticatedAuthenticationToken.
+ */
+public class UserDetailsByNameServiceWrapper implements PreAuthenticatedUserDetailsService, InitializingBean {
+	private UserDetailsService userDetailsService = null;
+
+	/**
+	 * Check whether all required properties have been set.
+	 * 
+	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
+	 */
+	public void afterPropertiesSet() throws Exception {
+		Assert.notNull(userDetailsService, "UserDetailsService must be set");
+	}
+
+	/**
+	 * Get the UserDetails object from the wrapped UserDetailsService
+	 * implementation
+	 */
+	public UserDetails getUserDetails(PreAuthenticatedAuthenticationToken aJ2eeAuthenticationToken) throws UsernameNotFoundException,
+			DataAccessException {
+		return userDetailsService.loadUserByUsername(aJ2eeAuthenticationToken.getName());
+	}
+
+	/**
+	 * Set the wrapped UserDetailsService implementation
+	 * 
+	 * @param aUserDetailsService
+	 *            The wrapped UserDetailsService to set
+	 */
+	public void setUserDetailsService(UserDetailsService aUserDetailsService) {
+		userDetailsService = aUserDetailsService;
+	}
+}

+ 16 - 0
core/src/main/java/org/springframework/security/rolemapping/MappableRolesRetriever.java

@@ -0,0 +1,16 @@
+package org.springframework.security.rolemapping;
+
+/**
+ * Interface to be implemented by classes that can retrieve a list of mappable
+ * roles (for example the list of all available J2EE roles in a web or EJB
+ * application).
+ */
+public interface MappableRolesRetriever {
+	/**
+	 * Implementations of this method should return a list of all mappable
+	 * roles.
+	 * 
+	 * @return String[] containg list of all mappable roles
+	 */
+	public String[] getMappableRoles();
+}

+ 22 - 0
core/src/main/java/org/springframework/security/rolemapping/Roles2GrantedAuthoritiesMapper.java

@@ -0,0 +1,22 @@
+package org.springframework.security.rolemapping;
+
+import org.springframework.security.GrantedAuthority;
+
+/**
+ * Interface to be implemented by classes that can map a list of roles to a list
+ * of Acegi GrantedAuthorities.
+ */
+public interface Roles2GrantedAuthoritiesMapper {
+	/**
+	 * Implementations of this method should map the given list of roles to a
+	 * list of Acegi GrantedAuthorities. There are no restrictions for the
+	 * mapping process; a single role can be mapped to multiple Acegi
+	 * GrantedAuthorities, all roles can be mapped to a single Acegi
+	 * GrantedAuthority, some roles may not be mapped, etc.
+	 * 
+	 * @param String[]
+	 *            containing list of roles
+	 * @return GrantedAuthority[] containing list of mapped GrantedAuthorities
+	 */
+	public GrantedAuthority[] getGrantedAuthorities(String[] roles);
+}

+ 30 - 0
core/src/main/java/org/springframework/security/rolemapping/SimpleMappableRolesRetriever.java

@@ -0,0 +1,30 @@
+package org.springframework.security.rolemapping;
+
+import org.springframework.util.Assert;
+
+/**
+ * This class implements the MappableRolesRetriever interface by just returning
+ * a list of mappable roles as previously set using the corresponding setter
+ * method.
+ */
+public class SimpleMappableRolesRetriever implements MappableRolesRetriever {
+	private String[] mappableRoles = null;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.springframework.security.rolemapping.MappableRolesRetriever#getMappableRoles()
+	 */
+	public String[] getMappableRoles() {
+		Assert.notNull(mappableRoles, "No mappable roles have been set");
+		String[] copy = new String[mappableRoles.length];
+		System.arraycopy(mappableRoles, 0, copy, 0, copy.length);
+		return copy;
+	}
+
+	public void setMappableRoles(String[] aMappableRoles) {
+		this.mappableRoles = new String[aMappableRoles.length];
+		System.arraycopy(aMappableRoles, 0, mappableRoles, 0, mappableRoles.length);
+	}
+
+}

+ 105 - 0
core/src/main/java/org/springframework/security/rolemapping/SimpleRoles2GrantedAuthoritiesMapper.java

@@ -0,0 +1,105 @@
+package org.springframework.security.rolemapping;
+
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+
+import java.util.Locale;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+
+/**
+ * <p>
+ * This class implements the Roles2GrantedAuthoritiesMapper interface by doing a
+ * one-on-one mapping from roles to Acegi GrantedAuthorities. Optionally a
+ * prefix can be added, and the role name can be converted to upper or lower
+ * case.
+ * </p>
+ * 
+ * <p>
+ * By default, the role is prefixed with "ROLE_" unless it already starts with
+ * "ROLE_", and no case conversion is done.
+ * </p>
+ */
+public class SimpleRoles2GrantedAuthoritiesMapper implements Roles2GrantedAuthoritiesMapper, InitializingBean {
+	private String rolePrefix = "ROLE_";
+
+	private boolean convertRoleToUpperCase = false;
+
+	private boolean convertRoleToLowerCase = false;
+
+	private boolean addPrefixIfAlreadyExisting = false;
+
+	/**
+	 * Check whether all properties have been set to correct values.
+	 */
+	public void afterPropertiesSet() throws Exception {
+		Assert.isTrue(!(isConvertRoleToUpperCase() && isConvertRoleToLowerCase()),
+				"Either convertRoleToUpperCase or convertRoleToLowerCase can be set to true, but not both");
+	}
+
+	/**
+	 * Map the given list of roles one-on-one to Acegi GrantedAuthorities.
+	 */
+	public GrantedAuthority[] getGrantedAuthorities(String[] roles) {
+		GrantedAuthority[] result = new GrantedAuthority[roles.length];
+		for (int i = 0; i < roles.length; i++) {
+			result[i] = getGrantedAuthority(roles[i]);
+		}
+		return result;
+	}
+
+	/**
+	 * Map the given role ono-on-one to an Acegi GrantedAuthority, optionally
+	 * doing case conversion and/or adding a prefix.
+	 * 
+	 * @param role
+	 *            The role for which to get a GrantedAuthority
+	 * @return GrantedAuthority representing the given role.
+	 */
+	private GrantedAuthority getGrantedAuthority(String role) {
+		if (isConvertRoleToLowerCase()) {
+			role = role.toLowerCase(Locale.getDefault());
+		} else if (isConvertRoleToUpperCase()) {
+			role = role.toUpperCase(Locale.getDefault());
+		}
+		if (isAddPrefixIfAlreadyExisting() || !role.startsWith(getRolePrefix())) {
+			return new GrantedAuthorityImpl(getRolePrefix() + role);
+		} else {
+			return new GrantedAuthorityImpl(role);
+		}
+	}
+
+	private boolean isConvertRoleToLowerCase() {
+		return convertRoleToLowerCase;
+	}
+
+	public void setConvertRoleToLowerCase(boolean b) {
+		convertRoleToLowerCase = b;
+	}
+
+	private boolean isConvertRoleToUpperCase() {
+		return convertRoleToUpperCase;
+	}
+
+	public void setConvertRoleToUpperCase(boolean b) {
+		convertRoleToUpperCase = b;
+	}
+
+	private String getRolePrefix() {
+		return rolePrefix == null ? "" : rolePrefix;
+	}
+
+	public void setRolePrefix(String string) {
+		rolePrefix = string;
+	}
+
+	private boolean isAddPrefixIfAlreadyExisting() {
+		return addPrefixIfAlreadyExisting;
+	}
+
+	public void setAddPrefixIfAlreadyExisting(boolean b) {
+		addPrefixIfAlreadyExisting = b;
+	}
+
+}

+ 181 - 0
core/src/main/java/org/springframework/security/rolemapping/XmlMappableRolesRetriever.java

@@ -0,0 +1,181 @@
+package org.springframework.security.rolemapping;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jaxen.JaxenException;
+import org.jaxen.dom.DOMXPath;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * This implementation for the MappableRolesRetriever interface retrieves the
+ * list of mappable roles from an XML file.
+ * 
+ * This class is defined as abstract because it is too generic to be used
+ * directly. As this class is usually used to read very specific XML files (e.g.
+ * web.xml, ejb-jar.xml), subclasses should usually define the actual
+ * XPath-expression to use, and define a more specifically named setter for the
+ * XML InputStream (e.g. setWebXmlInputStream).
+ */
+public abstract class XmlMappableRolesRetriever implements MappableRolesRetriever, InitializingBean {
+	private static final Log LOG = LogFactory.getLog(XmlMappableRolesRetriever.class);
+
+	private String[] mappableRoles = null;
+
+	private InputStream xmlInputStream = null;
+
+	private String xpathExpression = null;
+
+	private boolean closeInputStream = true;
+
+	/**
+	 * Check whether all required properties have been set.
+	 */
+	public void afterPropertiesSet() throws Exception {
+		Assert.notNull(xmlInputStream, "An XML InputStream must be set");
+		Assert.notNull(xpathExpression, "An XPath expression must be set");
+		mappableRoles = getMappableRoles(xmlInputStream);
+	}
+
+	public String[] getMappableRoles() {
+		String[] copy = new String[mappableRoles.length];
+		System.arraycopy(mappableRoles, 0, copy, 0, copy.length);
+		return copy;
+	}
+
+	/**
+	 * Get the mappable roles from the specified XML document.
+	 */
+	private String[] getMappableRoles(InputStream aStream) {
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("Reading mappable roles from XML document");
+		}
+		try {
+			Document doc = getDocument(aStream);
+			String[] roles = getMappableRoles(doc);
+			if (LOG.isDebugEnabled()) {
+				LOG.debug("Mappable roles from XML document: " + ArrayUtils.toString(roles));
+			}
+			return roles;
+		} finally {
+			if (closeInputStream) {
+				try {
+					aStream.close();
+				} catch (Exception e) {
+					LOG.debug("Input stream could not be closed", e);
+				}
+			}
+		}
+
+	}
+
+	/**
+	 * @return Document for the specified InputStream
+	 */
+	private Document getDocument(InputStream aStream) {
+		Document doc;
+		try {
+			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+			factory.setValidating(false);
+			DocumentBuilder db = factory.newDocumentBuilder();
+			db.setEntityResolver(new MyEntityResolver());
+			doc = db.parse(new IgnoreCloseInputStream(aStream));
+			return doc;
+		} catch (FactoryConfigurationError e) {
+			throw new RuntimeException("Unable to parse document object", e);
+		} catch (ParserConfigurationException e) {
+			throw new RuntimeException("Unable to parse document object", e);
+		} catch (SAXException e) {
+			throw new RuntimeException("Unable to parse document object", e);
+		} catch (IOException e) {
+			throw new RuntimeException("Unable to parse document object", e);
+		}
+	}
+
+	/**
+	 * @param doc
+	 *            The Document from which to read the list of roles
+	 * @return String[] the list of roles.
+	 * @throws JaxenException
+	 */
+	private String[] getMappableRoles(Document doc) {
+		try {
+			DOMXPath xpath = new DOMXPath(xpathExpression);
+			List roleElements = xpath.selectNodes(doc);
+			String[] roles = new String[roleElements.size()];
+			for (int i = 0; i < roles.length; i++) {
+				roles[i] = ((Node) roleElements.get(i)).getNodeValue();
+			}
+			return roles;
+		} catch (JaxenException e) {
+			throw new RuntimeException("Unable to retrieve mappable roles", e);
+		} catch (DOMException e) {
+			throw new RuntimeException("Unable to retrieve mappable roles", e);
+		}
+	}
+
+	/**
+	 * Subclasses should provide this method with a more specific name (e.g.
+	 * indicating the type of XML file the subclass expects, like
+	 * setWebXmlInputStream).
+	 */
+	protected void setXmlInputStream(InputStream aStream) {
+		this.xmlInputStream = aStream;
+	}
+
+	/**
+	 * Subclasses usually want to set an XPath expression by themselves (e.g.
+	 * not user-configurable). However subclasses may provide configuration
+	 * options to for example choose from a list of predefined XPath expressions
+	 * (e.g. to support multiple versions of the same type of XML file), as such
+	 * we provide a setter instead of mandatory constructor argument.
+	 */
+	protected void setXpathExpression(String anXpathExpression) {
+		xpathExpression = anXpathExpression;
+	}
+
+	/**
+	 * Define whether the provided InputStream must be closed after reading it.
+	 */
+	public void setCloseInputStream(boolean b) {
+		closeInputStream = b;
+	}
+
+	/**
+	 * We do not need to resolve external entities, so just return an empty
+	 * String.
+	 */
+	private static final class MyEntityResolver implements EntityResolver {
+		public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
+			return new InputSource(new StringReader(""));
+		}
+	}
+
+	public static final class IgnoreCloseInputStream extends FilterInputStream {
+		public IgnoreCloseInputStream(InputStream stream) {
+			super(stream);
+		}
+
+		public void close() throws IOException {
+			// do nothing
+		}
+	}
+}

+ 176 - 0
core/src/main/java/org/springframework/security/ui/preauth/AbstractPreAuthenticatedProcessingFilter.java

@@ -0,0 +1,176 @@
+package org.springframework.security.ui.preauth;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationToken;
+import org.springframework.security.AuthenticationManager;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.event.authentication.InteractiveAuthenticationSuccessEvent;
+import org.springframework.security.ui.AuthenticationDetailsSource;
+import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
+import org.springframework.security.ui.AbstractProcessingFilter;
+import org.springframework.security.context.SecurityContextHolder;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
+import org.springframework.util.Assert;
+
+/**
+ * Base class for processing filters that handle pre-authenticated
+ * authentication requests. Subclasses must implement the
+ * getPreAuthenticatedPrincipal() and getPreAuthenticatedCredentials() methods.
+ * <p>
+ * This code is partly based on
+ * {@link org.springframework.security.ui.x509.X509ProcessingFilter}.
+ * </p>
+ */
+public abstract class AbstractPreAuthenticatedProcessingFilter implements Filter, InitializingBean, ApplicationEventPublisherAware {
+	private static final Log LOG = LogFactory.getLog(AbstractPreAuthenticatedProcessingFilter.class);
+
+	private ApplicationEventPublisher eventPublisher = null;
+
+	private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
+
+	private AuthenticationManager authenticationManager = null;
+
+	/**
+	 * Check whether all required properties have been set.
+	 */
+	public void afterPropertiesSet() throws Exception {
+		Assert.notNull(authenticationManager, "An AuthenticationManager must be set");
+	}
+
+	/**
+	 * Try to authenticate a pre-authenticated user with Acegi if the user has
+	 * not yet been authenticated.
+	 */
+	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
+		if (!(request instanceof HttpServletRequest)) {
+			throw new ServletException("Can only process HttpServletRequest");
+		}
+		if (!(response instanceof HttpServletResponse)) {
+			throw new ServletException("Can only process HttpServletResponse");
+		}
+
+		HttpServletRequest httpRequest = (HttpServletRequest) request;
+		HttpServletResponse httpResponse = (HttpServletResponse) response;
+
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("Checking secure context token: " + SecurityContextHolder.getContext().getAuthentication());
+		}
+
+		if (SecurityContextHolder.getContext().getAuthentication() == null) {
+			doAuthenticate(httpRequest, httpResponse);
+		}
+		filterChain.doFilter(request, response);
+	}
+
+	/**
+	 * Do the actual authentication for a pre-authenticated user.
+	 * 
+	 * @param httpRequest
+	 *            The HttpServletRequest object
+	 * @param httpResponse
+	 *            The HttpServletResponse object
+	 */
+	private void doAuthenticate(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
+		Authentication authResult = null;
+
+		Object principal = getPreAuthenticatedPrincipal(httpRequest);
+		Object credentials = getPreAuthenticatedCredentials(httpRequest);
+
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("AbstractPreAuthenticatedProcessingFilter: preAuthenticatedPrincipal=" + principal + ", trying to authenticate");
+		}
+
+		try {
+			PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(principal, credentials);
+			authRequest.setDetails(authenticationDetailsSource.buildDetails(httpRequest));
+			authResult = authenticationManager.authenticate(authRequest);
+			successfulAuthentication(httpRequest, httpResponse, authResult);
+		} catch (AuthenticationException failed) {
+			unsuccessfulAuthentication(httpRequest, httpResponse, failed);
+		}
+	}
+
+	/**
+	 * Puts the <code>Authentication</code> instance returned by the
+	 * authentication manager into the secure context.
+	 */
+	protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("Authentication success: " + authResult);
+		}
+		SecurityContextHolder.getContext().setAuthentication(authResult);
+		// Fire event
+		if (this.eventPublisher != null) {
+			eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
+		}
+	}
+
+	/**
+	 * Ensures the authentication object in the secure context is set to null
+	 * when authentication fails.
+	 */
+	protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
+		SecurityContextHolder.getContext().setAuthentication(null);
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("Updated SecurityContextHolder to contain null Authentication due to exception", failed);
+		}
+		request.getSession().setAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY, failed);
+	}
+
+	/**
+	 * @param anApplicationEventPublisher
+	 *            The ApplicationEventPublisher to use
+	 */
+	public void setApplicationEventPublisher(ApplicationEventPublisher anApplicationEventPublisher) {
+		this.eventPublisher = anApplicationEventPublisher;
+	}
+
+	/**
+	 * @param authenticationDetailsSource
+	 *            The AuthenticationDetailsSource to use
+	 */
+	public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
+		Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
+		this.authenticationDetailsSource = authenticationDetailsSource;
+	}
+
+	/**
+	 * @param authenticationManager
+	 *            The AuthenticationManager to use
+	 */
+	public void setAuthenticationManager(AuthenticationManager authenticationManager) {
+		this.authenticationManager = authenticationManager;
+	}
+
+	/**
+	 * Required method, does nothing.
+	 */
+	public void init(FilterConfig filterConfig) {
+	}
+
+	/**
+	 * Required method, does nothing.
+	 */
+	public void destroy() {
+	}
+
+	protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest);
+
+	protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest httpRequest);
+}

+ 58 - 0
core/src/main/java/org/springframework/security/ui/preauth/PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails.java

@@ -0,0 +1,58 @@
+package org.springframework.security.ui.preauth;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.security.providers.preauth.PreAuthenticatedGrantedAuthoritiesRetriever;
+import org.springframework.security.providers.preauth.PreAuthenticatedGrantedAuthoritiesSetter;
+import org.springframework.security.ui.WebAuthenticationDetails;
+import org.springframework.security.GrantedAuthority;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.util.Assert;
+
+/**
+ * This WebAuthenticationDetails implementation allows for storing a list of
+ * pre-authenticated Granted Authorities.
+ */
+public class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails extends WebAuthenticationDetails implements
+		PreAuthenticatedGrantedAuthoritiesRetriever, PreAuthenticatedGrantedAuthoritiesSetter {
+	public static final long serialVersionUID = 1L;
+
+	private GrantedAuthority[] preAuthenticatedGrantedAuthorities = null;
+
+	public PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails(HttpServletRequest request) {
+		super(request);
+	}
+
+	/**
+	 * @return The String representation of this object.
+	 */
+	public String toString() {
+		StringBuffer sb = new StringBuffer();
+		sb.append(super.toString() + "; ");
+		sb.append("preAuthenticatedGrantedAuthorities: " + StringUtils.join(preAuthenticatedGrantedAuthorities, ", "));
+		return sb.toString();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.springframework.security.providers.preauth.PreAuthenticatedGrantedAuthoritiesRetriever#getPreAuthenticatedGrantedAuthorities()
+	 */
+	public GrantedAuthority[] getPreAuthenticatedGrantedAuthorities() {
+		Assert.notNull(preAuthenticatedGrantedAuthorities, "Pre-authenticated granted authorities have not been set");
+		GrantedAuthority[] result = new GrantedAuthority[preAuthenticatedGrantedAuthorities.length];
+		System.arraycopy(preAuthenticatedGrantedAuthorities, 0, result, 0, result.length);
+		return result;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.springframework.security.providers.preauth.j2ee.PreAuthenticatedGrantedAuthoritiesSetter#setJ2eeBasedGrantedAuthorities()
+	 */
+	public void setPreAuthenticatedGrantedAuthorities(GrantedAuthority[] aJ2eeBasedGrantedAuthorities) {
+		this.preAuthenticatedGrantedAuthorities = new GrantedAuthority[aJ2eeBasedGrantedAuthorities.length];
+		System.arraycopy(aJ2eeBasedGrantedAuthorities, 0, preAuthenticatedGrantedAuthorities, 0, preAuthenticatedGrantedAuthorities.length);
+	}
+}

+ 66 - 0
core/src/main/java/org/springframework/security/ui/preauth/PreAuthenticatedProcesingFilterEntryPoint.java

@@ -0,0 +1,66 @@
+package org.springframework.security.ui.preauth;
+
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.ui.AuthenticationEntryPoint;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.core.Ordered;
+
+/**
+ * <p>
+ * In the pre-authenticated authentication case (unlike CAS, for example) the
+ * user will already have been identified through some external mechanism and a
+ * secure context established by the time the security-enforcement filter is
+ * invoked.
+ * </p>
+ * <p>
+ * Therefore this class isn't actually responsible for the commencement of
+ * authentication, as it is in the case of other providers. It will be called if
+ * the user is rejected by the AbstractPreAuthenticatedProcessingFilter,
+ * resulting in a null authentication.
+ * </p>
+ * <p>
+ * The <code>commence</code> method will always return an
+ * <code>HttpServletResponse.SC_FORBIDDEN</code> (403 error).
+ * </p>
+ * <p>
+ * This code is based on
+ * {@link org.springframework.security.ui.x509.X509ProcessingFilterEntryPoint}.
+ * </p>
+ * 
+ * @see org.springframework.security.ui.ExceptionTranslationFilter
+ */
+public class PreAuthenticatedProcesingFilterEntryPoint implements AuthenticationEntryPoint, Ordered {
+	private static final Log LOG = LogFactory.getLog(PreAuthenticatedProcesingFilterEntryPoint.class);
+
+	private int order = Integer.MAX_VALUE;
+
+	/**
+	 * Always returns a 403 error code to the client.
+	 */
+	public void commence(ServletRequest request, ServletResponse response, AuthenticationException arg2) throws IOException,
+			ServletException {
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("J2EE entry point called. Rejecting access");
+		}
+		HttpServletResponse httpResponse = (HttpServletResponse) response;
+		httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
+	}
+
+	public int getOrder() {
+		return order;
+	}
+
+	public void setOrder(int i) {
+		order = i;
+	}
+
+}

+ 101 - 0
core/src/main/java/org/springframework/security/ui/preauth/j2ee/J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.java

@@ -0,0 +1,101 @@
+package org.springframework.security.ui.preauth.j2ee;
+
+import org.springframework.security.ui.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails;
+import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
+import org.springframework.security.providers.preauth.PreAuthenticatedGrantedAuthoritiesSetter;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.rolemapping.Roles2GrantedAuthoritiesMapper;
+import org.springframework.security.rolemapping.MappableRolesRetriever;
+
+import java.util.ArrayList;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+
+public class J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource extends AuthenticationDetailsSourceImpl implements InitializingBean {
+	private static final Log LOG = LogFactory.getLog(J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.class);
+
+	private String[] j2eeMappableRoles;
+
+	private Roles2GrantedAuthoritiesMapper j2eeUserRoles2GrantedAuthoritiesMapper;
+
+	/**
+	 * Public constructor which overrides the default AuthenticationDetails
+	 * class to be used.
+	 */
+	public J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource() {
+		super.setClazz(PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails.class);
+	}
+
+	/**
+	 * Check that all required properties have been set.
+	 */
+	public void afterPropertiesSet() throws Exception {
+		Assert.notNull(j2eeMappableRoles, "J2EE defined roles not available");
+		Assert.notNull(j2eeUserRoles2GrantedAuthoritiesMapper, "J2EE user roles to granted authorities mapper not set");
+	}
+
+	/**
+	 * Build the authentication details object. If the speficied authentication
+	 * details class implements the PreAuthenticatedGrantedAuthoritiesSetter, a
+	 * list of pre-authenticated Granted Authorities will be set based on the
+	 * J2EE roles for the current user.
+	 * 
+	 * @see org.springframework.security.ui.AuthenticationDetailsSource#buildDetails(javax.servlet.http.HttpServletRequest)
+	 */
+	public Object buildDetails(HttpServletRequest request) {
+		Object result = super.buildDetails(request);
+		if (result instanceof PreAuthenticatedGrantedAuthoritiesSetter) {
+			((PreAuthenticatedGrantedAuthoritiesSetter) result)
+					.setPreAuthenticatedGrantedAuthorities(getJ2eeBasedGrantedAuthorities(request));
+		}
+		return result;
+	}
+
+	/**
+	 * Get a list of Granted Authorities based on the current user's J2EE roles.
+	 * 
+	 * @param request
+	 *            The HttpServletRequest
+	 * @return GrantedAuthority[] mapped from the user's J2EE roles.
+	 */
+	private GrantedAuthority[] getJ2eeBasedGrantedAuthorities(HttpServletRequest request) {
+		ArrayList j2eeUserRolesList = new ArrayList();
+
+		for (int i = 0; i < j2eeMappableRoles.length; i++) {
+			if (request.isUserInRole(j2eeMappableRoles[i])) {
+				j2eeUserRolesList.add(j2eeMappableRoles[i]);
+			}
+		}
+		String[] j2eeUserRoles = new String[j2eeUserRolesList.size()];
+		j2eeUserRoles = (String[]) j2eeUserRolesList.toArray(j2eeUserRoles);
+		GrantedAuthority[] userGas = j2eeUserRoles2GrantedAuthoritiesMapper.getGrantedAuthorities(j2eeUserRoles);
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("J2EE user roles [" + StringUtils.join(j2eeUserRoles) + "] mapped to Granted Authorities: ["
+					+ StringUtils.join(userGas) + "]");
+		}
+		return userGas;
+	}
+
+	/**
+	 * @param aJ2eeMappableRolesRetriever
+	 *            The MappableRolesRetriever to use
+	 */
+	public void setJ2eeMappableRolesRetriever(MappableRolesRetriever aJ2eeMappableRolesRetriever) {
+		this.j2eeMappableRoles = aJ2eeMappableRolesRetriever.getMappableRoles();
+	}
+
+	/**
+	 * @param mapper
+	 *            The Roles2GrantedAuthoritiesMapper to use
+	 */
+	public void setJ2eeUserRoles2GrantedAuthoritiesMapper(Roles2GrantedAuthoritiesMapper mapper) {
+		j2eeUserRoles2GrantedAuthoritiesMapper = mapper;
+	}
+
+}

+ 36 - 0
core/src/main/java/org/springframework/security/ui/preauth/j2ee/J2eePreAuthenticatedProcessingFilter.java

@@ -0,0 +1,36 @@
+package org.springframework.security.ui.preauth.j2ee;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.security.ui.preauth.AbstractPreAuthenticatedProcessingFilter;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This AbstractPreAuthenticatedProcessingFilter implementation is based on the
+ * J2EE container-based authentication mechanism. It will use the J2EE user
+ * principal name as the pre-authenticated principal.
+ */
+public class J2eePreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter {
+	private static final Log LOG = LogFactory.getLog(J2eePreAuthenticatedProcessingFilter.class);
+
+	/**
+	 * Return the J2EE user name.
+	 */
+	protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest) {
+		Object principal = httpRequest.getUserPrincipal() == null ? null : httpRequest.getUserPrincipal().getName();
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("PreAuthenticated J2EE principal: " + principal);
+		}
+		return principal;
+	}
+
+	/**
+	 * For J2EE container-based authentication there is no generic way to
+	 * retrieve the credentials, as such this method returns a fixed dummy
+	 * value.
+	 */
+	protected Object getPreAuthenticatedCredentials(HttpServletRequest httpRequest) {
+		return "N/A";
+	}
+}

+ 52 - 0
core/src/main/java/org/springframework/security/ui/preauth/j2ee/WebXmlMappableRolesRetriever.java

@@ -0,0 +1,52 @@
+package org.springframework.security.ui.preauth.j2ee;
+
+import java.io.InputStream;
+
+import org.springframework.security.rolemapping.XmlMappableRolesRetriever;
+
+/**
+ * <p>
+ * This MappableRolesRetriever implementation reads the list of defined J2EE
+ * roles from a web.xml file. It's functionality is based on the
+ * XmlMappableRolesRetriever base class.
+ * </p>
+ * 
+ * <p>
+ * Example on how to configure this MappableRolesRetriever in the Spring
+ * configuration file:
+ * 
+ * <pre>
+ * 
+ *  
+ *   	&lt;bean id=&quot;j2eeMappableRolesRetriever&quot; class=&quot;org.springframework.security.ui.preauth.j2ee.WebXmlMappableRolesRetriever&quot;&gt;
+ *  		&lt;property name=&quot;webXmlInputStream&quot;&gt;&lt;bean factory-bean=&quot;webXmlResource&quot; factory-method=&quot;getInputStream&quot;/&gt;&lt;/property&gt;
+ *  	&lt;/bean&gt;
+ *  	&lt;bean id=&quot;webXmlResource&quot; class=&quot;org.springframework.web.context.support.ServletContextResource&quot;&gt;
+ *  		&lt;constructor-arg&gt;&lt;ref local=&quot;servletContext&quot;/&gt;&lt;/constructor-arg&gt;
+ *  		&lt;constructor-arg&gt;&lt;value&gt;/WEB-INF/web.xml&lt;/value&gt;&lt;/constructor-arg&gt;
+ *  	&lt;/bean&gt;
+ *  	&lt;bean id=&quot;servletContext&quot; class=&quot;org.springframework.web.context.support.ServletContextFactoryBean&quot;/&gt;
+ *   
+ *  
+ * </pre>
+ * 
+ * </p>
+ */
+public class WebXmlMappableRolesRetriever extends XmlMappableRolesRetriever {
+	private static final String XPATH_EXPR = "/web-app/security-role/role-name/text()";
+
+	/**
+	 * Constructor setting the XPath expression to use
+	 */
+	public WebXmlMappableRolesRetriever() {
+		super.setXpathExpression(XPATH_EXPR);
+	}
+
+	/**
+	 * @param anInputStream
+	 *            The InputStream to read the XML data from
+	 */
+	public void setWebXmlInputStream(InputStream anInputStream) {
+		super.setXmlInputStream(anInputStream);
+	}
+}

+ 102 - 0
core/src/test/java/org/springframework/security/providers/preauth/PreAuthenticatedAuthenticationProviderTests.java

@@ -0,0 +1,102 @@
+package org.springframework.security.providers.preauth;
+
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
+import org.springframework.security.userdetails.User;
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.UsernameNotFoundException;
+import org.springframework.security.Authentication;
+import org.springframework.security.GrantedAuthority;
+
+import junit.framework.TestCase;
+
+/**
+ * 
+ * @author TSARDD
+ * @since 18-okt-2007
+ */
+public class PreAuthenticatedAuthenticationProviderTests extends TestCase {
+	private static final String SUPPORTED_USERNAME = "dummyUser";
+
+	public final void testAfterPropertiesSet() {
+		PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
+		try {
+			provider.afterPropertiesSet();
+			fail("AfterPropertiesSet didn't throw expected exception");
+		} catch (IllegalArgumentException expected) {
+		} catch (Exception unexpected) {
+			fail("AfterPropertiesSet throws unexpected exception");
+		}
+	}
+
+	public final void testAuthenticateInvalidToken() throws Exception {
+		UserDetails ud = new User("dummyUser", "dummyPwd", true, true, true, true, new GrantedAuthority[] {});
+		PreAuthenticatedAuthenticationProvider provider = getProvider(ud);
+		Authentication request = new UsernamePasswordAuthenticationToken("dummyUser", "dummyPwd");
+		Authentication result = provider.authenticate(request);
+		assertNull(result);
+	}
+
+	public final void testAuthenticateKnownUser() throws Exception {
+		UserDetails ud = new User("dummyUser", "dummyPwd", true, true, true, true, new GrantedAuthority[] {});
+		PreAuthenticatedAuthenticationProvider provider = getProvider(ud);
+		Authentication request = new PreAuthenticatedAuthenticationToken("dummyUser", "dummyPwd");
+		Authentication result = provider.authenticate(request);
+		assertNotNull(result);
+		assertEquals(result.getPrincipal(), ud);
+		// @TODO: Add more asserts?
+	}
+
+	public final void testAuthenticateIgnoreCredentials() throws Exception {
+		UserDetails ud = new User("dummyUser1", "dummyPwd1", true, true, true, true, new GrantedAuthority[] {});
+		PreAuthenticatedAuthenticationProvider provider = getProvider(ud);
+		Authentication request = new PreAuthenticatedAuthenticationToken("dummyUser1", "dummyPwd2");
+		Authentication result = provider.authenticate(request);
+		assertNotNull(result);
+		assertEquals(result.getPrincipal(), ud);
+		// @TODO: Add more asserts?
+	}
+
+	public final void testAuthenticateUnknownUser() throws Exception {
+		UserDetails ud = new User("dummyUser1", "dummyPwd", true, true, true, true, new GrantedAuthority[] {});
+		PreAuthenticatedAuthenticationProvider provider = getProvider(ud);
+		Authentication request = new PreAuthenticatedAuthenticationToken("dummyUser2", "dummyPwd");
+		Authentication result = provider.authenticate(request);
+		assertNull(result);
+	}
+
+	public final void testSupportsArbitraryObject() throws Exception {
+		PreAuthenticatedAuthenticationProvider provider = getProvider(null);
+		assertFalse(provider.supports(Authentication.class));
+	}
+
+	public final void testSupportsPreAuthenticatedAuthenticationToken() throws Exception {
+		PreAuthenticatedAuthenticationProvider provider = getProvider(null);
+		assertTrue(provider.supports(PreAuthenticatedAuthenticationToken.class));
+	}
+
+	public void testGetSetOrder() throws Exception {
+		PreAuthenticatedAuthenticationProvider provider = getProvider(null);
+		provider.setOrder(333);
+		assertEquals(provider.getOrder(), 333);
+	}
+
+	private PreAuthenticatedAuthenticationProvider getProvider(UserDetails aUserDetails) throws Exception {
+		PreAuthenticatedAuthenticationProvider result = new PreAuthenticatedAuthenticationProvider();
+		result.setPreAuthenticatedUserDetailsService(getPreAuthenticatedUserDetailsService(aUserDetails));
+		result.afterPropertiesSet();
+		return result;
+	}
+
+	private PreAuthenticatedUserDetailsService getPreAuthenticatedUserDetailsService(final UserDetails aUserDetails) {
+		return new PreAuthenticatedUserDetailsService() {
+			public UserDetails getUserDetails(PreAuthenticatedAuthenticationToken token) throws UsernameNotFoundException {
+				if (aUserDetails != null && aUserDetails.getUsername().equals(token.getName())) {
+					return aUserDetails;
+				} else {
+					return null;
+				}
+			}
+		};
+	}
+
+}

+ 57 - 0
core/src/test/java/org/springframework/security/providers/preauth/PreAuthenticatedAuthenticationTokenTests.java

@@ -0,0 +1,57 @@
+package org.springframework.security.providers.preauth;
+
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.GrantedAuthority;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import junit.framework.TestCase;
+
+/**
+ * 
+ * @author TSARDD
+ * @since 18-okt-2007
+ */
+public class PreAuthenticatedAuthenticationTokenTests extends TestCase {
+
+	public void testPreAuthenticatedAuthenticationTokenRequestWithDetails() {
+		Object principal = "dummyUser";
+		Object credentials = "dummyCredentials";
+		Object details = "dummyDetails";
+		PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, credentials);
+		token.setDetails(details);
+		assertEquals(principal, token.getPrincipal());
+		assertEquals(credentials, token.getCredentials());
+		assertEquals(details, token.getDetails());
+		assertNull(token.getAuthorities());
+	}
+
+	public void testPreAuthenticatedAuthenticationTokenRequestWithoutDetails() {
+		Object principal = "dummyUser";
+		Object credentials = "dummyCredentials";
+		PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, credentials);
+		assertEquals(principal, token.getPrincipal());
+		assertEquals(credentials, token.getCredentials());
+		assertNull(token.getDetails());
+		assertNull(token.getAuthorities());
+	}
+
+	public void testPreAuthenticatedAuthenticationTokenResponse() {
+		Object principal = "dummyUser";
+		Object credentials = "dummyCredentials";
+		GrantedAuthority[] gas = new GrantedAuthority[] { new GrantedAuthorityImpl("Role1") };
+		PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, credentials, gas);
+		assertEquals(principal, token.getPrincipal());
+		assertEquals(credentials, token.getCredentials());
+		assertNull(token.getDetails());
+		assertNotNull(token.getAuthorities());
+		Collection expectedColl = Arrays.asList(gas);
+		Collection resultColl = Arrays.asList(token.getAuthorities());
+		assertTrue("GrantedAuthority collections do not match; result: " + resultColl + ", expected: " + expectedColl, expectedColl
+				.containsAll(resultColl)
+				&& resultColl.containsAll(expectedColl));
+
+	}
+
+}

+ 79 - 0
core/src/test/java/org/springframework/security/providers/preauth/PreAuthenticatedGrantedAuthoritiesUserDetailsServiceTests.java

@@ -0,0 +1,79 @@
+package org.springframework.security.providers.preauth;
+
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.userdetails.UserDetails;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import junit.framework.TestCase;
+
+/**
+ * 
+ * @author TSARDD
+ * @since 18-okt-2007
+ */
+public class PreAuthenticatedGrantedAuthoritiesUserDetailsServiceTests extends TestCase {
+
+	public final void testGetUserDetailsInvalidType() {
+		PreAuthenticatedGrantedAuthoritiesUserDetailsService svc = new PreAuthenticatedGrantedAuthoritiesUserDetailsService();
+		PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken("dummy", "dummy");
+		token.setDetails(new Object());
+		try {
+			svc.getUserDetails(token);
+			fail("Expected exception didn't occur");
+		} catch (IllegalArgumentException expected) {
+		}
+	}
+
+	public final void testGetUserDetailsNoDetails() {
+		PreAuthenticatedGrantedAuthoritiesUserDetailsService svc = new PreAuthenticatedGrantedAuthoritiesUserDetailsService();
+		PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken("dummy", "dummy");
+		token.setDetails(null);
+		try {
+			svc.getUserDetails(token);
+			fail("Expected exception didn't occur");
+		} catch (IllegalArgumentException expected) {
+		}
+	}
+
+	public final void testGetUserDetailsEmptyAuthorities() {
+		final String userName = "dummyUser";
+		final GrantedAuthority[] gas = new GrantedAuthority[] {};
+		testGetUserDetails(userName, gas);
+	}
+
+	public final void testGetUserDetailsWithAuthorities() {
+		final String userName = "dummyUser";
+		final GrantedAuthority[] gas = new GrantedAuthority[] { new GrantedAuthorityImpl("Role1"), new GrantedAuthorityImpl("Role2") };
+		testGetUserDetails(userName, gas);
+	}
+
+	private void testGetUserDetails(final String userName, final GrantedAuthority[] gas) {
+		PreAuthenticatedGrantedAuthoritiesUserDetailsService svc = new PreAuthenticatedGrantedAuthoritiesUserDetailsService();
+		PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(userName, "dummy");
+		token.setDetails(new PreAuthenticatedGrantedAuthoritiesRetriever() {
+			public GrantedAuthority[] getPreAuthenticatedGrantedAuthorities() {
+				return gas;
+			}
+		});
+		UserDetails ud = svc.getUserDetails(token);
+		assertTrue(ud.isAccountNonExpired());
+		assertTrue(ud.isAccountNonLocked());
+		assertTrue(ud.isCredentialsNonExpired());
+		assertTrue(ud.isEnabled());
+		assertEquals(ud.getUsername(), userName);
+
+		//Password is not saved by
+		// PreAuthenticatedGrantedAuthoritiesUserDetailsService
+		//assertEquals(ud.getPassword(),password);
+
+		Collection expectedColl = Arrays.asList(gas);
+		Collection resultColl = Arrays.asList(ud.getAuthorities());
+		assertTrue("GrantedAuthority collections do not match; result: " + resultColl + ", expected: " + expectedColl, expectedColl
+				.containsAll(resultColl)
+				&& resultColl.containsAll(expectedColl));
+	}
+
+}

+ 51 - 0
core/src/test/java/org/springframework/security/providers/preauth/UserDetailsByNameServiceWrapperTests.java

@@ -0,0 +1,51 @@
+package org.springframework.security.providers.preauth;
+
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.UsernameNotFoundException;
+import org.springframework.security.userdetails.UserDetailsService;
+import org.springframework.security.userdetails.User;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.GrantedAuthority;
+
+import junit.framework.TestCase;
+
+import org.springframework.dao.DataAccessException;
+
+/**
+ * 
+ * @author TSARDD
+ * @since 18-okt-2007
+ */
+public class UserDetailsByNameServiceWrapperTests extends TestCase {
+
+	public final void testAfterPropertiesSet() {
+		UserDetailsByNameServiceWrapper svc = new UserDetailsByNameServiceWrapper();
+		try {
+			svc.afterPropertiesSet();
+			fail("AfterPropertiesSet didn't throw expected exception");
+		} catch (IllegalArgumentException expected) {
+		} catch (Exception unexpected) {
+			fail("AfterPropertiesSet throws unexpected exception");
+		}
+	}
+
+	public final void testGetUserDetails() throws Exception {
+		UserDetailsByNameServiceWrapper svc = new UserDetailsByNameServiceWrapper();
+		final User user = new User("dummy", "dummy", true, true, true, true, new GrantedAuthority[] { new GrantedAuthorityImpl("dummy") });
+		svc.setUserDetailsService(new UserDetailsService() {
+			public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException, DataAccessException {
+				if (user != null && user.getUsername().equals(name)) {
+					return user;
+				} else {
+					return null;
+				}
+			}
+		});
+		svc.afterPropertiesSet();
+		UserDetails result1 = svc.getUserDetails(new PreAuthenticatedAuthenticationToken("dummy", "dummy"));
+		assertEquals("Result doesn't match original user", user, result1);
+		UserDetails result2 = svc.getUserDetails(new PreAuthenticatedAuthenticationToken("dummy2", "dummy"));
+		assertNull("Result should have been null", result2);
+	}
+
+}

+ 26 - 0
core/src/test/java/org/springframework/security/rolemapping/SimpleMappableRolesRetrieverTests.java

@@ -0,0 +1,26 @@
+package org.springframework.security.rolemapping;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+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" };
+		SimpleMappableRolesRetriever r = new SimpleMappableRolesRetriever();
+		r.setMappableRoles(roles);
+		String[] result = r.getMappableRoles();
+		Collection resultColl = Arrays.asList(result);
+		Collection rolesColl = Arrays.asList(roles);
+		assertTrue("Role collections do not match; result: " + resultColl + ", expected: " + rolesColl, rolesColl.containsAll(resultColl)
+				&& resultColl.containsAll(rolesColl));
+	}
+
+}

+ 121 - 0
core/src/test/java/org/springframework/security/rolemapping/SimpleRoles2GrantedAuthoritiesMapperTests.java

@@ -0,0 +1,121 @@
+package org.springframework.security.rolemapping;
+
+import org.springframework.security.GrantedAuthority;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+import junit.framework.TestCase;
+
+/**
+ * 
+ * @author TSARDD
+ * @since 18-okt-2007
+ */
+public class SimpleRoles2GrantedAuthoritiesMapperTests extends TestCase {
+
+	public final void testAfterPropertiesSetConvertToUpperAndLowerCase() {
+		SimpleRoles2GrantedAuthoritiesMapper mapper = new SimpleRoles2GrantedAuthoritiesMapper();
+		mapper.setConvertRoleToLowerCase(true);
+		mapper.setConvertRoleToUpperCase(true);
+		try {
+			mapper.afterPropertiesSet();
+			fail("Expected exception not thrown");
+		} catch (IllegalArgumentException expected) {
+		} catch (Exception unexpected) {
+			fail("Unexpected exception: " + unexpected);
+		}
+	}
+
+	public final void testAfterPropertiesSet() {
+		SimpleRoles2GrantedAuthoritiesMapper mapper = new SimpleRoles2GrantedAuthoritiesMapper();
+		try {
+			mapper.afterPropertiesSet();
+		} catch (Exception unexpected) {
+			fail("Unexpected exception: " + unexpected);
+		}
+	}
+
+	public final void testGetGrantedAuthoritiesNoConversion() {
+		String[] roles = { "Role1", "Role2" };
+		String[] expectedGas = { "Role1", "Role2" };
+		SimpleRoles2GrantedAuthoritiesMapper mapper = getDefaultMapper();
+		testGetGrantedAuthorities(mapper, roles, expectedGas);
+	}
+
+	public final void testGetGrantedAuthoritiesToUpperCase() {
+		String[] roles = { "Role1", "Role2" };
+		String[] expectedGas = { "ROLE1", "ROLE2" };
+		SimpleRoles2GrantedAuthoritiesMapper mapper = getDefaultMapper();
+		mapper.setConvertRoleToUpperCase(true);
+		testGetGrantedAuthorities(mapper, roles, expectedGas);
+	}
+
+	public final void testGetGrantedAuthoritiesToLowerCase() {
+		String[] roles = { "Role1", "Role2" };
+		String[] expectedGas = { "role1", "role2" };
+		SimpleRoles2GrantedAuthoritiesMapper mapper = getDefaultMapper();
+		mapper.setConvertRoleToLowerCase(true);
+		testGetGrantedAuthorities(mapper, roles, expectedGas);
+	}
+
+	public final void testGetGrantedAuthoritiesAddPrefixIfAlreadyExisting() {
+		String[] roles = { "Role1", "Role2", "ROLE_Role3" };
+		String[] expectedGas = { "ROLE_Role1", "ROLE_Role2", "ROLE_ROLE_Role3" };
+		SimpleRoles2GrantedAuthoritiesMapper mapper = getDefaultMapper();
+		mapper.setAddPrefixIfAlreadyExisting(true);
+		mapper.setRolePrefix("ROLE_");
+		testGetGrantedAuthorities(mapper, roles, expectedGas);
+	}
+
+	public final void testGetGrantedAuthoritiesDontAddPrefixIfAlreadyExisting1() {
+		String[] roles = { "Role1", "Role2", "ROLE_Role3" };
+		String[] expectedGas = { "ROLE_Role1", "ROLE_Role2", "ROLE_Role3" };
+		SimpleRoles2GrantedAuthoritiesMapper mapper = getDefaultMapper();
+		mapper.setAddPrefixIfAlreadyExisting(false);
+		mapper.setRolePrefix("ROLE_");
+		testGetGrantedAuthorities(mapper, roles, expectedGas);
+	}
+
+	public final void testGetGrantedAuthoritiesDontAddPrefixIfAlreadyExisting2() {
+		String[] roles = { "Role1", "Role2", "role_Role3" };
+		String[] expectedGas = { "ROLE_Role1", "ROLE_Role2", "ROLE_role_Role3" };
+		SimpleRoles2GrantedAuthoritiesMapper mapper = getDefaultMapper();
+		mapper.setAddPrefixIfAlreadyExisting(false);
+		mapper.setRolePrefix("ROLE_");
+		testGetGrantedAuthorities(mapper, roles, expectedGas);
+	}
+
+	public final void testGetGrantedAuthoritiesCombination1() {
+		String[] roles = { "Role1", "Role2", "role_Role3" };
+		String[] expectedGas = { "ROLE_ROLE1", "ROLE_ROLE2", "ROLE_ROLE3" };
+		SimpleRoles2GrantedAuthoritiesMapper mapper = getDefaultMapper();
+		mapper.setAddPrefixIfAlreadyExisting(false);
+		mapper.setConvertRoleToUpperCase(true);
+		mapper.setRolePrefix("ROLE_");
+		testGetGrantedAuthorities(mapper, roles, expectedGas);
+	}
+
+	private void testGetGrantedAuthorities(SimpleRoles2GrantedAuthoritiesMapper mapper, String[] roles, String[] expectedGas) {
+		GrantedAuthority[] result = mapper.getGrantedAuthorities(roles);
+		Collection resultColl = new ArrayList(result.length);
+		for (int i = 0; i < result.length; i++) {
+			resultColl.add(result[i].getAuthority());
+		}
+		Collection expectedColl = Arrays.asList(expectedGas);
+		assertTrue("Role collections do not match; result: " + resultColl + ", expected: " + expectedColl, expectedColl
+				.containsAll(resultColl)
+				&& resultColl.containsAll(expectedColl));
+	}
+
+	private SimpleRoles2GrantedAuthoritiesMapper getDefaultMapper() {
+		SimpleRoles2GrantedAuthoritiesMapper mapper = new SimpleRoles2GrantedAuthoritiesMapper();
+		mapper.setRolePrefix("");
+		mapper.setConvertRoleToLowerCase(false);
+		mapper.setConvertRoleToUpperCase(false);
+		mapper.setAddPrefixIfAlreadyExisting(false);
+		return mapper;
+	}
+
+}

+ 100 - 0
core/src/test/java/org/springframework/security/rolemapping/XmlMappableRolesRetrieverTests.java

@@ -0,0 +1,100 @@
+package org.springframework.security.rolemapping;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collection;
+
+import junit.framework.TestCase;
+
+/**
+ * 
+ * @author TSARDD
+ * @since 18-okt-2007
+ */
+public class XmlMappableRolesRetrieverTests extends TestCase {
+	private static final String DEFAULT_XML = "<roles><role>Role1</role><role>Role2</role></roles>";
+
+	private static final String DEFAULT_XPATH = "/roles/role/text()";
+
+	private static final String[] DEFAULT_EXPECTED_ROLES = new String[] { "Role1", "Role2" };
+
+	public final void testAfterPropertiesSetException() {
+		TestXmlMappableRolesRetriever t = new TestXmlMappableRolesRetriever();
+		try {
+			t.afterPropertiesSet();
+			fail("AfterPropertiesSet didn't throw expected exception");
+		} catch (IllegalArgumentException expected) {
+		} catch (Exception unexpected) {
+			fail("AfterPropertiesSet throws unexpected exception");
+		}
+	}
+
+	public void testGetMappableRoles() {
+		XmlMappableRolesRetriever r = getXmlMappableRolesRetriever(true, getDefaultInputStream(), DEFAULT_XPATH);
+		String[] resultRoles = r.getMappableRoles();
+		assertNotNull("Result roles should not be null", resultRoles);
+		assertTrue("Number of result roles doesn't match expected number of roles", resultRoles.length == DEFAULT_EXPECTED_ROLES.length);
+		Collection resultRolesColl = Arrays.asList(resultRoles);
+		Collection expectedRolesColl = Arrays.asList(DEFAULT_EXPECTED_ROLES);
+		assertTrue("Role collections do not match", expectedRolesColl.containsAll(resultRolesColl)
+				&& resultRolesColl.containsAll(expectedRolesColl));
+	}
+
+	public void testCloseInputStream() {
+		testCloseInputStream(true);
+	}
+
+	public void testDontCloseInputStream() {
+		testCloseInputStream(false);
+	}
+
+	private void testCloseInputStream(boolean closeAfterRead) {
+		CloseableByteArrayInputStream is = getDefaultInputStream();
+		XmlMappableRolesRetriever r = getXmlMappableRolesRetriever(closeAfterRead, is, DEFAULT_XPATH);
+		r.getMappableRoles();
+		assertEquals(is.isClosed(), closeAfterRead);
+	}
+
+	private XmlMappableRolesRetriever getXmlMappableRolesRetriever(boolean closeInputStream, InputStream is, String xpath) {
+		XmlMappableRolesRetriever result = new TestXmlMappableRolesRetriever();
+		result.setCloseInputStream(closeInputStream);
+		result.setXmlInputStream(is);
+		result.setXpathExpression(xpath);
+		try {
+			result.afterPropertiesSet();
+		} catch (Exception e) {
+			fail("Unexpected exception" + e.toString());
+		}
+		return result;
+	}
+
+	private CloseableByteArrayInputStream getDefaultInputStream() {
+		return getInputStream(DEFAULT_XML);
+	}
+
+	private CloseableByteArrayInputStream getInputStream(String data) {
+		return new CloseableByteArrayInputStream(data.getBytes());
+	}
+
+	private static final class TestXmlMappableRolesRetriever extends XmlMappableRolesRetriever {
+	}
+
+	private static final class CloseableByteArrayInputStream extends ByteArrayInputStream {
+		private boolean closed = false;
+
+		public CloseableByteArrayInputStream(byte[] buf) {
+			super(buf);
+		}
+
+		public void close() throws IOException {
+			super.close();
+			closed = true;
+		}
+
+		public boolean isClosed() {
+			return closed;
+		}
+	}
+}

+ 72 - 0
core/src/test/java/org/springframework/security/ui/preauth/PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetailsTests.java

@@ -0,0 +1,72 @@
+package org.springframework.security.ui.preauth;
+
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.GrantedAuthority;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.mock.web.MockHttpServletRequest;
+
+/**
+ * 
+ * @author TSARDD
+ * @since 18-okt-2007
+ */
+public class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetailsTests extends TestCase {
+
+    public final void testToString() {
+		PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails details = new PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails(
+				getRequest("testUser", new String[] {}));
+		GrantedAuthority[] gas = new GrantedAuthority[] { new GrantedAuthorityImpl("Role1"), new GrantedAuthorityImpl("Role2") };
+		details.setPreAuthenticatedGrantedAuthorities(gas);
+		String toString = details.toString();
+		assertTrue("toString doesn't contain Role1", StringUtils.contains(toString, "Role1"));
+		assertTrue("toString doesn't contain Role2", StringUtils.contains(toString, "Role2"));
+	}
+
+	public final void testGetSetPreAuthenticatedGrantedAuthorities() {
+		PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails details = new PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails(
+				getRequest("testUser", new String[] {}));
+		GrantedAuthority[] gas = new GrantedAuthority[] { new GrantedAuthorityImpl("Role1"), new GrantedAuthorityImpl("Role2") };
+		Collection expectedGas = Arrays.asList(gas);
+
+		details.setPreAuthenticatedGrantedAuthorities(gas);
+		Collection returnedGas = Arrays.asList(details.getPreAuthenticatedGrantedAuthorities());
+		assertTrue("Collections do not contain same elements; expected: " + expectedGas + ", returned: " + returnedGas, expectedGas
+				.containsAll(returnedGas)
+				&& returnedGas.containsAll(expectedGas));
+	}
+
+	public final void testGetWithoutSetPreAuthenticatedGrantedAuthorities() {
+		PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails details = new PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails(
+				getRequest("testUser", new String[] {}));
+		try {
+			GrantedAuthority[] gas = details.getPreAuthenticatedGrantedAuthorities();
+			fail("Expected exception didn't occur");
+		} catch (IllegalArgumentException expected) {
+		} catch (Exception unexpected) {
+			fail("Unexpected exception: " + unexpected.toString());
+		}
+	}
+	
+	private final HttpServletRequest getRequest(final String userName,final String[] aRoles)
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest() {
+			private Set roles = new HashSet(Arrays.asList(aRoles));
+			public boolean isUserInRole(String arg0) {
+				return roles.contains(arg0);
+			}
+		};
+		req.setRemoteUser(userName);
+		return req;
+	}
+
+}

+ 42 - 0
core/src/test/java/org/springframework/security/ui/preauth/PreAuthenticatedProcesingFilterEntryPointTests.java

@@ -0,0 +1,42 @@
+package org.springframework.security.ui.preauth;
+
+import org.springframework.security.AuthenticationCredentialsNotFoundException;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestCase;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+/**
+ * 
+ * @author TSARDD
+ * @since 18-okt-2007
+ */
+public class PreAuthenticatedProcesingFilterEntryPointTests extends TestCase {
+
+	public void testGetSetOrder() {
+		PreAuthenticatedProcesingFilterEntryPoint fep = new PreAuthenticatedProcesingFilterEntryPoint();
+		fep.setOrder(333);
+		assertEquals(fep.getOrder(), 333);
+	}
+	
+	public void testCommence() {
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		MockHttpServletResponse resp = new MockHttpServletResponse();
+		PreAuthenticatedProcesingFilterEntryPoint fep = new PreAuthenticatedProcesingFilterEntryPoint();
+		try {
+			fep.commence(req,resp,new AuthenticationCredentialsNotFoundException("test"));
+			assertEquals("Incorrect status",resp.getStatus(),HttpServletResponse.SC_FORBIDDEN);
+		} catch (IOException e) {
+			fail("Unexpected exception thrown: "+e);
+		} catch (ServletException e) {
+			fail("Unexpected exception thrown: "+e);
+		}
+		
+	}
+}

+ 79 - 0
core/src/test/java/org/springframework/security/ui/preauth/PreAuthenticatedProcessingFilterTests.java

@@ -0,0 +1,79 @@
+package org.springframework.security.ui.preauth;
+
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.MockAuthenticationManager;
+
+import javax.servlet.http.HttpServletRequest;
+
+import junit.framework.TestCase;
+
+import org.springframework.mock.web.MockFilterChain;
+import org.springframework.mock.web.MockFilterConfig;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+public class PreAuthenticatedProcessingFilterTests extends TestCase {
+	protected void setUp() throws Exception {
+		SecurityContextHolder.clearContext();
+	}
+	
+	public void testAfterPropertiesSet()
+	{
+		ConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();
+		try {
+			filter.afterPropertiesSet();
+			fail("AfterPropertiesSet didn't throw expected exception");
+		} catch (IllegalArgumentException expected) {
+		} catch (Exception unexpected) {
+			fail("AfterPropertiesSet throws unexpected exception");
+		}
+	}
+	
+	public void testInit() throws Exception
+	{
+		getFilter(true).init(new MockFilterConfig());
+		// Init doesn't do anything, so nothing to test
+	}
+	
+	public void testDestroy() throws Exception
+	{
+		getFilter(true).destroy();
+		// Destroy doesn't do anything, so nothing to test
+	}
+
+	public final void testDoFilterAuthenticated() throws Exception
+	{
+		testDoFilter(true);
+	}
+
+	public final void testDoFilterUnauthenticated() throws Exception
+	{
+		testDoFilter(false);
+	}
+	
+	private final void testDoFilter(boolean grantAccess) throws Exception
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		MockHttpServletResponse res = new MockHttpServletResponse();
+		getFilter(grantAccess).doFilter(req,res,new MockFilterChain());
+		assertEquals(grantAccess,null!= SecurityContextHolder.getContext().getAuthentication());
+	}
+	
+	private static final ConcretePreAuthenticatedProcessingFilter getFilter(boolean grantAccess) throws Exception
+	{
+		ConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();
+		filter.setAuthenticationManager(new MockAuthenticationManager(grantAccess));
+		filter.afterPropertiesSet();
+		return filter;
+	}
+	
+	private static final class ConcretePreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter
+	{
+		protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest) {
+			return "testPrincipal";
+		}
+		protected Object getPreAuthenticatedCredentials(HttpServletRequest httpRequest) {
+			return "testCredentials";
+		}
+	}
+}

+ 149 - 0
core/src/test/java/org/springframework/security/ui/preauth/j2ee/J2eeBasedPreAuthenticatedWebAuthenticationDetailsSourceTests.java

@@ -0,0 +1,149 @@
+package org.springframework.security.ui.preauth.j2ee;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import junit.framework.TestCase;
+
+import org.springframework.security.rolemapping.MappableRolesRetriever;
+import org.springframework.security.rolemapping.Roles2GrantedAuthoritiesMapper;
+import org.springframework.security.rolemapping.SimpleMappableRolesRetriever;
+import org.springframework.security.rolemapping.SimpleRoles2GrantedAuthoritiesMapper;
+import org.springframework.security.ui.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails;
+import org.springframework.security.GrantedAuthority;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+
+/**
+ * 
+ * @author TSARDD
+ * @since 18-okt-2007
+ */
+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;
+		GrantedAuthority[] gas = details.getPreAuthenticatedGrantedAuthorities();
+		assertNotNull("Granted authorities should not be null", gas);
+		assertTrue("Number of granted authorities should be " + expectedRoles.length, gas.length == expectedRoles.length);
+
+		Collection expectedRolesColl = Arrays.asList(expectedRoles);
+		Collection gasRolesSet = new HashSet();
+		for (int i = 0; i < gas.length; i++) {
+			gasRolesSet.add(gas[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.setJ2eeMappableRolesRetriever(getMappableRolesRetriever(mappedRoles));
+		result.setJ2eeUserRoles2GrantedAuthoritiesMapper(getJ2eeUserRoles2GrantedAuthoritiesMapper());
+		result.setClazz(PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails.class);
+
+		try {
+			result.afterPropertiesSet();
+		} catch (Exception expected) {
+			fail("AfterPropertiesSet throws unexpected exception");
+		}
+		return result;
+	}
+
+	private MappableRolesRetriever getMappableRolesRetriever(String[] mappedRoles) {
+		SimpleMappableRolesRetriever result = new SimpleMappableRolesRetriever();
+		result.setMappableRoles(mappedRoles);
+		return result;
+	}
+
+	private Roles2GrantedAuthoritiesMapper getJ2eeUserRoles2GrantedAuthoritiesMapper() {
+		SimpleRoles2GrantedAuthoritiesMapper result = new SimpleRoles2GrantedAuthoritiesMapper();
+		result.setAddPrefixIfAlreadyExisting(false);
+		result.setConvertRoleToLowerCase(false);
+		result.setConvertRoleToUpperCase(false);
+		result.setRolePrefix("");
+		return result;
+	}
+	
+	private final HttpServletRequest getRequest(final String userName,final String[] aRoles)
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest() {
+			private Set roles = new HashSet(Arrays.asList(aRoles));
+			public boolean isUserInRole(String arg0) {
+				return roles.contains(arg0);
+			}
+		};
+		req.setRemoteUser(userName);
+		return req;
+	}
+}

+ 49 - 0
core/src/test/java/org/springframework/security/ui/preauth/j2ee/J2eePreAuthenticatedProcessingFilterTests.java

@@ -0,0 +1,49 @@
+package org.springframework.security.ui.preauth.j2ee;
+
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import junit.framework.TestCase;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+
+/**
+ * 
+ * @author TSARDD
+ * @since 18-okt-2007
+ */
+public class J2eePreAuthenticatedProcessingFilterTests extends TestCase {
+
+    public final void testGetPreAuthenticatedPrincipal() {
+		String user = "testUser";
+		assertEquals(user, new J2eePreAuthenticatedProcessingFilter().getPreAuthenticatedPrincipal(
+			getRequest(user,new String[] {})));
+	}
+
+	public final void testGetPreAuthenticatedCredentials() {
+		assertEquals("N/A", new J2eePreAuthenticatedProcessingFilter().getPreAuthenticatedCredentials(
+			getRequest("testUser", new String[] {})));
+	}
+	
+	private final HttpServletRequest getRequest(final String aUserName,final String[] aRoles)
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest() {
+			private Set roles = new HashSet(Arrays.asList(aRoles));
+			public boolean isUserInRole(String arg0) {
+				return roles.contains(arg0);
+			}
+		};
+		req.setRemoteUser(aUserName);
+		req.setUserPrincipal(new Principal() {
+			public String getName() {
+				return aUserName;
+			}
+		});
+		return req;
+	}
+
+}

+ 34 - 0
core/src/test/java/org/springframework/security/ui/preauth/j2ee/WebXmlJ2eeDefinedRolesRetrieverTests.java

@@ -0,0 +1,34 @@
+package org.springframework.security.ui.preauth.j2ee;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+public class WebXmlJ2eeDefinedRolesRetrieverTests extends TestCase {
+
+	public final void testRole1To4Roles() throws Exception {
+		final List ROLE1TO4_EXPECTED_ROLES = Arrays.asList(new String[] { "Role1", "Role2", "Role3", "Role4" });
+		InputStream role1to4InputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("webxml/Role1-4.web.xml");
+		WebXmlMappableRolesRetriever rolesRetriever = new WebXmlMappableRolesRetriever();
+		rolesRetriever.setWebXmlInputStream(role1to4InputStream);
+		rolesRetriever.afterPropertiesSet();
+		String[] j2eeRoles = rolesRetriever.getMappableRoles();
+		assertNotNull(j2eeRoles);
+		List j2eeRolesList = Arrays.asList(j2eeRoles);
+		assertTrue("J2eeRoles expected size: " + ROLE1TO4_EXPECTED_ROLES.size() + ", actual size: " + j2eeRolesList.size(), j2eeRolesList
+				.size() == ROLE1TO4_EXPECTED_ROLES.size());
+		assertTrue("J2eeRoles expected contents (arbitrary order): " + ROLE1TO4_EXPECTED_ROLES + ", actual content: " + j2eeRolesList,
+				j2eeRolesList.containsAll(ROLE1TO4_EXPECTED_ROLES));
+	}
+
+	public final void testGetZeroJ2eeRoles() throws Exception {
+		InputStream noRolesInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("webxml/NoRoles.web.xml");
+		WebXmlMappableRolesRetriever rolesRetriever = new WebXmlMappableRolesRetriever();
+		rolesRetriever.setWebXmlInputStream(noRolesInputStream);
+		rolesRetriever.afterPropertiesSet();
+		String[] j2eeRoles = rolesRetriever.getMappableRoles();
+		assertTrue("J2eeRoles expected size: 0, actual size: " + j2eeRoles.length, j2eeRoles.length == 0);
+	}
+}