SEC-1137: Added support for an external UserDetailsContextMapper using the attribute user-context-mapper-ref.

This commit is contained in:
Luke Taylor 2009-06-08 23:35:05 +00:00
parent bfa2806034
commit 0473cfbfc0
5 changed files with 105 additions and 51 deletions

View File

@ -97,7 +97,7 @@ public class LdapProviderBeanDefinitionParser implements BeanDefinitionParser {
ldapProvider.addConstructorArgValue(authenticatorBuilder.getBeanDefinition());
ldapProvider.addConstructorArgValue(LdapUserServiceBeanDefinitionParser.parseAuthoritiesPopulator(elt, parserContext));
ldapProvider.addPropertyValue("userDetailsContextMapper",
LdapUserServiceBeanDefinitionParser.parseUserDetailsClass(elt, parserContext));
LdapUserServiceBeanDefinitionParser.parseUserDetailsClassOrUserMapperRef(elt, parserContext));
parserContext.getRegistry().registerBeanDefinition(BeanIds.LDAP_AUTHENTICATION_PROVIDER, ldapProvider.getBeanDefinition());
ConfigUtils.addAuthenticationProvider(parserContext, BeanIds.LDAP_AUTHENTICATION_PROVIDER);

View File

@ -1,5 +1,6 @@
package org.springframework.security.config;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@ -29,6 +30,7 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
static final String ATT_ROLE_PREFIX = "role-prefix";
static final String ATT_USER_CLASS = "user-details-class";
static final String ATT_USER_CONTEXT_MAPPER_REF = "user-context-mapper-ref";
static final String OPT_PERSON = "person";
static final String OPT_INETORGPERSON = "inetOrgPerson";
@ -49,8 +51,9 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
}
builder.addConstructorArgValue(parseSearchBean(elt, parserContext));
builder.getRawBeanDefinition().setSource(parserContext.extractSource(elt));
builder.addConstructorArgValue(parseAuthoritiesPopulator(elt, parserContext));
builder.addPropertyValue("userDetailsMapper", parseUserDetailsClass(elt, parserContext));
builder.addPropertyValue("userDetailsMapper", parseUserDetailsClassOrUserMapperRef(elt, parserContext));
}
static RootBeanDefinition parseSearchBean(Element elt, ParserContext parserContext) {
@ -109,15 +112,32 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
registry.registerBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR, bdb.getBeanDefinition());
}
static RootBeanDefinition parseUserDetailsClass(Element elt, ParserContext parserContext) {
static BeanMetadataElement parseUserDetailsClassOrUserMapperRef(Element elt, ParserContext parserContext) {
String userDetailsClass = elt.getAttribute(ATT_USER_CLASS);
String userMapperRef = elt.getAttribute(ATT_USER_CONTEXT_MAPPER_REF);
if (StringUtils.hasText(userDetailsClass) && StringUtils.hasText(userMapperRef)) {
parserContext.getReaderContext().error("Attributes " + ATT_USER_CLASS + " and " + ATT_USER_CONTEXT_MAPPER_REF
+ " cannot be used together.", parserContext.extractSource(elt));
}
if (StringUtils.hasText(userMapperRef)) {
return new RuntimeBeanReference(userMapperRef);
}
RootBeanDefinition mapper;
if (OPT_PERSON.equals(userDetailsClass)) {
return new RootBeanDefinition(PERSON_MAPPER_CLASS, null, null);
mapper = new RootBeanDefinition(PERSON_MAPPER_CLASS, null, null);
} else if (OPT_INETORGPERSON.equals(userDetailsClass)) {
return new RootBeanDefinition(INET_ORG_PERSON_MAPPER_CLASS, null, null);
mapper = new RootBeanDefinition(INET_ORG_PERSON_MAPPER_CLASS, null, null);
} else {
mapper = new RootBeanDefinition(LDAP_USER_MAPPER_CLASS, null, null);
}
return new RootBeanDefinition(LDAP_USER_MAPPER_CLASS, null, null);
mapper.setSource(parserContext.extractSource(elt));
return mapper;
}
static RootBeanDefinition parseAuthoritiesPopulator(Element elt, ParserContext parserContext) {

View File

@ -106,6 +106,9 @@ group-role-attribute-attribute =
user-details-class-attribute =
## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object
attribute user-details-class {"person" | "inetOrgPerson"}
user-context-mapper-attribute =
## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry
attribute user-context-mapper-ref {xsd:token}
ldap-user-service =
@ -128,7 +131,7 @@ ldap-us.attlist &=
ldap-us.attlist &=
role-prefix?
ldap-us.attlist &=
user-details-class-attribute?
(user-details-class-attribute | user-context-mapper-attribute)?
ldap-authentication-provider =
## Sets up an ldap authentication provider
@ -151,7 +154,7 @@ ldap-ap.attlist &=
ldap-ap.attlist &=
role-prefix?
ldap-ap.attlist &=
user-details-class-attribute?
(user-details-class-attribute | user-context-mapper-attribute)?
password-compare-element =
## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user

View File

@ -301,6 +301,15 @@
</xs:simpleType>
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="user-context-mapper-attribute">
<xs:attribute name="user-context-mapper-ref" use="required" type="xs:token">
<xs:annotation>
<xs:documentation>Allows explicit customization of the loaded user object by specifying
a UserDetailsContextMapper bean which will be called with the context information
from the user's directory entry</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="ldap-user-service" substitutionGroup="security:any-user-service">
<xs:complexType>
<xs:attributeGroup ref="security:ldap-us.attlist"/>
@ -376,6 +385,13 @@
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="user-context-mapper-ref" type="xs:token">
<xs:annotation>
<xs:documentation>Allows explicit customization of the loaded user object by specifying
a UserDetailsContextMapper bean which will be called with the context information
from the user's directory entry</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="ldap-authentication-provider">
<xs:annotation>
@ -504,6 +520,13 @@
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="user-context-mapper-ref" type="xs:token">
<xs:annotation>
<xs:documentation>Allows explicit customization of the loaded user object by specifying
a UserDetailsContextMapper bean which will be called with the context information
from the user's directory entry</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="password-compare.attlist">
<xs:attribute name="password-attribute" type="xs:token">
@ -623,7 +646,16 @@
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element ref="security:expression-handler"/>
<xs:element name="expression-handler">
<xs:annotation>
<xs:documentation>Defines the SecurityExpressionHandler instance which will be
used if expression-based access-control is enabled. A default implementation
(with no ACL support) will be used if not supplied.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attributeGroup ref="security:ref"/>
</xs:complexType>
</xs:element>
</xs:choice>
<xs:element minOccurs="0" maxOccurs="unbounded" name="protect-pointcut">
<xs:annotation>
@ -693,16 +725,6 @@
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="expression-handler">
<xs:annotation>
<xs:documentation>Defines the SecurityExpressionHandler instance which will be used if
expression-based access-control is enabled. A default implementation (with no ACL
support) will be used if not supplied.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attributeGroup ref="security:ref"/>
</xs:complexType>
</xs:element>
<xs:element name="custom-after-invocation-provider">
<xs:annotation>
<xs:documentation>Used to decorate an AfterInvocationProvider to specify that it should be
@ -759,7 +781,21 @@
<xs:attributeGroup ref="security:form-login.attlist"/>
</xs:complexType>
</xs:element>
<xs:element ref="security:openid-login"/>
<xs:element name="openid-login">
<xs:annotation>
<xs:documentation>Sets up form login for authentication with an Open ID
identity</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attributeGroup ref="security:form-login.attlist"/>
<xs:attribute name="user-service-ref" type="xs:token">
<xs:annotation>
<xs:documentation>A reference to a user-service (or UserDetailsService bean)
Id</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="x509">
<xs:annotation>
<xs:documentation>Adds support for X.509 client authentication.</xs:documentation>
@ -823,7 +859,12 @@
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" ref="security:port-mapping"/>
<xs:element maxOccurs="unbounded" name="port-mapping">
<xs:complexType>
<xs:attributeGroup ref="security:http-port"/>
<xs:attributeGroup ref="security:https-port"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
@ -1108,21 +1149,6 @@
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="openid-login">
<xs:annotation>
<xs:documentation>Sets up form login for authentication with an Open ID
identity</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attributeGroup ref="security:form-login.attlist"/>
<xs:attribute name="user-service-ref" type="xs:token">
<xs:annotation>
<xs:documentation>A reference to a user-service (or UserDetailsService bean)
Id</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="filter-chain-map">
<xs:annotation>
<xs:documentation>Used to explicitly configure a FilterChainProxy instance with a
@ -1348,12 +1374,6 @@
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="port-mapping">
<xs:complexType>
<xs:attributeGroup ref="security:http-port"/>
<xs:attributeGroup ref="security:https-port"/>
</xs:complexType>
</xs:element>
<xs:attributeGroup name="http-port">
<xs:attribute name="http" use="required" type="xs:token"/>
</xs:attributeGroup>
@ -1474,7 +1494,14 @@
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="security:user"/>
<xs:element minOccurs="0" maxOccurs="unbounded" name="user">
<xs:annotation>
<xs:documentation>Represents a user in the application.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attributeGroup ref="security:user.attlist"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="id" type="xs:ID">
<xs:annotation>
@ -1488,14 +1515,6 @@
<xs:attributeGroup name="properties-file">
<xs:attribute name="properties" type="xs:token"/>
</xs:attributeGroup>
<xs:element name="user">
<xs:annotation>
<xs:documentation>Represents a user in the application.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attributeGroup ref="security:user.attlist"/>
</xs:complexType>
</xs:element>
<xs:attributeGroup name="user.attlist">
<xs:attribute name="name" use="required" type="xs:token">
<xs:annotation>

View File

@ -141,6 +141,18 @@ public class LdapUserServiceBeanDefinitionParserTests {
assertTrue(ben instanceof InetOrgPerson);
}
@Test
public void externalContextMapperIsSupported() {
setContext(
"<ldap-server id='someServer'/>" +
"<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' user-context-mapper-ref='mapper'/>" +
"<b:bean id='mapper' class='"+ InetOrgPersonContextMapper.class.getName() +"'/>");
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetails ben = uds.loadUserByUsername("ben");
assertTrue(ben instanceof InetOrgPerson);
}
private void setContext(String context) {
appCtx = new InMemoryXmlApplicationContext(context);