SEC-705: Extend ldap-authentication-provider namespace elt to support user searches and multiple authentication strategies

http://jira.springframework.org/browse/SEC-705
This commit is contained in:
Luke Taylor 2008-03-09 19:26:34 +00:00
parent 8165e1d37f
commit f7ae070b2f
8 changed files with 335 additions and 96 deletions

View File

@ -35,4 +35,5 @@ abstract class Elements {
public static final String CUSTOM_AUTH_RPOVIDER = "custom-authentication-provider";
public static final String X509 = "x509";
public static final String FILTER_INVOCATION_DEFINITION_SOURCE = "filter-invocation-definition-source";
public static final String LDAP_PASSWORD_COMPARE = "password-compare";
}

View File

@ -1,21 +1,23 @@
package org.springframework.security.config;
import org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.providers.ldap.LdapAuthenticationProvider;
import org.springframework.security.providers.ldap.authenticator.BindAuthenticator;
import org.springframework.security.providers.ldap.authenticator.PasswordComparisonAuthenticator;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;
/**
* Experimental "security:ldap" namespace configuration.
* Ldap authentication provider namespace configuration.
*
* @author Luke Taylor
* @version $Id$
@ -23,36 +25,64 @@ import org.w3c.dom.Element;
*/
public class LdapProviderBeanDefinitionParser implements BeanDefinitionParser {
private Log logger = LogFactory.getLog(getClass());
private static final String ATT_AUTH_TYPE = "auth-type";
private static final String ATT_SERVER = "server-ref";
private static final String OPT_DEFAULT_DN_PATTERN = "uid={0},ou=people";
private static final String DEF_GROUP_CONTEXT = "ou=groups";
private static final String DEF_GROUP_SEARCH_FILTER = "(uniqueMember={0})";
private static final String ATT_USER_DN_PATTERN = "user-dn-pattern";
private static final String ATT_USER_PASSWORD= "password-attribute";
private static final String DEF_USER_SEARCH_FILTER="uid={0}";
public BeanDefinition parse(Element elt, ParserContext parserContext) {
String server = elt.getAttribute(ATT_SERVER);
if (!StringUtils.hasText(server)) {
server = BeanIds.CONTEXT_SOURCE;
RuntimeBeanReference contextSource = LdapUserServiceBeanDefinitionParser.parseServerReference(elt, parserContext);
RootBeanDefinition searchBean = LdapUserServiceBeanDefinitionParser.parseSearchBean(elt, parserContext);
String userDnPattern = elt.getAttribute(ATT_USER_DN_PATTERN);
String[] userDnPatternArray = new String[0];
if (StringUtils.hasText(userDnPattern)) {
userDnPatternArray = new String[] {userDnPattern};
// TODO: Validate the pattern and make sure it is a valid DN.
} else if (searchBean == null) {
logger.info("No search information or DN pattern specified. Using default search filter '" + DEF_USER_SEARCH_FILTER + "'");
searchBean = new RootBeanDefinition(FilterBasedLdapUserSearch.class);
searchBean.setSource(elt);
searchBean.getConstructorArgumentValues().addIndexedArgumentValue(0, "");
searchBean.getConstructorArgumentValues().addIndexedArgumentValue(1, DEF_USER_SEARCH_FILTER);
searchBean.getConstructorArgumentValues().addIndexedArgumentValue(2, contextSource);
}
RuntimeBeanReference contextSource = new RuntimeBeanReference(server);
RootBeanDefinition bindAuthenticator = new RootBeanDefinition(BindAuthenticator.class);
bindAuthenticator.getConstructorArgumentValues().addGenericArgumentValue(contextSource);
bindAuthenticator.getPropertyValues().addPropertyValue("userDnPatterns", new String[] {OPT_DEFAULT_DN_PATTERN});
RootBeanDefinition authoritiesPopulator = new RootBeanDefinition(DefaultLdapAuthoritiesPopulator.class);
authoritiesPopulator.getConstructorArgumentValues().addGenericArgumentValue(contextSource);
authoritiesPopulator.getConstructorArgumentValues().addGenericArgumentValue(DEF_GROUP_CONTEXT);
// TODO: Change to using uniqueMember as default
// authoritiesPopulator.getPropertyValues().addPropertyValue("groupSearchFilter", DEF_GROUP_SEARCH_FILTER);
RootBeanDefinition authenticator = new RootBeanDefinition(BindAuthenticator.class);
Element passwordCompareElt = DomUtils.getChildElementByTagName(elt, Elements.LDAP_PASSWORD_COMPARE);
if (passwordCompareElt != null) {
authenticator = new RootBeanDefinition(PasswordComparisonAuthenticator.class);
String passwordAttribute = passwordCompareElt.getAttribute(ATT_USER_PASSWORD);
if (StringUtils.hasText(passwordAttribute)) {
authenticator.getPropertyValues().addPropertyValue("passwordAttributeName", passwordAttribute);
}
Element passwordEncoderElement = DomUtils.getChildElementByTagName(passwordCompareElt, Elements.PASSWORD_ENCODER);
if (passwordEncoderElement != null) {
PasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElement, parserContext);
authenticator.getPropertyValues().addPropertyValue("passwordEncoder", pep.getPasswordEncoder());
if (pep.getSaltSource() != null) {
parserContext.getReaderContext().warning("Salt source information isn't valid when used with LDAP", passwordEncoderElement);
}
}
}
authenticator.getConstructorArgumentValues().addGenericArgumentValue(contextSource);
authenticator.getPropertyValues().addPropertyValue("userDnPatterns", userDnPatternArray);
if (searchBean != null) {
authenticator.getPropertyValues().addPropertyValue("userSearch", searchBean);
}
RootBeanDefinition ldapProvider = new RootBeanDefinition(LdapAuthenticationProvider.class);
ldapProvider.getConstructorArgumentValues().addGenericArgumentValue(bindAuthenticator);
ldapProvider.getConstructorArgumentValues().addGenericArgumentValue(authoritiesPopulator);
ldapProvider.getConstructorArgumentValues().addGenericArgumentValue(authenticator);
ldapProvider.getConstructorArgumentValues().addGenericArgumentValue(LdapUserServiceBeanDefinitionParser.parseAuthoritiesPopulator(elt, parserContext));
LdapConfigUtils.registerPostProcessorIfNecessary(parserContext.getRegistry());

View File

@ -17,13 +17,14 @@ import org.w3c.dom.Element;
* @since 2.0
*/
public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServiceBeanDefinitionParser {
private 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 = "";
public static final String ATT_GROUP_SEARCH_FILTER = "group-search-filter";
public static final String ATT_GROUP_SEARCH_BASE = "group-search-base";
public static final String ATT_GROUP_ROLE_ATTRIBUTE = "group-role-attribute";
public static final String DEF_GROUP_SEARCH_FILTER = "(uniqueMember={0})";
public static final String DEF_GROUP_SEARCH_BASE = "ou=groups";
@ -32,26 +33,60 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
}
protected void doParse(Element elt, ParserContext parserContext, BeanDefinitionBuilder builder) {
if (!StringUtils.hasText(elt.getAttribute(ATT_USER_SEARCH_FILTER))) {
parserContext.getReaderContext().error("User search filter must be supplied", elt);
}
builder.addConstructorArg(parseSearchBean(elt, parserContext));
builder.addConstructorArg(parseAuthoritiesPopulator(elt, parserContext));
LdapConfigUtils.registerPostProcessorIfNecessary(parserContext.getRegistry());
}
static RootBeanDefinition parseSearchBean(Element elt, ParserContext parserContext) {
String userSearchFilter = elt.getAttribute(ATT_USER_SEARCH_FILTER);
String userSearchBase = elt.getAttribute(ATT_USER_SEARCH_BASE);
Object source = parserContext.extractSource(elt);
if (StringUtils.hasText(userSearchBase)) {
if(!StringUtils.hasText(userSearchFilter)) {
parserContext.getReaderContext().error(ATT_USER_SEARCH_BASE + " cannot be used without a " + ATT_USER_SEARCH_FILTER, source);
}
} else {
userSearchBase = DEF_USER_SEARCH_BASE;
}
if (!StringUtils.hasText(userSearchFilter)) {
return null;
}
RootBeanDefinition search = new RootBeanDefinition(FilterBasedLdapUserSearch.class);
search.setSource(source);
search.getConstructorArgumentValues().addIndexedArgumentValue(0, userSearchBase);
search.getConstructorArgumentValues().addIndexedArgumentValue(1, userSearchFilter);
search.getConstructorArgumentValues().addIndexedArgumentValue(2, parseServerReference(elt, parserContext));
return search;
}
static RuntimeBeanReference parseServerReference(Element elt, ParserContext parserContext) {
String server = elt.getAttribute(ATT_SERVER);
if (!StringUtils.hasText(server)) {
server = BeanIds.CONTEXT_SOURCE;
}
String userSearchFilter = elt.getAttribute(ATT_USER_SEARCH_FILTER);
if (!StringUtils.hasText(userSearchFilter)) {
parserContext.getReaderContext().error("User search filter must be supplied", elt);
}
String userSearchBase = elt.getAttribute(ATT_USER_SEARCH_BASE);
if (!StringUtils.hasText(userSearchBase)) {
userSearchBase = DEF_USER_SEARCH_BASE;
}
RuntimeBeanReference contextSource = new RuntimeBeanReference(server);
contextSource.setSource(parserContext.extractSource(elt));
return contextSource;
}
static RootBeanDefinition parseAuthoritiesPopulator(Element elt, ParserContext parserContext) {
String groupSearchFilter = elt.getAttribute(ATT_GROUP_SEARCH_FILTER);
String groupSearchBase = elt.getAttribute(ATT_GROUP_SEARCH_BASE);
String groupRoleAttribute = elt.getAttribute(ATT_GROUP_ROLE_ATTRIBUTE);
if (!StringUtils.hasText(groupSearchFilter)) {
groupSearchFilter = DEF_GROUP_SEARCH_FILTER;
@ -60,25 +95,17 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
if (!StringUtils.hasText(groupSearchBase)) {
groupSearchBase = DEF_GROUP_SEARCH_BASE;
}
Object source = parserContext.extractSource(elt);
RuntimeBeanReference contextSource = new RuntimeBeanReference(server);
RootBeanDefinition search = new RootBeanDefinition(FilterBasedLdapUserSearch.class);
search.setSource(source);
search.getConstructorArgumentValues().addIndexedArgumentValue(0, userSearchBase);
search.getConstructorArgumentValues().addIndexedArgumentValue(1, userSearchFilter);
search.getConstructorArgumentValues().addIndexedArgumentValue(2, contextSource);
RootBeanDefinition populator = new RootBeanDefinition(DefaultLdapAuthoritiesPopulator.class);
populator.setSource(source);
populator.getConstructorArgumentValues().addIndexedArgumentValue(0, contextSource);
populator.setSource(parserContext.extractSource(elt));
populator.getConstructorArgumentValues().addIndexedArgumentValue(0, parseServerReference(elt, parserContext));
populator.getConstructorArgumentValues().addIndexedArgumentValue(1, groupSearchBase);
populator.getPropertyValues().addPropertyValue("groupSearchFilter", groupSearchFilter);
builder.addConstructorArg(search);
builder.addConstructorArg(populator);
LdapConfigUtils.registerPostProcessorIfNecessary(parserContext.getRegistry());
if (StringUtils.hasLength(groupRoleAttribute)) {
populator.getPropertyValues().addPropertyValue("groupRoleAttribute", groupRoleAttribute);
}
return populator;
}
}

View File

@ -2,6 +2,7 @@ package org.springframework.security.config;
import org.springframework.security.providers.encoding.Md4PasswordEncoder;
import org.springframework.security.providers.encoding.Md5PasswordEncoder;
import org.springframework.security.providers.encoding.PlaintextPasswordEncoder;
import org.springframework.security.providers.encoding.ShaPasswordEncoder;
import org.springframework.security.providers.encoding.BaseDigestPasswordEncoder;
import org.springframework.security.providers.ldap.authenticator.LdapShaPasswordEncoder;
@ -32,6 +33,7 @@ public class PasswordEncoderParser {
static final String ATT_REF = "ref";
static final String ATT_HASH = "hash";
static final String ATT_BASE_64 = "base64";
static final String OPT_HASH_PLAINTEXT = "plaintext";
static final String OPT_HASH_SHA = "sha";
static final String OPT_HASH_MD4 = "md4";
static final String OPT_HASH_MD5 = "md5";
@ -41,6 +43,7 @@ public class PasswordEncoderParser {
static {
ENCODER_CLASSES = new HashMap();
ENCODER_CLASSES.put(OPT_HASH_PLAINTEXT, PlaintextPasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_SHA, ShaPasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_MD4, Md4PasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_MD5, Md5PasswordEncoder.class);

View File

@ -10,7 +10,7 @@ start = http | ldap-server | authentication-provider | ldap-authentication-provi
hash =
## Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm.
attribute hash {"sha" | "md5" | "md4" | "{sha}" | "{ssha}"}
attribute hash {"plaintext" | "sha" | "md5" | "md4" | "{sha}" | "{ssha}"}
base64 =
## Whether a string should be base64 encoded
attribute base64 {"true" | "false"}
@ -66,33 +66,71 @@ ldap-server.attlist &=
## Optional root suffix for the embedded LDAP server. Default is "dc=springframework,dc=org"
attribute root { xsd:string }?
ldap-server-ref-attribute =
## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.
attribute server-ref {xsd:string}
group-search-filter-attribute =
## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.
attribute group-search-filter {xsd:string}
group-search-base-attribute =
## Search base for group membership searches. Defaults to "ou=groups".
attribute group-search-base {xsd:string}
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}?
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}
ldap-user-service =
element ldap-user-service {ldap-us.attlist}
ldap-us.attlist &= id?
ldap-us.attlist &=
## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.
attribute server-ref {xsd:string}?
ldap-server-ref-attribute?
ldap-us.attlist &=
attribute user-search-filter {xsd:string}
user-search-filter-attribute?
ldap-us.attlist &=
## Search base for user searches. Defaults to "".
attribute user-search-base {xsd:string}?
user-search-base-attribute?
ldap-us.attlist &=
## Group search filter. Defaults to (uniqueMember={0}).
attribute group-search-filter {xsd:string}?
group-search-filter-attribute?
ldap-us.attlist &=
## Search base for group membership searches. Defaults to "ou=groups".
attribute group-search-base {xsd:string}?
group-search-base-attribute?
ldap-us.attlist &=
group-role-attribute-attribute?
ldap-authentication-provider =
## Sets up an ldap authentication provider
element ldap-authentication-provider {ldap-ap.attlist, empty}
element ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}
ldap-ap.attlist &=
## The server to authenticate against.
attribute server-ref {xsd:string}?
ldap-server-ref-attribute?
ldap-ap.attlist &=
user-search-base-attribute?
ldap-ap.attlist &=
user-search-filter-attribute?
ldap-ap.attlist &=
group-search-base-attribute?
ldap-ap.attlist &=
group-search-filter-attribute?
ldap-ap.attlist &=
group-role-attribute-attribute?
ldap-ap.attlist &=
## A specific pattern used to build the user's DN, for example "uid={0},ou=people". The key "{0}" must be present and will be substituted with the username.
attribute user-dn-pattern {xsd:string}?
password-compare-element =
## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user
element password-compare {password-compare.attlist, password-encoder?}
password-compare.attlist &=
## The attribute in the directory which contains the user password. Defaults to "userPassword".
attribute password-attribute {xsd:string}?
password-compare.attlist &=
hash?
intercept-methods =
## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods

View File

@ -7,6 +7,7 @@
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="plaintext"/>
<xs:enumeration value="sha"/>
<xs:enumeration value="md5"/>
<xs:enumeration value="md4"/>
@ -96,6 +97,7 @@
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="plaintext"/>
<xs:enumeration value="sha"/>
<xs:enumeration value="md5"/>
<xs:enumeration value="md4"/>
@ -186,6 +188,44 @@
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="ldap-server-ref-attribute">
<xs:attribute name="server-ref" use="required" type="xs:string">
<xs:annotation>
<xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using &lt;ldap-server&gt; with no Id), that server will be used. </xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="group-search-filter-attribute">
<xs:attribute name="group-search-filter" use="required" type="xs:string">
<xs:annotation>
<xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="group-search-base-attribute">
<xs:attribute name="group-search-base" use="required" type="xs:string">
<xs:annotation>
<xs:documentation>Search base for group membership searches. Defaults to "ou=groups".</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="user-search-filter-attribute">
<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:annotation>
<xs:documentation>Search base for user searches. Defaults to "".</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="group-role-attribute-attribute">
<xs:attribute name="group-role-attribute" use="required" type="xs:string">
<xs:annotation>
<xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to "cn".</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="ldap-user-service">
<xs:complexType>
<xs:attributeGroup ref="security:ldap-us.attlist"/>
@ -202,15 +242,11 @@
<xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using &lt;ldap-server&gt; with no Id), that server will be used. </xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="user-search-filter" use="required" type="xs:string"/>
<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:attributeGroup ref="security:user-search-base-attribute"/>
<xs:attribute name="group-search-filter" type="xs:string">
<xs:annotation>
<xs:documentation>Group search filter. Defaults to (uniqueMember={0}).</xs:documentation>
<xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="group-search-base" type="xs:string">
@ -218,21 +254,84 @@
<xs:documentation>Search base for group membership searches. Defaults to "ou=groups".</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="group-role-attribute" type="xs:string">
<xs:annotation>
<xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to "cn".</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="ldap-authentication-provider">
<xs:annotation>
<xs:documentation>Sets up an ldap authentication provider</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" ref="security:password-compare"/>
</xs:sequence>
<xs:attributeGroup ref="security:ldap-ap.attlist"/>
</xs:complexType>
</xs:element>
<xs:attributeGroup name="ldap-ap.attlist">
<xs:attribute name="server-ref" type="xs:string">
<xs:annotation>
<xs:documentation>The server to authenticate against. </xs:documentation>
<xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using &lt;ldap-server&gt; with no Id), that server will be used. </xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attributeGroup ref="security:user-search-base-attribute"/>
<xs:attribute name="user-search-filter" type="xs:string"/>
<xs:attribute name="group-search-base" type="xs:string">
<xs:annotation>
<xs:documentation>Search base for group membership searches. Defaults to "ou=groups".</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 parameter is the DN of the user.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="group-role-attribute" type="xs:string">
<xs:annotation>
<xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to "cn".</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="user-dn-pattern" type="xs:string">
<xs:annotation>
<xs:documentation>A specific pattern used to build the user's DN, for example "uid={0},ou=people". The key "{0}" must be present and will be substituted with the username.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="password-compare">
<xs:annotation>
<xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" ref="security:password-encoder"/>
</xs:sequence>
<xs:attributeGroup ref="security:password-compare.attlist"/>
</xs:complexType>
</xs:element>
<xs:attributeGroup name="password-compare.attlist">
<xs:attribute name="password-attribute" type="xs:string">
<xs:annotation>
<xs:documentation>The attribute in the directory which contains the user password. Defaults to "userPassword".</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="hash">
<xs:annotation>
<xs:documentation>Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="plaintext"/>
<xs:enumeration value="sha"/>
<xs:enumeration value="md5"/>
<xs:enumeration value="md4"/>
<xs:enumeration value="{sha}"/>
<xs:enumeration value="{ssha}"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="intercept-methods">
<xs:annotation>

View File

@ -1,5 +1,6 @@
package org.springframework.security.config;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.security.providers.ProviderManager;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.providers.ldap.LdapAuthenticationProvider;
@ -12,7 +13,7 @@ import org.junit.After;
/**
* @author luke
* @author Luke Taylor
* @version $Id$
*/
public class LdapProviderBeanDefinitionParserTests {
@ -28,22 +29,43 @@ public class LdapProviderBeanDefinitionParserTests {
@Test
public void simpleProviderAuthenticatesCorrectly() {
appCtx = new InMemoryXmlApplicationContext("<ldap-server /> <ldap-authentication-provider />");
setContext("<ldap-server /> <ldap-authentication-provider group-search-filter='member={0}' />");
LdapAuthenticationProvider provider = getProvider();
Authentication auth = provider.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
LdapUserDetailsImpl ben = (LdapUserDetailsImpl) auth.getPrincipal();
assertEquals(2, ben.getAuthorities().length);
}
@Test(expected = SecurityConfigurationException.class)
public void missingServerEltCausesConfigException() {
setContext("<ldap-authentication-provider />");
}
@Test
public void supportsPasswordComparisonAuthentication() {
setContext("<ldap-server /> " +
"<ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>" +
" <password-compare password-attribute='uid'>" +
" <password-encoder hash='plaintext'/>" +
" </password-compare>" +
"</ldap-authentication-provider>");
LdapAuthenticationProvider provider = getProvider();
provider.authenticate(new UsernamePasswordAuthenticationToken("ben", "ben"));
}
private void setContext(String context) {
appCtx = new InMemoryXmlApplicationContext(context);
}
private LdapAuthenticationProvider getProvider() {
ProviderManager authManager = (ProviderManager) appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER);
assertEquals(1, authManager.getProviders().size());
LdapAuthenticationProvider provider = (LdapAuthenticationProvider) authManager.getProviders().get(0);
Authentication auth = provider.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
LdapUserDetailsImpl ben = (LdapUserDetailsImpl) auth.getPrincipal();
assertEquals(2, ben.getAuthorities().length);
}
@Test(expected = SecurityConfigurationException.class)
public void missingServerEltCausesConfigException() {
appCtx = new InMemoryXmlApplicationContext("<ldap-authentication-provider />");
}
return provider;
}
}

View File

@ -1,12 +1,16 @@
package org.springframework.security.config;
import java.util.Set;
import org.springframework.security.GrantedAuthorityImpl;
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.junit.Test;
import org.junit.After;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
/**
* @author Luke Taylor
@ -35,7 +39,9 @@ public class LdapUserServiceBeanDefinitionParserTests {
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetails ben = uds.loadUserByUsername("ben");
assertEquals(2, ben.getAuthorities().length);
Set authorities = AuthorityUtils.authorityArrayToSet(ben.getAuthorities());
assertEquals(2, authorities.size());
assertTrue(authorities.contains(new GrantedAuthorityImpl("ROLE_DEVELOPERS")));
}
@Test
@ -48,6 +54,19 @@ public class LdapUserServiceBeanDefinitionParserTests {
assertEquals("Joe Smeth", joe.getUsername());
}
@Test
public void differentGroupRoleAttributeWorksAsExpected() throws Exception {
setContext("<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' group-role-attribute='ou' group-search-filter='member={0}' /><ldap-server />");
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetails ben = uds.loadUserByUsername("ben");
Set authorities = AuthorityUtils.authorityArrayToSet(ben.getAuthorities());
assertEquals(2, authorities.size());
assertTrue(authorities.contains(new GrantedAuthorityImpl("ROLE_DEVELOPER")));
}
@Test
public void isSupportedByAuthenticationProviderElement() {
setContext(