Explorar el Código

SEC-935: Support for OpenID attribute exchange and changes to namespace syntax to allow simple configuration of attributes to request.

Luke Taylor hace 16 años
padre
commit
48988bde84
Se han modificado 16 ficheros con 334 adiciones y 116 borrados
  1. 2 0
      config/src/main/java/org/springframework/security/config/Elements.java
  2. 29 0
      config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java
  3. 3 2
      config/src/main/java/org/springframework/security/config/method/InterceptMethodsBeanDefinitionDecorator.java
  4. 16 1
      config/src/main/resources/org/springframework/security/config/spring-security-3.0.rnc
  5. 21 0
      config/src/main/resources/org/springframework/security/config/spring-security-3.0.xsd
  6. 32 4
      config/src/test/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParserTests.java
  7. 3 0
      config/src/test/java/org/springframework/security/config/method/InterceptMethodsBeanDefinitionDecoratorTests.java
  8. 79 31
      openid/src/main/java/org/springframework/security/openid/OpenID4JavaConsumer.java
  9. 93 0
      openid/src/main/java/org/springframework/security/openid/OpenIDAttribute.java
  10. 17 4
      openid/src/main/java/org/springframework/security/openid/OpenIDAuthenticationProvider.java
  11. 27 7
      openid/src/main/java/org/springframework/security/openid/OpenIDAuthenticationToken.java
  12. 0 6
      openid/src/main/java/org/springframework/security/openid/OpenIDConsumer.java
  13. 0 16
      openid/src/test/java/org/springframework/security/openid/MockOpenIDConsumer.java
  14. 6 6
      openid/src/test/java/org/springframework/security/openid/OpenIDAuthenticationProviderTests.java
  15. 0 38
      openid/src/test/java/org/springframework/security/openid/OpenIDAuthenticationTokenTests.java
  16. 6 1
      samples/openid/src/main/webapp/WEB-INF/applicationContext-security.xml

+ 2 - 0
config/src/main/java/org/springframework/security/config/Elements.java

@@ -32,6 +32,8 @@ public abstract class Elements {
     public static final String LOGOUT = "logout";
     public static final String FORM_LOGIN = "form-login";
     public static final String OPENID_LOGIN = "openid-login";
+    public static final String OPENID_ATTRIBUTE_EXCHANGE = "attribute-exchange";
+    public static final String OPENID_ATTRIBUTE = "openid-attribute";
     public static final String BASIC_AUTH = "http-basic";
     public static final String REMEMBER_ME = "remember-me";
     public static final String ANONYMOUS = "anonymous";

+ 29 - 0
config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java

@@ -142,6 +142,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
 
     static final String OPEN_ID_AUTHENTICATION_PROCESSING_FILTER_CLASS = "org.springframework.security.openid.OpenIDAuthenticationProcessingFilter";
     static final String OPEN_ID_AUTHENTICATION_PROVIDER_CLASS = "org.springframework.security.openid.OpenIDAuthenticationProvider";
+    static final String OPEN_ID_CONSUMER_CLASS = "org.springframework.security.openid.OpenID4JavaConsumer";
+    static final String OPEN_ID_ATTRIBUTE_CLASS = "org.springframework.security.openid.OpenIDAttribute";
     static final String AUTHENTICATION_PROCESSING_FILTER_CLASS = "org.springframework.security.web.authentication.UsernamePasswordAuthenticationProcessingFilter";
 
     static final String EXPRESSION_FIMDS_CLASS = "org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource";
@@ -1004,6 +1006,33 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
             parser.parse(openIDLoginElt, pc);
             openIDFilter = parser.getFilterBean();
             openIDEntryPoint = parser.getEntryPointBean();
+
+            Element attrExElt = DomUtils.getChildElementByTagName(openIDLoginElt, Elements.OPENID_ATTRIBUTE_EXCHANGE);
+
+            if (attrExElt != null) {
+                // Set up the consumer with the required attribute list
+                BeanDefinitionBuilder consumerBldr = BeanDefinitionBuilder.rootBeanDefinition(OPEN_ID_CONSUMER_CLASS);
+                ManagedList<BeanDefinition> attributes = new ManagedList<BeanDefinition> ();
+                for (Element attElt : DomUtils.getChildElementsByTagName(attrExElt, Elements.OPENID_ATTRIBUTE)) {
+                    String name = attElt.getAttribute("name");
+                    String type = attElt.getAttribute("type");
+                    String required = attElt.getAttribute("required");
+                    String count = attElt.getAttribute("count");
+                    BeanDefinitionBuilder attrBldr = BeanDefinitionBuilder.rootBeanDefinition(OPEN_ID_ATTRIBUTE_CLASS);
+                    attrBldr.addConstructorArgValue(name);
+                    attrBldr.addConstructorArgValue(type);
+                    if (StringUtils.hasLength(required)) {
+                        attrBldr.addPropertyValue("required", Boolean.valueOf(required));
+                    }
+
+                    if (StringUtils.hasLength(count)) {
+                        attrBldr.addPropertyValue("count", Integer.parseInt(count));
+                    }
+                    attributes.add(attrBldr.getBeanDefinition());
+                }
+                consumerBldr.addConstructorArgValue(attributes);
+                openIDFilter.getPropertyValues().addPropertyValue("consumer", consumerBldr.getBeanDefinition());
+            }
         }
 
         if (openIDFilter != null) {

+ 3 - 2
config/src/main/java/org/springframework/security/config/method/InterceptMethodsBeanDefinitionDecorator.java

@@ -88,8 +88,9 @@ class InternalInterceptMethodsBeanDefinitionDecorator extends AbstractIntercepto
             mappings.put(methodName, SecurityConfig.createList(tokens));
         }
 
-        // TODO: Use a bean for the metadata source
-        interceptor.addPropertyValue("securityMetadataSource", new MapBasedMethodSecurityMetadataSource(mappings));
+        BeanDefinition metadataSource = new RootBeanDefinition(MapBasedMethodSecurityMetadataSource.class);
+        metadataSource.getConstructorArgumentValues().addGenericArgumentValue(mappings);
+        interceptor.addPropertyValue("securityMetadataSource", metadataSource);
 
         return interceptor.getBeanDefinition();
     }

+ 16 - 1
config/src/main/resources/org/springframework/security/config/spring-security-3.0.rnc

@@ -360,8 +360,23 @@ form-login.attlist &=
 
 openid-login = 
     ## Sets up form login for authentication with an Open ID identity
-    element openid-login {form-login.attlist, user-service-ref?, empty}
+    element openid-login {form-login.attlist, user-service-ref?, attribute-exchange?}
 
+attribute-exchange =
+    element attribute-exchange {openid-attribute+}
+
+openid-attribute =
+    element openid-attribute {openid-attribute.attlist}
+
+openid-attribute.attlist &=
+    attribute name {xsd:token}
+openid-attribute.attlist &=
+    attribute type {xsd:token}
+openid-attribute.attlist &=
+    attribute required {boolean}?
+openid-attribute.attlist &=
+    attribute count {xsd:int}?
+    
 
 filter-chain-map =
     ## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap

+ 21 - 0
config/src/main/resources/org/springframework/security/config/spring-security-3.0.xsd

@@ -735,6 +735,9 @@
                      identity</xs:documentation>
                </xs:annotation>
                <xs:complexType>
+                  <xs:sequence>
+                     <xs:element minOccurs="0" ref="security:attribute-exchange"/>
+                  </xs:sequence>
                   <xs:attributeGroup ref="security:form-login.attlist"/>
                   <xs:attribute name="user-service-ref" type="xs:token">
                      <xs:annotation>
@@ -1104,6 +1107,24 @@
          </xs:annotation>
       </xs:attribute>
    </xs:attributeGroup>
+   <xs:element name="attribute-exchange">
+      <xs:complexType>
+         <xs:sequence>
+            <xs:element maxOccurs="unbounded" ref="security:openid-attribute"/>
+         </xs:sequence>
+      </xs:complexType>
+   </xs:element>
+   <xs:element name="openid-attribute">
+      <xs:complexType>
+         <xs:attributeGroup ref="security:openid-attribute.attlist"/>
+      </xs:complexType>
+   </xs:element>
+   <xs:attributeGroup name="openid-attribute.attlist">
+      <xs:attribute name="name" use="required" type="xs:token"/>
+      <xs:attribute name="type" use="required" type="xs:token"/>
+      <xs:attribute name="required" type="xs:token"/>
+      <xs:attribute name="count" type="xs:int"/>
+   </xs:attributeGroup>
    <xs:element name="filter-chain-map">
       <xs:annotation>
          <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a

+ 32 - 4
config/src/test/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParserTests.java

@@ -6,10 +6,10 @@ import static org.springframework.security.config.ConfigTestUtils.AUTH_PROVIDER_
 import static org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.*;
 
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.ArrayList;
 import java.util.regex.Pattern;
 
 import javax.servlet.Filter;
@@ -36,6 +36,8 @@ import org.springframework.security.config.BeanIds;
 import org.springframework.security.config.PostProcessedMockUserDetailsService;
 import org.springframework.security.config.util.InMemoryXmlApplicationContext;
 import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.openid.OpenID4JavaConsumer;
+import org.springframework.security.openid.OpenIDAttribute;
 import org.springframework.security.openid.OpenIDAuthenticationProcessingFilter;
 import org.springframework.security.openid.OpenIDAuthenticationProvider;
 import org.springframework.security.util.FieldUtils;
@@ -51,12 +53,12 @@ import org.springframework.security.web.access.intercept.FilterInvocationSecurit
 import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
 import org.springframework.security.web.authentication.AnonymousProcessingFilter;
 import org.springframework.security.web.authentication.AuthenticationFailureHandler;
-import org.springframework.security.web.authentication.RememberMeServices;
-import org.springframework.security.web.authentication.UsernamePasswordAuthenticationProcessingFilter;
-import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
 import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
+import org.springframework.security.web.authentication.RememberMeServices;
 import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
 import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationProcessingFilter;
 import org.springframework.security.web.authentication.WebAuthenticationDetails;
 import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter;
 import org.springframework.security.web.authentication.logout.LogoutFilter;
@@ -959,6 +961,32 @@ public class HttpSecurityBeanDefinitionParserTests {
                 "</http>", appContext);
     }
 
+    @SuppressWarnings("unchecked")
+    @Test
+    public void openIDWithAttributeExchangeConfigurationIsParsedCorrectly() throws Exception {
+        setContext(
+                "<http>" +
+                "   <openid-login>" +
+                "      <attribute-exchange>" +
+                "          <openid-attribute name='nickname' type='http://schema.openid.net/namePerson/friendly'/>" +
+                "          <openid-attribute name='email' type='http://schema.openid.net/contact/email' required='true' count='2'/>" +
+                "      </attribute-exchange>" +
+                "   </openid-login>" +
+                "</http>" +
+                AUTH_PROVIDER_XML);
+        OpenIDAuthenticationProcessingFilter apf = (OpenIDAuthenticationProcessingFilter) getFilter(OpenIDAuthenticationProcessingFilter.class);
+
+        OpenID4JavaConsumer consumer = (OpenID4JavaConsumer) FieldUtils.getFieldValue(apf, "consumer");
+        List<OpenIDAttribute> attributes = (List<OpenIDAttribute>) FieldUtils.getFieldValue(consumer, "attributesToFetch");
+        assertEquals(2, attributes.size());
+        assertEquals("nickname", attributes.get(0).getName());
+        assertEquals("http://schema.openid.net/namePerson/friendly", attributes.get(0).getType());
+        assertFalse(attributes.get(0).isRequired());
+        assertTrue(attributes.get(1).isRequired());
+        assertEquals(2, attributes.get(1).getCount());
+    }
+
+
     private void setContext(String context) {
         appContext = new InMemoryXmlApplicationContext(context);
     }

+ 3 - 0
config/src/test/java/org/springframework/security/config/method/InterceptMethodsBeanDefinitionDecoratorTests.java

@@ -40,7 +40,10 @@ public class InterceptMethodsBeanDefinitionDecoratorTests {
 
     @Test
     public void targetDoesntLoseApplicationListenerInterface() {
+        assertEquals(1, appContext.getBeansOfType(ApplicationListener.class).size());
+        assertEquals(1, appContext.getBeanNamesForType(ApplicationListener.class).length);
         appContext.publishEvent(new AuthenticationSuccessEvent(new TestingAuthenticationToken("user", "")));
+
         assertTrue(target instanceof ApplicationListener);
     }
 

+ 79 - 31
openid/src/main/java/org/springframework/security/openid/OpenID4JavaConsumer.java

@@ -14,51 +14,63 @@
  */
 package org.springframework.security.openid;
 
-import org.openid4java.association.AssociationException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.openid4java.association.AssociationException;
 import org.openid4java.consumer.ConsumerException;
 import org.openid4java.consumer.ConsumerManager;
 import org.openid4java.consumer.VerificationResult;
-
 import org.openid4java.discovery.DiscoveryException;
 import org.openid4java.discovery.DiscoveryInformation;
 import org.openid4java.discovery.Identifier;
-
 import org.openid4java.message.AuthRequest;
+import org.openid4java.message.Message;
 import org.openid4java.message.MessageException;
+import org.openid4java.message.MessageExtension;
 import org.openid4java.message.ParameterList;
-
-import java.util.List;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
+import org.openid4java.message.ax.AxMessage;
+import org.openid4java.message.ax.FetchRequest;
+import org.openid4java.message.ax.FetchResponse;
 
 
 /**
- *
  * @author Ray Krueger
+ * @version $Id$
  */
 public class OpenID4JavaConsumer implements OpenIDConsumer {
+    private static final String DISCOVERY_INFO_KEY = DiscoveryInformation.class.getName();
+
     //~ Instance fields ================================================================================================
 
+    protected final Log logger = LogFactory.getLog(getClass());
+
     private final ConsumerManager consumerManager;
+    private List<OpenIDAttribute> attributesToFetch = Collections.emptyList();
 
     //~ Constructors ===================================================================================================
 
-    public OpenID4JavaConsumer(ConsumerManager consumerManager) {
-        this.consumerManager = consumerManager;
-    }
-
     public OpenID4JavaConsumer() throws ConsumerException {
-        this(new ConsumerManager());
+        this.consumerManager = new ConsumerManager();
     }
 
-    //~ Methods ========================================================================================================
+    public OpenID4JavaConsumer(List<OpenIDAttribute> attributes) throws ConsumerException {
+        this(new ConsumerManager(), attributes);
+    }
 
-    public String beginConsumption(HttpServletRequest req, String identityUrl, String returnToUrl) throws OpenIDConsumerException {
-        return beginConsumption(req, identityUrl,  returnToUrl, returnToUrl);
+    public OpenID4JavaConsumer(ConsumerManager consumerManager, List<OpenIDAttribute> attributes)
+            throws ConsumerException {
+        this.consumerManager = consumerManager;
+        this.attributesToFetch = Collections.unmodifiableList(attributes);
     }
 
+    //~ Methods ========================================================================================================
+
     @SuppressWarnings("unchecked")
     public String beginConsumption(HttpServletRequest req, String identityUrl, String returnToUrl, String realm)
             throws OpenIDConsumerException {
@@ -71,31 +83,37 @@ public class OpenID4JavaConsumer implements OpenIDConsumer {
         }
 
         DiscoveryInformation information = consumerManager.associate(discoveries);
-        HttpSession session = req.getSession(true);
-        session.setAttribute(DiscoveryInformation.class.getName(), information);
+        req.getSession().setAttribute(DISCOVERY_INFO_KEY, information);
 
         AuthRequest authReq;
 
         try {
             authReq = consumerManager.authenticate(information, returnToUrl, realm);
+            if (!attributesToFetch.isEmpty()) {
+                FetchRequest fetchRequest = FetchRequest.createFetchRequest();
+                for (OpenIDAttribute attr : attributesToFetch) {
+                    fetchRequest.addAttribute(attr.getName(), attr.getType(), attr.isRequired(), attr.getCount());
+                }
+                authReq.addExtension(fetchRequest);
+            }
         } catch (MessageException e) {
-            throw new OpenIDConsumerException("Error processing ConumerManager authentication", e);
+            throw new OpenIDConsumerException("Error processing ConsumerManager authentication", e);
         } catch (ConsumerException e) {
-            throw new OpenIDConsumerException("Error processing ConumerManager authentication", e);
+            throw new OpenIDConsumerException("Error processing ConsumerManager authentication", e);
         }
 
         return authReq.getDestinationUrl(true);
     }
 
-    public OpenIDAuthenticationToken endConsumption(HttpServletRequest request)
-        throws OpenIDConsumerException {
+    @SuppressWarnings("unchecked")
+    public OpenIDAuthenticationToken endConsumption(HttpServletRequest request) throws OpenIDConsumerException {
+        final boolean debug = logger.isDebugEnabled();
         // extract the parameters from the authentication response
         // (which comes in as a HTTP request from the OpenID provider)
         ParameterList openidResp = new ParameterList(request.getParameterMap());
 
         // retrieve the previously stored discovery information
-        DiscoveryInformation discovered = (DiscoveryInformation) request.getSession()
-                                                                        .getAttribute(DiscoveryInformation.class.getName());
+        DiscoveryInformation discovered = (DiscoveryInformation) request.getSession().getAttribute(DISCOVERY_INFO_KEY);
 
         // extract the receiving URL from the HTTP request
         StringBuffer receivingURL = request.getRequestURL();
@@ -118,16 +136,46 @@ public class OpenID4JavaConsumer implements OpenIDConsumer {
             throw new OpenIDConsumerException("Error verifying openid response", e);
         }
 
+        // fetch the attributesToFetch of the response
+        Message authSuccess = verification.getAuthResponse();
+        List<OpenIDAttribute> attributes = new ArrayList<OpenIDAttribute>(this.attributesToFetch.size());
+
+        if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) {
+            if (debug) {
+                logger.debug("Extracting attributes retrieved by attribute exchange");
+            }
+            try {
+                MessageExtension ext = authSuccess.getExtension(AxMessage.OPENID_NS_AX);
+                if (ext instanceof FetchResponse) {
+                    FetchResponse fetchResp = (FetchResponse) ext;
+                    for (OpenIDAttribute attr : attributesToFetch) {
+                        List<String> values = fetchResp.getAttributeValues(attr.getName());
+                        if (!values.isEmpty()) {
+                            OpenIDAttribute fetched = new OpenIDAttribute(attr.getName(), attr.getType(), values);
+                            fetched.setRequired(attr.isRequired());
+                            attributes.add(fetched);
+                        }
+                    }
+                }
+            } catch (MessageException e) {
+                attributes.clear();
+                throw new OpenIDConsumerException("Attribute retrievel failed", e);
+            }
+            if (debug) {
+                logger.debug("Retrieved attributes" + attributes);
+            }
+        }
+
         // examine the verification result and extract the verified identifier
         Identifier verified = verification.getVerifiedId();
 
-        if (verified != null) {
-            return new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.SUCCESS, verified.getIdentifier(),
-                "some message");
-        } else {
+        if (verified == null) {
             return new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.FAILURE,
-                discovered.getClaimedIdentifier().getIdentifier(),
-                "Verification status message: [" + verification.getStatusMsg() + "]");
+                    discovered.getClaimedIdentifier().getIdentifier(),
+                    "Verification status message: [" + verification.getStatusMsg() + "]", attributes);
         }
+
+        return new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.SUCCESS, verified.getIdentifier(),
+                        "some message", attributes);
     }
 }

+ 93 - 0
openid/src/main/java/org/springframework/security/openid/OpenIDAttribute.java

@@ -0,0 +1,93 @@
+package org.springframework.security.openid;
+
+import java.util.List;
+
+import org.springframework.util.Assert;
+
+/**
+ * Represents an OpenID subject identity attribute.
+ * <p>
+ * Can be used for configuring the <tt>OpenID4JavaConsumer</tt> with the attributes which should be requested during a
+ * fetch request, or to hold values for an attribute which are returned during the authentication process.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 3.0
+ */
+public class OpenIDAttribute {
+    private final String name;
+    private final String typeIdentifier;
+    private boolean required = false;
+    private int count = 1;
+
+    private final List<String> values;
+
+    public OpenIDAttribute(String name, String type) {
+        this.name = name;
+        this.typeIdentifier = type;
+        this.values = null;
+    }
+
+    public OpenIDAttribute(String name, String type, List<String> values) {
+        Assert.notEmpty(values);
+        this.name = name;
+        this.typeIdentifier = type;
+        this.values = values;
+    }
+
+    /**
+     * The attribute name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * The attribute type Identifier (a URI).
+     */
+    public String getType() {
+        return typeIdentifier;
+    }
+
+    /**
+     * The "required" flag for the attribute when used with an authentication request. Defaults to "false".
+     */
+    public boolean isRequired() {
+        return required;
+    }
+
+    public void setRequired(boolean required) {
+        this.required = required;
+    }
+
+    /**
+     * The requested count for the attribute when it is used as part of an authentication
+     * request. Defaults to 1.
+     */
+    public int getCount() {
+        return count;
+    }
+
+    public void setCount(int count) {
+        this.count = count;
+    }
+
+    /**
+     * The values obtained from an attribute exchange.
+     */
+    public List<String> getValues() {
+        Assert.notNull(values, "Cannot read values from an authentication request attribute");
+        return values;
+    }
+
+    public String toString() {
+        StringBuilder result = new StringBuilder("[");
+        result.append(name);
+        if (values != null) {
+            result.append(":");
+            result.append(values.toString());
+        }
+        result.append("]");
+        return result.toString();
+    }
+}

+ 17 - 4
openid/src/main/java/org/springframework/security/openid/OpenIDAuthenticationProvider.java

@@ -63,14 +63,12 @@ public class OpenIDAuthenticationProvider implements AuthenticationProvider, Ini
             OpenIDAuthenticationToken response = (OpenIDAuthenticationToken) authentication;
             OpenIDAuthenticationStatus status = response.getStatus();
 
-            // handle the various possibilites
+            // handle the various possibilities
             if (status == OpenIDAuthenticationStatus.SUCCESS) {
-
                 // Lookup user details
                 UserDetails userDetails = userDetailsService.loadUserByUsername(response.getIdentityUrl());
 
-                return new OpenIDAuthenticationToken(userDetails.getAuthorities(), response.getStatus(),
-                        response.getIdentityUrl());
+                return createSuccessfulAuthentication(userDetails, response);
 
             } else if (status == OpenIDAuthenticationStatus.CANCELLED) {
                 throw new AuthenticationCancelledException("Log in cancelled");
@@ -89,6 +87,21 @@ public class OpenIDAuthenticationProvider implements AuthenticationProvider, Ini
         return null;
     }
 
+    /**
+     * Handles the creation of the final <tt>Authentication</tt> object which will be returned by the provider.
+     * <p>
+     * The default implementation just creates a new OpenIDAuthenticationToken from the original, but with the
+     * UserDetails as the principal and including the authorities loaded by the UserDetailsService.
+     *
+     * @param userDetails the loaded UserDetails object
+     * @param auth the token passed to the authenticate method, containing
+     * @return the token which will represent the authenticated user.
+     */
+    protected Authentication createSuccessfulAuthentication(UserDetails userDetails, OpenIDAuthenticationToken auth) {
+        return new OpenIDAuthenticationToken(userDetails, userDetails.getAuthorities(),
+                auth.getIdentityUrl(), auth.getAttributes());
+    }
+
     /**
      * Used to load the authorities for the authenticated OpenID user.
      */

+ 27 - 7
openid/src/main/java/org/springframework/security/openid/OpenIDAuthenticationToken.java

@@ -30,29 +30,39 @@ public class OpenIDAuthenticationToken extends AbstractAuthenticationToken {
     //~ Instance fields ================================================================================================
 
     private final OpenIDAuthenticationStatus status;
+    private final Object principal;
     private final String identityUrl;
     private final String message;
+    private final List<OpenIDAttribute> attributes;
 
     //~ Constructors ===================================================================================================
 
-    public OpenIDAuthenticationToken(OpenIDAuthenticationStatus status, String identityUrl, String message) {
+    public OpenIDAuthenticationToken(OpenIDAuthenticationStatus status, String identityUrl,
+            String message, List<OpenIDAttribute> attributes) {
         super(new ArrayList<GrantedAuthority>(0));
+        this.principal = identityUrl;
         this.status = status;
         this.identityUrl = identityUrl;
         this.message = message;
+        this.attributes = attributes;
         setAuthenticated(false);
     }
 
     /**
-     * Created by the OpenIDAuthenticationProvider on successful authentication.
-     * <b>Do not use directly</b>
+     * Created by the <tt>OpenIDAuthenticationProvider</tt> on successful authentication.
+     *
+     * @param principal usually the <tt>UserDetails</tt> returned by the the configured <tt>UserDetailsService</tt>
+     * used by the <tt>OpenIDAuthenticationProvider</tt>.
      *
      */
-    public OpenIDAuthenticationToken(List<GrantedAuthority> authorities, OpenIDAuthenticationStatus status, String identityUrl) {
+    public OpenIDAuthenticationToken(Object principal, List<GrantedAuthority> authorities,
+            String identityUrl, List<OpenIDAttribute> attributes) {
         super(authorities);
-        this.status = status;
+        this.principal = principal;
+        this.status = OpenIDAuthenticationStatus.SUCCESS;
         this.identityUrl = identityUrl;
         this.message = null;
+        this.attributes = attributes;
 
         setAuthenticated(true);
     }
@@ -76,14 +86,24 @@ public class OpenIDAuthenticationToken extends AbstractAuthenticationToken {
     }
 
     /**
-     * Returns the <tt>identityUrl</tt> value.
+     * Returns the <tt>principal</tt> value.
+     *
      * @see org.springframework.security.core.Authentication#getPrincipal()
      */
     public Object getPrincipal() {
-        return identityUrl;
+        return principal;
     }
 
     public OpenIDAuthenticationStatus getStatus() {
         return status;
     }
+
+    public List<OpenIDAttribute> getAttributes() {
+        return attributes;
+    }
+
+    @Override
+    public String toString() {
+        return "[" + super.toString() + ", attributes : " + attributes +"]";
+    }
 }

+ 0 - 6
openid/src/main/java/org/springframework/security/openid/OpenIDConsumer.java

@@ -26,12 +26,6 @@ import javax.servlet.http.HttpServletRequest;
  */
 public interface OpenIDConsumer {
 
-    /**
-     * @deprecated Use {@link #beginConsumption(javax.servlet.http.HttpServletRequest, String, String, String)}
-     */
-    public String beginConsumption(HttpServletRequest req, String identityUrl, String returnToUrl)
-            throws OpenIDConsumerException;
-
     /**
      * Given the request, the claimedIdentity, the return to url, and a realm, lookup the openId authentication
      * page the user should be redirected to.

+ 0 - 16
openid/src/test/java/org/springframework/security/openid/MockOpenIDConsumer.java

@@ -22,8 +22,6 @@ import javax.servlet.http.HttpServletRequest;
 
 
 /**
- * DOCUMENT ME!
- *
  * @author Robin Bramley, Opsera Ltd
  */
 public class MockOpenIDConsumer implements OpenIDConsumer {
@@ -54,17 +52,6 @@ public class MockOpenIDConsumer implements OpenIDConsumer {
         return redirectUrl;
     }
 
-    /* (non-Javadoc)
-    * @see org.springframework.security.ui.openid.OpenIDConsumer#beginConsumption(javax.servlet.http.HttpServletRequest, java.lang.String)
-    */
-    public String beginConsumption(HttpServletRequest req, String identityUrl, String returnToUrl)
-            throws OpenIDConsumerException {
-        throw new UnsupportedOperationException("This method is deprecated, stop using it");
-    }
-
-    /* (non-Javadoc)
-     * @see org.springframework.security.ui.openid.OpenIDConsumer#endConsumption(javax.servlet.http.HttpServletRequest)
-     */
     public OpenIDAuthenticationToken endConsumption(HttpServletRequest req)
             throws OpenIDConsumerException {
         return token;
@@ -79,9 +66,6 @@ public class MockOpenIDConsumer implements OpenIDConsumer {
         this.redirectUrl = redirectUrl;
     }
 
-    /* (non-Javadoc)
-     * @see org.springframework.security.ui.openid.OpenIDConsumer#setReturnToUrl(java.lang.String)
-     */
     public void setReturnToUrl(String returnToUrl) {
         // TODO Auto-generated method stub
     }

+ 6 - 6
openid/src/test/java/org/springframework/security/openid/OpenIDAuthenticationProviderTests.java

@@ -46,7 +46,7 @@ public class OpenIDAuthenticationProviderTests extends TestCase {
         OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider();
         provider.setUserDetailsService(new MockUserDetailsService());
 
-        Authentication preAuth = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.CANCELLED, USERNAME, "");
+        Authentication preAuth = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.CANCELLED, USERNAME, "" ,null);
 
         assertFalse(preAuth.isAuthenticated());
 
@@ -65,7 +65,7 @@ public class OpenIDAuthenticationProviderTests extends TestCase {
         OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider();
         provider.setUserDetailsService(new MockUserDetailsService());
 
-        Authentication preAuth = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.ERROR, USERNAME, "");
+        Authentication preAuth = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.ERROR, USERNAME, "", null);
 
         assertFalse(preAuth.isAuthenticated());
 
@@ -84,7 +84,7 @@ public class OpenIDAuthenticationProviderTests extends TestCase {
         OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider();
         provider.setUserDetailsService(new MockUserDetailsService());
 
-        Authentication preAuth = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.FAILURE, USERNAME, "");
+        Authentication preAuth = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.FAILURE, USERNAME, "", null);
 
         assertFalse(preAuth.isAuthenticated());
 
@@ -103,7 +103,7 @@ public class OpenIDAuthenticationProviderTests extends TestCase {
         OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider();
         provider.setUserDetailsService(new MockUserDetailsService());
 
-        Authentication preAuth = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.SETUP_NEEDED, USERNAME, "");
+        Authentication preAuth = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.SETUP_NEEDED, USERNAME, "", null);
 
         assertFalse(preAuth.isAuthenticated());
 
@@ -122,7 +122,7 @@ public class OpenIDAuthenticationProviderTests extends TestCase {
         OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider();
         provider.setUserDetailsService(new MockUserDetailsService());
 
-        Authentication preAuth = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.SUCCESS, USERNAME, "");
+        Authentication preAuth = new OpenIDAuthenticationToken(OpenIDAuthenticationStatus.SUCCESS, USERNAME, "", null);
 
         assertFalse(preAuth.isAuthenticated());
 
@@ -132,7 +132,7 @@ public class OpenIDAuthenticationProviderTests extends TestCase {
         assertTrue(postAuth instanceof OpenIDAuthenticationToken);
         assertTrue(postAuth.isAuthenticated());
         assertNotNull(postAuth.getPrincipal());
-        assertEquals(preAuth.getPrincipal(), postAuth.getPrincipal());
+        assertTrue(postAuth.getPrincipal() instanceof UserDetails);
         assertNotNull(postAuth.getAuthorities());
         assertTrue(postAuth.getAuthorities().size() > 0);
         assertTrue(((OpenIDAuthenticationToken) postAuth).getStatus() == OpenIDAuthenticationStatus.SUCCESS);

+ 0 - 38
openid/src/test/java/org/springframework/security/openid/OpenIDAuthenticationTokenTests.java

@@ -1,38 +0,0 @@
-/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
- *
- * 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.openid;
-
-import org.springframework.security.openid.OpenIDAuthenticationStatus;
-import org.springframework.security.openid.OpenIDAuthenticationToken;
-
-import junit.framework.TestCase;
-
-/**
- * @author Ray Krueger
- */
-public class OpenIDAuthenticationTokenTests extends TestCase {
-
-    public void test() throws Exception {
-        OpenIDAuthenticationToken token = newToken();
-        assertEquals(token, newToken());
-    }
-
-    private OpenIDAuthenticationToken newToken() {
-        return new OpenIDAuthenticationToken(
-                OpenIDAuthenticationStatus.SUCCESS,
-                "http://raykrueger.blogspot.com/",
-                "what is this for anyway?");
-    }
-}

+ 6 - 1
samples/openid/src/main/webapp/WEB-INF/applicationContext-security.xml

@@ -16,7 +16,12 @@
         <intercept-url pattern="/**" access="ROLE_USER"/>
         <intercept-url pattern="/openidlogin.jsp*" filters="none"/>
         <logout/>
-        <openid-login login-page="/openidlogin.jsp" authentication-failure-url="/openidlogin.jsp?login_error=true" />
+        <openid-login login-page="/openidlogin.jsp" authentication-failure-url="/openidlogin.jsp?login_error=true">
+            <attribute-exchange>
+                <openid-attribute name="email" type="http://schema.openid.net/contact/email" required="true" count="2"/>
+                <openid-attribute name="name" type="http://schema.openid.net/namePerson/friendly" />
+            </attribute-exchange>
+        </openid-login>
     </http>
 
     <authentication-manager alias="authenticationManager"/>