瀏覽代碼

OPEN - issue SEC-807: Allow mapping to a standard Ldap UserDetails through the namespace
http://jira.springframework.org/browse/SEC-807. Added support for user-details-class attribute to ldap-authentication-provider and ldap-user-service.

Luke Taylor 17 年之前
父節點
當前提交
e4b32b8d29

+ 3 - 1
core/src/main/java/org/springframework/security/config/LdapProviderBeanDefinitionParser.java

@@ -93,7 +93,9 @@ public class LdapProviderBeanDefinitionParser implements BeanDefinitionParser {
         RootBeanDefinition ldapProvider = new RootBeanDefinition(LdapAuthenticationProvider.class);
         ldapProvider.getConstructorArgumentValues().addGenericArgumentValue(authenticator);
         ldapProvider.getConstructorArgumentValues().addGenericArgumentValue(LdapUserServiceBeanDefinitionParser.parseAuthoritiesPopulator(elt, parserContext));
-
+        ldapProvider.getPropertyValues().addPropertyValue("userDetailsContextMapper", 
+        		LdapUserServiceBeanDefinitionParser.parseUserDetailsClass(elt, parserContext));
+        
         ConfigUtils.getRegisteredProviders(parserContext).add(ldapProvider);
 
         return null;

+ 20 - 2
core/src/main/java/org/springframework/security/config/LdapUserServiceBeanDefinitionParser.java

@@ -1,6 +1,9 @@
 package org.springframework.security.config;
 
+import org.springframework.security.userdetails.ldap.InetOrgPersonContextMapper;
+import org.springframework.security.userdetails.ldap.LdapUserDetailsMapper;
 import org.springframework.security.userdetails.ldap.LdapUserDetailsService;
+import org.springframework.security.userdetails.ldap.PersonContextMapper;
 import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
 import org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator;
 import org.springframework.beans.factory.xml.ParserContext;
@@ -17,7 +20,7 @@ import org.w3c.dom.Element;
  * @since 2.0
  */
 public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServiceBeanDefinitionParser {
-    public static final String ATT_SERVER = "server-ref";
+    public static final String ATT_SERVER = "server-ref";    
     public static final String ATT_USER_SEARCH_FILTER = "user-search-filter";
     public static final String ATT_USER_SEARCH_BASE = "user-search-base";
     public static final String DEF_USER_SEARCH_BASE = "";
@@ -28,7 +31,10 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
     public static final String DEF_GROUP_SEARCH_FILTER = "(uniqueMember={0})";
     public static final String DEF_GROUP_SEARCH_BASE = "ou=groups";
     
-    static final String ATT_ROLE_PREFIX = "role-prefix";    
+    static final String ATT_ROLE_PREFIX = "role-prefix";
+    static final String ATT_USER_CLASS = "user-details-class";
+    static final String OPT_PERSON = "person";
+    static final String OPT_INETORGPERSON = "inetOrgPerson";
 
     protected Class getBeanClass(Element element) {
         return LdapUserDetailsService.class;
@@ -42,6 +48,7 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
         
         builder.addConstructorArg(parseSearchBean(elt, parserContext));
         builder.addConstructorArg(parseAuthoritiesPopulator(elt, parserContext));
+        builder.addPropertyValue("userDetailsMapper", parseUserDetailsClass(elt, parserContext));
     }
     
     static RootBeanDefinition parseSearchBean(Element elt, ParserContext parserContext) {
@@ -86,6 +93,17 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
         return contextSource;
     }
     
+    static RootBeanDefinition parseUserDetailsClass(Element elt, ParserContext parserContext) {
+    	String userDetailsClass = elt.getAttribute(ATT_USER_CLASS);
+    	
+    	if(OPT_PERSON.equals(userDetailsClass)) {
+    		return new RootBeanDefinition(PersonContextMapper.class);
+    	} else if (OPT_INETORGPERSON.equals(userDetailsClass)) {
+    		return new RootBeanDefinition(InetOrgPersonContextMapper.class);
+    	}
+    	return new RootBeanDefinition(LdapUserDetailsMapper.class);
+    }
+    
     static RootBeanDefinition parseAuthoritiesPopulator(Element elt, ParserContext parserContext) {
         String groupSearchFilter = elt.getAttribute(ATT_GROUP_SEARCH_FILTER);
         String groupSearchBase = elt.getAttribute(ATT_GROUP_SEARCH_BASE);

+ 8 - 1
core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc

@@ -90,10 +90,13 @@ user-search-filter-attribute =
     attribute user-search-filter {xsd:string}
 user-search-base-attribute =
     ## Search base for user searches. Defaults to "".
-    attribute user-search-base {xsd:string}?
+    attribute user-search-base {xsd:string}
 group-role-attribute-attribute =
     ## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to "cn".
     attribute group-role-attribute {xsd:string}
+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"}
 
 
 ldap-user-service =
@@ -115,6 +118,8 @@ ldap-us.attlist &=
     cache-ref?
 ldap-us.attlist &=
     role-prefix?
+ldap-us.attlist &=
+    user-details-class-attribute?
 
 ldap-authentication-provider =
     ## Sets up an ldap authentication provider
@@ -136,6 +141,8 @@ ldap-ap.attlist &=
     attribute user-dn-pattern {xsd:string}?
 ldap-ap.attlist &=
     role-prefix?
+ldap-ap.attlist &=
+    user-details-class-attribute?
 
 password-compare-element =
     ## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user

+ 52 - 3
core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd

@@ -238,7 +238,7 @@
     <xs:attribute name="user-search-filter" use="required" type="xs:string"/>
   </xs:attributeGroup>
   <xs:attributeGroup name="user-search-base-attribute">
-    <xs:attribute name="user-search-base" type="xs:string">
+    <xs:attribute name="user-search-base" use="required" type="xs:string">
       <xs:annotation>
         <xs:documentation>Search base for user searches. Defaults to "".</xs:documentation>
       </xs:annotation>
@@ -252,6 +252,21 @@
       </xs:annotation>
     </xs:attribute>
   </xs:attributeGroup>
+  <xs:attributeGroup name="user-details-class-attribute">
+    <xs:attribute name="user-details-class" use="required">
+      <xs:annotation>
+        <xs:documentation>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</xs:documentation>
+      </xs:annotation>
+      <xs:simpleType>
+        <xs:restriction base="xs:token">
+          <xs:enumeration value="person"/>
+          <xs:enumeration value="inetOrgPerson"/>
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:attribute>
+  </xs:attributeGroup>
   <xs:element name="ldap-user-service" substitutionGroup="security:any-user-service">
     <xs:complexType>
       <xs:attributeGroup ref="security:ldap-us.attlist"/>
@@ -272,7 +287,11 @@
       </xs:annotation>
     </xs:attribute>
     <xs:attribute name="user-search-filter" type="xs:string"/>
-    <xs:attributeGroup ref="security:user-search-base-attribute"/>
+    <xs:attribute name="user-search-base" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Search base for user searches. Defaults to "".</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
     <xs:attribute name="group-search-filter" type="xs:string">
       <xs:annotation>
         <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted
@@ -303,6 +322,19 @@
           persistent storage (e.g. "ROLE_").</xs:documentation>
       </xs:annotation>
     </xs:attribute>
+    <xs:attribute name="user-details-class">
+      <xs:annotation>
+        <xs:documentation>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</xs:documentation>
+      </xs:annotation>
+      <xs:simpleType>
+        <xs:restriction base="xs:token">
+          <xs:enumeration value="person"/>
+          <xs:enumeration value="inetOrgPerson"/>
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:attribute>
   </xs:attributeGroup>
   <xs:element name="ldap-authentication-provider">
     <xs:annotation>
@@ -362,7 +394,11 @@
         </xs:documentation>
       </xs:annotation>
     </xs:attribute>
-    <xs:attributeGroup ref="security:user-search-base-attribute"/>
+    <xs:attribute name="user-search-base" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Search base for user searches. Defaults to "".</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
     <xs:attribute name="user-search-filter" type="xs:string"/>
     <xs:attribute name="group-search-base" type="xs:string">
       <xs:annotation>
@@ -395,6 +431,19 @@
           persistent storage (e.g. "ROLE_").</xs:documentation>
       </xs:annotation>
     </xs:attribute>
+    <xs:attribute name="user-details-class">
+      <xs:annotation>
+        <xs:documentation>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</xs:documentation>
+      </xs:annotation>
+      <xs:simpleType>
+        <xs:restriction base="xs:token">
+          <xs:enumeration value="person"/>
+          <xs:enumeration value="inetOrgPerson"/>
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:attribute>
   </xs:attributeGroup>
   <xs:attributeGroup name="password-compare.attlist">
     <xs:attribute name="password-attribute" type="xs:string">

+ 24 - 1
core/src/test/java/org/springframework/security/config/LdapUserServiceBeanDefinitionParserTests.java

@@ -7,6 +7,8 @@ import org.springframework.security.util.AuthorityUtils;
 import org.springframework.security.util.InMemoryXmlApplicationContext;
 import org.springframework.security.userdetails.UserDetailsService;
 import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.ldap.InetOrgPerson;
+import org.springframework.security.userdetails.ldap.Person;
 
 import org.junit.Test;
 import org.junit.After;
@@ -99,7 +101,28 @@ public class LdapUserServiceBeanDefinitionParserTests {
                 "    <ldap-user-service user-search-filter='(uid={0})' />" +
                 "</authentication-provider>");
     }
-
+    
+    @Test
+    public void personContextMapperIsSupported() {
+        setContext(
+                "<ldap-server />" +
+                "<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' user-details-class='person'/>");
+        UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
+        UserDetails ben = uds.loadUserByUsername("ben");
+        assertTrue(ben instanceof Person);
+    }
+    
+    @Test
+    public void inetOrgContextMapperIsSupported() {
+        setContext(
+                "<ldap-server id='someServer'/>" +
+                "<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' user-details-class='inetOrgPerson'/>");
+        UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
+        UserDetails ben = uds.loadUserByUsername("ben");
+        assertTrue(ben instanceof InetOrgPerson);
+    }
+    
+    
     private void setContext(String context) {
         appCtx = new InMemoryXmlApplicationContext(context);
     }