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

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

Luke Taylor 16 жил өмнө
parent
commit
48988bde84
16 өөрчлөгдсөн 334 нэмэгдсэн , 116 устгасан
  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"/>