1
0
mirror of synced 2026-08-05 17:57:15 +00:00

SEC-1460: Added AxFetchListFactory which matches OpenID identifiers to lists of attributes to use in a fetch-request.

This allows different configurations to be used based on the identity-provider (google, yahoo etc). The default implementation iterates through a map of regex patterns to attribute lists. The namespace has also been extended to support this facility, with the "identifier-match" attribute being added to the attribute-exchange element. Multiple attribute-exchange elements can now be defined, each matching a different identifier.
This commit is contained in:
Luke Taylor
2010-04-20 23:44:58 +01:00
parent 3af75afec1
commit 2f025fba6c
9 changed files with 211 additions and 44 deletions
@@ -18,6 +18,7 @@ import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.authentication.AnonymousAuthenticationProvider;
@@ -52,8 +53,9 @@ final class AuthenticationConfigBuilder {
static final String OPEN_ID_AUTHENTICATION_PROCESSING_FILTER_CLASS = "org.springframework.security.openid.OpenIDAuthenticationFilter";
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";
private static final String OPEN_ID_CONSUMER_CLASS = "org.springframework.security.openid.OpenID4JavaConsumer";
static final String OPEN_ID_ATTRIBUTE_CLASS = "org.springframework.security.openid.OpenIDAttribute";
private static final String OPEN_ID_ATTRIBUTE_FACTORY_CLASS = "org.springframework.security.openid.RegexBasedAxFetchListFactory";
static final String AUTHENTICATION_PROCESSING_FILTER_CLASS = "org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter";
private static final String ATT_AUTO_CONFIG = "auto-config";
@@ -192,30 +194,31 @@ final class AuthenticationConfigBuilder {
openIDFilter = parser.getFilterBean();
openIDEntryPoint = parser.getEntryPointBean();
Element attrExElt = DomUtils.getChildElementByTagName(openIDLoginElt, Elements.OPENID_ATTRIBUTE_EXCHANGE);
List<Element> attrExElts = DomUtils.getChildElementsByTagName(openIDLoginElt, Elements.OPENID_ATTRIBUTE_EXCHANGE);
if (attrExElt != null) {
if (!attrExElts.isEmpty()) {
// 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));
BeanDefinitionBuilder axFactory = BeanDefinitionBuilder.rootBeanDefinition(OPEN_ID_ATTRIBUTE_FACTORY_CLASS);
ManagedMap<String, ManagedList<BeanDefinition>> axMap = new ManagedMap<String, ManagedList<BeanDefinition>>();
for (Element attrExElt : attrExElts) {
String identifierMatch = attrExElt.getAttribute("identifier-match");
if (!StringUtils.hasText(identifierMatch)) {
if (attrExElts.size() > 1) {
pc.getReaderContext().error("You must supply an identifier-match attribute if using more" +
" than one " + Elements.OPENID_ATTRIBUTE_EXCHANGE + " element", attrExElt);
}
// Match anything
identifierMatch = ".*";
}
if (StringUtils.hasLength(count)) {
attrBldr.addPropertyValue("count", Integer.parseInt(count));
}
attributes.add(attrBldr.getBeanDefinition());
axMap.put(identifierMatch, parseOpenIDAttributes(attrExElt));
}
consumerBldr.addConstructorArgValue(attributes);
axFactory.addConstructorArgValue(axMap);
consumerBldr.addConstructorArgValue(axFactory.getBeanDefinition());
openIDFilter.getPropertyValues().addPropertyValue("consumer", consumerBldr.getBeanDefinition());
}
}
@@ -232,6 +235,29 @@ final class AuthenticationConfigBuilder {
}
}
private ManagedList<BeanDefinition> parseOpenIDAttributes(Element attrExElt) {
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());
}
return attributes;
}
private void createOpenIDProvider() {
Element openIDLoginElt = DomUtils.getChildElementByTagName(httpElt, Elements.OPENID_LOGIN);
BeanDefinitionBuilder openIDProviderBuilder =
@@ -371,10 +371,15 @@ 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?, attribute-exchange?}
element openid-login {form-login.attlist, user-service-ref?, attribute-exchange*}
attribute-exchange =
element attribute-exchange {openid-attribute+}
## Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found.
element attribute-exchange {attribute-exchange.attlist, openid-attribute+}
attribute-exchange.attlist &=
## A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.
attribute identifier-match {xsd:token}?
openid-attribute =
element openid-attribute {openid-attribute.attlist}
@@ -627,7 +627,7 @@
<xs:documentation>Sets up form login for authentication with an Open ID identity</xs:documentation>
</xs:annotation><xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" ref="security:attribute-exchange"/>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="security:attribute-exchange"/>
</xs:sequence>
<xs:attributeGroup ref="security:form-login.attlist"/>
<xs:attribute name="user-service-ref" type="xs:token">
@@ -902,11 +902,21 @@
</xs:attribute>
</xs:attributeGroup>
<xs:element name="attribute-exchange"><xs:complexType>
<xs:element name="attribute-exchange"><xs:annotation>
<xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found. </xs:documentation>
</xs:annotation><xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" ref="security:openid-attribute"/>
</xs:sequence>
<xs:attributeGroup ref="security:attribute-exchange.attlist"/>
</xs:complexType></xs:element>
<xs:attributeGroup name="attribute-exchange.attlist">
<xs:attribute name="identifier-match" type="xs:token">
<xs:annotation>
<xs:documentation>A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="openid-attribute"><xs:complexType>
<xs:attributeGroup ref="security:openid-attribute.attlist"/>
</xs:complexType></xs:element>
@@ -46,6 +46,7 @@ import org.springframework.security.openid.OpenIDAuthenticationProvider;
import org.springframework.security.openid.OpenIDAuthenticationToken;
import org.springframework.security.openid.OpenIDConsumer;
import org.springframework.security.openid.OpenIDConsumerException;
import org.springframework.security.openid.RegexBasedAxFetchListFactory;
import org.springframework.security.util.FieldUtils;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
@@ -1152,7 +1153,6 @@ public class HttpSecurityBeanDefinitionParserTests {
assertEquals("/openid_login", ap.getLoginFormUrl());
}
@SuppressWarnings("unchecked")
@Test
public void openIDWithAttributeExchangeConfigurationIsParsedCorrectly() throws Exception {
setContext(
@@ -1168,7 +1168,8 @@ public class HttpSecurityBeanDefinitionParserTests {
OpenIDAuthenticationFilter apf = getFilter(OpenIDAuthenticationFilter.class);
OpenID4JavaConsumer consumer = (OpenID4JavaConsumer) FieldUtils.getFieldValue(apf, "consumer");
List<OpenIDAttribute> attributes = (List<OpenIDAttribute>) FieldUtils.getFieldValue(consumer, "attributesToFetch");
RegexBasedAxFetchListFactory axFactory = (RegexBasedAxFetchListFactory) FieldUtils.getFieldValue(consumer, "attributesToFetchFactory");
List<OpenIDAttribute> attributes = axFactory.createAttributeList("https://anyopenidprovider.com/");
assertEquals(2, attributes.size());
assertEquals("nickname", attributes.get(0).getName());
assertEquals("http://schema.openid.net/namePerson/friendly", attributes.get(0).getType());