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:
parent
8165e1d37f
commit
f7ae070b2f
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <ldap-server> 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 <ldap-server> 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 <ldap-server> 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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue