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:
+45
-19
@@ -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 =
|
||||
|
||||
+7
-2
@@ -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}
|
||||
|
||||
+12
-2
@@ -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>
|
||||
|
||||
+3
-2
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user