SEC-935: Support for OpenID attribute exchange and changes to namespace syntax to allow simple configuration of attributes to request.
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user