diff --git a/core-tiger/src/test/resources/org/springframework/security/config/annotated-method-security.xml b/core-tiger/src/test/resources/org/springframework/security/config/annotated-method-security.xml index 6299a1ae76..c09672582b 100644 --- a/core-tiger/src/test/resources/org/springframework/security/config/annotated-method-security.xml +++ b/core-tiger/src/test/resources/org/springframework/security/config/annotated-method-security.xml @@ -10,11 +10,11 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc - + - + \ No newline at end of file diff --git a/core/src/main/java/org/springframework/security/config/AbstractUserDetailsServiceBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/AbstractUserDetailsServiceBeanDefinitionParser.java new file mode 100644 index 0000000000..7bf5bc7969 --- /dev/null +++ b/core/src/main/java/org/springframework/security/config/AbstractUserDetailsServiceBeanDefinitionParser.java @@ -0,0 +1,31 @@ +package org.springframework.security.config; + +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.util.StringUtils; + +import org.w3c.dom.Element; + +/** + * @author Luke Taylor + * @version $Id$ + */ +public class AbstractUserDetailsServiceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { + + protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException { + String id = super.resolveId(element, definition, parserContext); + + if (StringUtils.hasText(id)) { + return id; + } + + if (parserContext.getRegistry().containsBeanDefinition(BeanIds.USER_DETAILS_SERVICE)) { + throw new SecurityConfigurationException("No id supplied in <" + element.getNodeName() + "> and another " + + "bean is already registered as " + BeanIds.USER_DETAILS_SERVICE); + } + + return BeanIds.USER_DETAILS_SERVICE; + } +} diff --git a/core/src/main/java/org/springframework/security/config/AuthenticationProviderBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/AuthenticationProviderBeanDefinitionParser.java index 899c15ba1c..52130f2bf9 100644 --- a/core/src/main/java/org/springframework/security/config/AuthenticationProviderBeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/AuthenticationProviderBeanDefinitionParser.java @@ -1,18 +1,67 @@ package org.springframework.security.config; +import org.springframework.security.providers.dao.DaoAuthenticationProvider; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.util.xml.DomUtils; +import org.springframework.util.StringUtils; + import org.w3c.dom.Element; /** + * Wraps a UserDetailsService bean with a DaoAuthenticationProvider and registers the latter with the + * ProviderManager. + * * @author Luke Taylor * @version $Id$ */ class AuthenticationProviderBeanDefinitionParser implements BeanDefinitionParser { + private static String ATT_REF = "ref"; + static final String ATT_DATA_SOURCE = "data-source"; public BeanDefinition parse(Element element, ParserContext parserContext) { - // TODO: Proper implementation + RootBeanDefinition authProvider = new RootBeanDefinition(DaoAuthenticationProvider.class); + + Element passwordEncoderElt = DomUtils.getChildElementByTagName(element, Elements.PASSWORD_ENCODER); + + if (passwordEncoderElt != null) { + //TODO: Parse password encoder object and add to dao provider + } + + ConfigUtils.getRegisteredProviders(parserContext).add(authProvider); + + String ref = element.getAttribute(ATT_REF); + Element userServiceElt = DomUtils.getChildElementByTagName(element, Elements.USER_SERVICE); + Element jdbcUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.JDBC_USER_SERVICE); + + if (StringUtils.hasText(ref)) { + if (userServiceElt != null || jdbcUserServiceElt != null) { + throw new SecurityConfigurationException("The ref attribute cannot be used in combination with child" + + "elements '" + Elements.USER_SERVICE + "' or '" + Elements.JDBC_USER_SERVICE + "'"); + } + + authProvider.getPropertyValues().addPropertyValue("userDetailsService", new RuntimeBeanReference(ref)); + + return null; + } + + // Use the child elements to create the UserDetailsService + BeanDefinition userDetailsService; + + if (userServiceElt != null) { + userDetailsService = new UserServiceBeanDefinitionParser().parse(userServiceElt, parserContext); + } else if (jdbcUserServiceElt != null) { + userDetailsService = new UserServiceBeanDefinitionParser().parse(userServiceElt, parserContext); + } else { + throw new SecurityConfigurationException(Elements.AUTHENTICATION_PROVIDER + + " requireds a UserDetailsService" ); + } + + authProvider.getPropertyValues().addPropertyValue("userDetailsService", userDetailsService); + return null; } } diff --git a/core/src/main/java/org/springframework/security/config/BeanIds.java b/core/src/main/java/org/springframework/security/config/BeanIds.java index 84e8bc354a..18a0553845 100644 --- a/core/src/main/java/org/springframework/security/config/BeanIds.java +++ b/core/src/main/java/org/springframework/security/config/BeanIds.java @@ -2,11 +2,11 @@ package org.springframework.security.config; /** * Contains all the default Bean IDs created by the namespace support in Spring Security 2. - * + * * @author Ben Alex * @version $Id$ */ -public class BeanIds { +public abstract class BeanIds { /** Package protected as end users shouldn't really be using this BFPP directly */ static final String INTERCEPT_METHODS_BEAN_FACTORY_POST_PROCESSOR = "_interceptMethodsBeanfactoryPP"; @@ -39,5 +39,5 @@ public class BeanIds { public static final String METHOD_DEFINITION_SOURCE_ADVISOR = "_methodDefinitionSourceAdvisor"; public static final String SECURITY_ANNOTATION_ATTRIBUTES = "_securityAnnotationAttributes"; public static final String METHOD_DEFINITION_ATTRIBUTES = "_methodDefinitionAttributes"; - + } diff --git a/core/src/main/java/org/springframework/security/config/ConfigUtils.java b/core/src/main/java/org/springframework/security/config/ConfigUtils.java index f4a295aac8..73d15eb1b7 100644 --- a/core/src/main/java/org/springframework/security/config/ConfigUtils.java +++ b/core/src/main/java/org/springframework/security/config/ConfigUtils.java @@ -117,9 +117,7 @@ public abstract class ConfigUtils { throw new IllegalArgumentException("More than one AuthenticationManager registered."); } - AuthenticationManager accessMgr = (AuthenticationManager) authManagers.values().toArray()[0]; - - return accessMgr; + return (AuthenticationManager) authManagers.values().toArray()[0]; } static ManagedList getRegisteredProviders(ParserContext parserContext) { diff --git a/core/src/main/java/org/springframework/security/config/Elements.java b/core/src/main/java/org/springframework/security/config/Elements.java index e617f21c41..e7f8607164 100644 --- a/core/src/main/java/org/springframework/security/config/Elements.java +++ b/core/src/main/java/org/springframework/security/config/Elements.java @@ -6,15 +6,13 @@ package org.springframework.security.config; * @author Ben Alex * @version $Id$ */ -class Elements { +abstract class Elements { public static final String USER_SERVICE = "user-service"; public static final String JDBC_USER_SERVICE = "jdbc-user-service"; - public static final String CUSTOM_USER_SERVICE = "custom-user-service"; public static final String FILTER_CHAIN_MAP = "filter-chain-map"; public static final String INTERCEPT_METHODS = "intercept-methods"; public static final String AUTHENTICATION_PROVIDER = "authentication-provider"; - public static final String REPOSITORY = "repository"; public static final String HTTP = "http"; public static final String LDAP = "ldap"; public static final String PROTECT = "protect"; @@ -26,5 +24,6 @@ class Elements { public static final String ANONYMOUS = "anonymous"; public static final String FILTER_CHAIN = "filter-chain"; public static final String ANNOTATION_DRIVEN = "annotation-driven"; - + public static final String PASSWORD_ENCODER = "password-encoder"; + public static final String SALT_SOURCE = "salt-source"; } diff --git a/core/src/main/java/org/springframework/security/config/JdbcUserServiceBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/JdbcUserServiceBeanDefinitionParser.java new file mode 100644 index 0000000000..ba298f510e --- /dev/null +++ b/core/src/main/java/org/springframework/security/config/JdbcUserServiceBeanDefinitionParser.java @@ -0,0 +1,31 @@ +package org.springframework.security.config; + +import org.springframework.security.userdetails.jdbc.JdbcUserDetailsManager; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; + +import org.w3c.dom.Element; + +/** + * @author Luke Taylor + * @version $Id$ + */ +public class JdbcUserServiceBeanDefinitionParser extends AbstractUserDetailsServiceBeanDefinitionParser { + static final String ATT_DATA_SOURCE = "data-source"; + + protected Class getBeanClass(Element element) { + return JdbcUserDetailsManager.class; + } + + protected void doParse(Element element, BeanDefinitionBuilder builder) { + // TODO: Set authenticationManager property + String dataSource = element.getAttribute(ATT_DATA_SOURCE); + // An explicit dataSource was specified, so use it + if (dataSource != null) { + builder.addPropertyReference("dataSource", dataSource); + } else { + // TODO: Have some sensible fallback if dataSource not specified, eg autowire + throw new SecurityConfigurationException(ATT_DATA_SOURCE + " is required for " + + Elements.JDBC_USER_SERVICE ); + } + } +} diff --git a/core/src/main/java/org/springframework/security/config/PasswordEncoderParser.java b/core/src/main/java/org/springframework/security/config/PasswordEncoderParser.java new file mode 100644 index 0000000000..3e3890ba82 --- /dev/null +++ b/core/src/main/java/org/springframework/security/config/PasswordEncoderParser.java @@ -0,0 +1,88 @@ +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.ShaPasswordEncoder; +import org.springframework.security.providers.encoding.BaseDigestPasswordEncoder; +import org.springframework.security.providers.ldap.authenticator.LdapShaPasswordEncoder; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; + +import org.w3c.dom.Element; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Map; +import java.util.HashMap; + +/** + * Stateful parser for the element. + * + * Will produce a PasswordEncoder and (optionally) a SaltSource. + * + * @author Luke Taylor + * @version $Id$ + */ +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_SHA = "sha"; + static final String OPT_HASH_MD4 = "md4"; + static final String OPT_HASH_MD5 = "md5"; + static final String OPT_HASH_LDAP_SHA = "{sha}"; + + static final Map ENCODER_CLASSES; + + static { + ENCODER_CLASSES = new HashMap(); + ENCODER_CLASSES.put(OPT_HASH_SHA, ShaPasswordEncoder.class); + ENCODER_CLASSES.put(OPT_HASH_MD4, Md4PasswordEncoder.class); + ENCODER_CLASSES.put(OPT_HASH_MD5, Md5PasswordEncoder.class); + ENCODER_CLASSES.put(OPT_HASH_LDAP_SHA, LdapShaPasswordEncoder.class); + } + + private Log logger = LogFactory.getLog(getClass()); + + private BeanDefinition passwordEncoder; + private BeanDefinition saltSource; + + + public PasswordEncoderParser(Element element, ParserContext parserContext) { + parse(element, parserContext); + } + + private void parse(Element element, ParserContext parserContext) { + String hash = element.getAttribute(ATT_HASH); + boolean useBase64 = StringUtils.hasText(element.getAttribute(ATT_BASE_64)); + + Class beanClass = (Class) ENCODER_CLASSES.get(hash); + passwordEncoder = new RootBeanDefinition(beanClass); + + if (useBase64) { + if (beanClass.isAssignableFrom(BaseDigestPasswordEncoder.class)) { + passwordEncoder.getPropertyValues().addPropertyValue("encodeHashAsBase64", "true"); + } else { + logger.warn(ATT_BASE_64 + " isn't compatible with " + OPT_HASH_LDAP_SHA + " and will be ignored"); + } + } + + Element saltSourceElt = DomUtils.getChildElementByTagName(element, Elements.SALT_SOURCE); + + if (saltSourceElt != null) { + saltSource = new SaltSourceBeanDefinitionParser().parse(saltSourceElt, parserContext); + } + } + + public BeanDefinition getPasswordEncoder() { + return passwordEncoder; + } + + public BeanDefinition getSaltSource() { + return saltSource; + } +} diff --git a/core/src/main/java/org/springframework/security/config/RepositoryBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/RepositoryBeanDefinitionParser.java deleted file mode 100644 index 320629fa81..0000000000 --- a/core/src/main/java/org/springframework/security/config/RepositoryBeanDefinitionParser.java +++ /dev/null @@ -1,119 +0,0 @@ -package org.springframework.security.config; - -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.beans.factory.xml.BeanDefinitionParser; -import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.security.providers.dao.DaoAuthenticationProvider; -import org.springframework.security.providers.encoding.Md4PasswordEncoder; -import org.springframework.security.providers.encoding.Md5PasswordEncoder; -import org.springframework.security.providers.encoding.PasswordEncoder; -import org.springframework.security.providers.encoding.PlaintextPasswordEncoder; -import org.springframework.security.providers.encoding.ShaPasswordEncoder; -import org.springframework.security.userdetails.jdbc.JdbcUserDetailsManager; -import org.springframework.util.StringUtils; -import org.springframework.util.xml.DomUtils; -import org.w3c.dom.Element; - -/** - * Processes the top-level "repository" element. - * - *

A "repository" element is used to indicate a UserDetailsService or equivalent. - * - * @author Ben Alex - * @version $Id$ - */ -class RepositoryBeanDefinitionParser implements BeanDefinitionParser { - - static final String ATT_DATA_SOURCE = "data-source"; - static final String ATT_REF = "ref"; - - static final String ATT_CREATE_PROVIDER = "create-provider"; - static final String DEF_CREATE_PROVIDER = "true"; - - static final String ATT_HASH = "hash"; - static final String DEF_HASH_PLAINTEXT = "plaintext"; - static final String OPT_HASH_SHA_HEX = "sha-hex"; - static final String OPT_HASH_SHA_BASE64 = "sha-base64"; - static final String OPT_HASH_MD4_HEX = "md4-hex"; - static final String OPT_HASH_MD4_BASE64 = "md4-base64"; - static final String OPT_HASH_MD5_HEX = "md5-hex"; - static final String OPT_HASH_MD5_BASE64 = "md5-base64"; - - public BeanDefinition parse(Element element, ParserContext parserContext) { - boolean createProvider = true; - String createProviderAtt = element.getAttribute(ATT_CREATE_PROVIDER); - if (StringUtils.hasText(createProviderAtt) && "false".equals(createProviderAtt)) { - createProvider = false; - } - - if (createProvider) { - ConfigUtils.registerProviderManagerIfNecessary(parserContext); - } - - Element userServiceElt = DomUtils.getChildElementByTagName(element, Elements.USER_SERVICE); - Element jdbcUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.JDBC_USER_SERVICE); - Element customUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.CUSTOM_USER_SERVICE); - - if (userServiceElt != null) { - BeanDefinition userDetailsService = new UserServiceBeanDefinitionParser().parse(userServiceElt, parserContext); - createDaoAuthenticationProviderIfRequired(createProvider, userServiceElt.getAttribute(ATT_HASH), userDetailsService, parserContext); - } - - if (jdbcUserServiceElt != null) { - // TODO: Set authenticationManager property - // TODO: Have some sensible fallback if dataSource not specified, eg autowire - BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(JdbcUserDetailsManager.class); - String dataSource = jdbcUserServiceElt.getAttribute(ATT_DATA_SOURCE); - // An explicit dataSource was specified, so use it - builder.addPropertyReference("dataSource", dataSource); - parserContext.getRegistry().registerBeanDefinition(BeanIds.JDBC_USER_DETAILS_MANAGER, builder.getBeanDefinition()); - createDaoAuthenticationProviderIfRequired(createProvider, jdbcUserServiceElt.getAttribute(ATT_HASH), builder.getBeanDefinition(), parserContext); - } - - if (customUserServiceElt != null) { - String ref = customUserServiceElt.getAttribute(ATT_REF); - BeanDefinition userDetailsService = parserContext.getRegistry().getBeanDefinition(ref); - createDaoAuthenticationProviderIfRequired(createProvider, customUserServiceElt.getAttribute(ATT_HASH), userDetailsService, parserContext); - } - - return null; - } - - private void createDaoAuthenticationProviderIfRequired(boolean createProvider, String hash, BeanDefinition userDetailsService, ParserContext parserContext) { - if (createProvider) { - if (!StringUtils.hasText(hash)) { - hash = DEF_HASH_PLAINTEXT; - } - RootBeanDefinition authProvider = new RootBeanDefinition(DaoAuthenticationProvider.class); - authProvider.getPropertyValues().addPropertyValue("userDetailsService", userDetailsService); - - PasswordEncoder pwdEnc = null; - if (OPT_HASH_MD4_HEX.equals(hash)) { - pwdEnc = new Md4PasswordEncoder(); - ((Md4PasswordEncoder)pwdEnc).setEncodeHashAsBase64(false); - } else if (OPT_HASH_MD4_BASE64.equals(hash)) { - pwdEnc = new Md4PasswordEncoder(); - ((Md4PasswordEncoder)pwdEnc).setEncodeHashAsBase64(true); - } else if (OPT_HASH_MD5_HEX.equals(hash)) { - pwdEnc = new Md5PasswordEncoder(); - ((Md5PasswordEncoder)pwdEnc).setEncodeHashAsBase64(false); - } else if (OPT_HASH_MD5_BASE64.equals(hash)) { - pwdEnc = new Md5PasswordEncoder(); - ((Md5PasswordEncoder)pwdEnc).setEncodeHashAsBase64(true); - } else if (OPT_HASH_SHA_HEX.equals(hash)) { - pwdEnc = new ShaPasswordEncoder(); - ((ShaPasswordEncoder)pwdEnc).setEncodeHashAsBase64(false); - } else if (OPT_HASH_SHA_BASE64.equals(hash)) { - pwdEnc = new ShaPasswordEncoder(); - ((ShaPasswordEncoder)pwdEnc).setEncodeHashAsBase64(true); - } else { - pwdEnc = new PlaintextPasswordEncoder(); - } - authProvider.getPropertyValues().addPropertyValue("passwordEncoder", pwdEnc); - - ConfigUtils.getRegisteredProviders(parserContext).add(authProvider); - } - } -} diff --git a/core/src/main/java/org/springframework/security/config/SaltSourceBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/SaltSourceBeanDefinitionParser.java new file mode 100644 index 0000000000..5e97e44b32 --- /dev/null +++ b/core/src/main/java/org/springframework/security/config/SaltSourceBeanDefinitionParser.java @@ -0,0 +1,43 @@ +package org.springframework.security.config; + +import org.springframework.security.providers.dao.salt.ReflectionSaltSource; +import org.springframework.security.providers.dao.salt.SystemWideSaltSource; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.util.StringUtils; + +import org.w3c.dom.Element; + +/** + * @author Luke Taylor + * @version $Id$ + */ +public class SaltSourceBeanDefinitionParser implements BeanDefinitionParser { + static final String ATT_USER_PROPERTY = "user-property"; + static final String ATT_SYSTEM_WIDE = "system-wide"; + + public BeanDefinition parse(Element element, ParserContext parserContext) { + BeanDefinition saltSource; + String userProperty = element.getAttribute(ATT_USER_PROPERTY); + + if (StringUtils.hasText(userProperty)) { + saltSource = new RootBeanDefinition(ReflectionSaltSource.class); + saltSource.getPropertyValues().addPropertyValue("userPropertyToUse", userProperty); + + return saltSource; + } + + String systemWideSalt = element.getAttribute(ATT_SYSTEM_WIDE); + + if (StringUtils.hasText(systemWideSalt)) { + saltSource = new RootBeanDefinition(SystemWideSaltSource.class); + saltSource.getPropertyValues().addPropertyValue("systemWideSalt", systemWideSalt); + + return saltSource; + } + + throw new SecurityConfigurationException(Elements.SALT_SOURCE + " requires an attribute"); + } +} diff --git a/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java b/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java index de981ad511..2e11d16af7 100644 --- a/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java +++ b/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java @@ -9,19 +9,19 @@ import org.springframework.beans.factory.xml.NamespaceHandlerSupport; * @author Ben Alex * @version $Id$ */ -public class SecurityNamespaceHandler extends NamespaceHandlerSupport { +public class SecurityNamespaceHandler extends NamespaceHandlerSupport { public void init() { // Parsers registerBeanDefinitionParser(Elements.LDAP, new LdapBeanDefinitionParser()); registerBeanDefinitionParser(Elements.HTTP, new HttpSecurityBeanDefinitionParser()); registerBeanDefinitionParser(Elements.USER_SERVICE, new UserServiceBeanDefinitionParser()); - registerBeanDefinitionParser(Elements.REPOSITORY, new RepositoryBeanDefinitionParser()); + registerBeanDefinitionParser(Elements.JDBC_USER_SERVICE, new JdbcUserServiceBeanDefinitionParser()); registerBeanDefinitionParser(Elements.AUTHENTICATION_PROVIDER, new AuthenticationProviderBeanDefinitionParser()); - registerBeanDefinitionParser(Elements.ANNOTATION_DRIVEN, new AnnotationDrivenBeanDefinitionParser()); + registerBeanDefinitionParser(Elements.ANNOTATION_DRIVEN, new AnnotationDrivenBeanDefinitionParser()); // Decorators registerBeanDefinitionDecorator(Elements.INTERCEPT_METHODS, new InterceptMethodsBeanDefinitionDecorator()); - registerBeanDefinitionDecorator(Elements.FILTER_CHAIN_MAP, new FilterChainMapBeanDefinitionDecorator()); + registerBeanDefinitionDecorator(Elements.FILTER_CHAIN_MAP, new FilterChainMapBeanDefinitionDecorator()); } } diff --git a/core/src/main/java/org/springframework/security/config/UserServiceBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/UserServiceBeanDefinitionParser.java index 16fa2f5b04..a5fa4e7f18 100644 --- a/core/src/main/java/org/springframework/security/config/UserServiceBeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/UserServiceBeanDefinitionParser.java @@ -1,16 +1,11 @@ package org.springframework.security.config; -import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; -import org.springframework.beans.factory.xml.ParserContext; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.security.userdetails.memory.InMemoryDaoImpl; import org.springframework.security.userdetails.memory.UserMap; import org.springframework.security.userdetails.User; import org.springframework.security.util.AuthorityUtils; import org.springframework.util.xml.DomUtils; -import org.springframework.util.StringUtils; import org.w3c.dom.Element; import java.util.List; @@ -21,7 +16,7 @@ import java.util.Iterator; * @author Ben Alex * @version $Id$ */ -public class UserServiceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { +public class UserServiceBeanDefinitionParser extends AbstractUserDetailsServiceBeanDefinitionParser { static final String ATT_PASSWORD = "password"; static final String ATT_NAME = "name"; @@ -47,16 +42,4 @@ public class UserServiceBeanDefinitionParser extends AbstractSingleBeanDefinitio builder.addPropertyValue("userMap", users); } - - protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException { - String id = super.resolveId(element, definition, parserContext); - - if (StringUtils.hasText(id)) { - return id; - } - - // TODO: Check for duplicate using default id here. - - return BeanIds.USER_DETAILS_SERVICE; - } } diff --git a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc index 216dfe9490..69b97e3717 100644 --- a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc +++ b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc @@ -6,11 +6,15 @@ datatypes xsd = "http://www.w3.org/2001/XMLSchema-datatypes" default namespace = "http://www.springframework.org/schema/security" -start = http | ldap | repository +start = http | ldap hash = - ## Defines the type of hashing used on user passwords. If unspecified, "plaintext" is nominated, which indicates that the passwords are not hashed. We recommend strongly against using MD4, as it is a very weak hashing algorithm. - attribute hash {"plaintext" | "sha-hex" | "sha-base64" | "md5-hex" | "md5-base64" | "md4-hex" | "md4-base64"} + ## 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}"} + +base64 = + ## Whether a string should be base64 encoded + attribute base64 {"true" | "false"} path-type = ## Defines the type of pattern used to specify URL paths (either JDK 1.4-compatible regular expressions, or Apache Ant expressions). Defaults to "ant" if unspecified. @@ -23,6 +27,27 @@ port = url = ## Specifies a URL. attribute url { xsd:string } + +id = + ## A bean identifier, used for referring to the bean elsewhere in the context. + attribute id {xsd:ID} +ref = + ## Defines a reference to a Spring bean id. + attribute ref {xsd:IDREF} + +password-encoder = + ## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example. + element password-encoder {ref | (hash? & base64? & salt-source*)} + +salt-source = + element salt-source {user-property | system-wide} +user-property = + ## A property of the UserDetails object which will be used as salt by a password encoder. Typically something like "username" might be used. + attribute user-property {xsd:string} +system-wide = + ## A single value that will be used as the salt for a password encoder. + attribute system-wide {xsd:string} + ldap = ## Sets up an ldap authentication provider, optionally with an embedded ldap server @@ -33,7 +58,7 @@ ldap.attlist &= ldap.attlist &= ## Explicitly specify an ldif file resource to load into the embedded server - [ a:defaultValue = "classpath:*.ldif" ] attribute ldif { xsd:string }? + [ a:defaultValue = "classpath*:*.ldif" ] attribute ldif { xsd:string }? 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 @@ -179,17 +204,15 @@ anonymous.attlist &= ## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to "ROLE_ANONYMOUS". attribute granted-authority {xsd:string}? -repository = - element repository {repository.attlist, (user-service | jdbc-user-service | custom-user-service)} -repository.attlist &= - ## Indicates the repository should have an authentication provider created. If unspecified, defaults to true. - attribute create-provider {"true" | "false"}? +authentication-provider = + ## Indicates that the contained user-service should be used as an authentication source. May either refer to an external UserDetailsService bean by id (using the "ref" attribute) or contain a child element which creates the service. + element authentication-provider {(ref | (user-service | jdbc-user-service)) & password-encoder} +ap.attlist &= + attribute ref {xsd:IDREF} user-service = - element user-service {user-service.attlist, (user*)} -user-service.attlist &= - hash? -user-service.attlist &= + element user-service {id? & (properties-file | (user*))} +properties-file = attribute properties {xsd:string}* user = @@ -207,19 +230,7 @@ user.attlist &= jdbc-user-service = ## Causes creation of a JDBC-based UserDetailsService. - element jdbc-user-service {jdbc-user-service.attlist} -jdbc-user-service.attlist &= - hash? + element jdbc-user-service {id? & jdbc-user-service.attlist} jdbc-user-service.attlist &= ## The bean ID of the DataSource which provides the required tables. attribute data-source {xsd:string} - -custom-user-service = - ## Represents a UserDetailsService implementation that has been provided by you. Registration here will automate the creation of a DaoAuthenticationProvider that delegates to your UserDetailsService implementation. - element custom-user-service {custom-user-service.attlist} -custom-user-service.attlist &= - hash? -custom-user-service.attlist &= - ## The bean ID of your custom UserDetailsService implementation. - attribute ref {xsd:string} - diff --git a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd index 5682ea4ce2..5fe8afc80f 100644 --- a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd +++ b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd @@ -3,17 +3,28 @@ - Defines the type of hashing used on user passwords. If unspecified, "plaintext" is nominated, which indicates that the passwords are not hashed. We recommend strongly against using MD4, as it is a very weak hashing algorithm. + Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm. - - - - - - - + + + + + + + + + + + + + Whether a string should be base64 encoded + + + + + @@ -45,6 +56,88 @@ + + + + A bean identifier, used for referring to the bean elsewhere in the context. + + + + + + + Defines a reference to a Spring bean id. + + + + + + element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example. + + + + + + + + Defines a reference to a Spring bean id. + + + + + Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm. + + + + + + + + + + + + + + Whether a string should be base64 encoded + + + + + + + + + + + + + + + A property of the UserDetails object which will be used as salt by a password encoder. Typically something like "username" might be used. + + + + + A single value that will be used as the salt for a password encoder. + + + + + + + + A property of the UserDetails object which will be used as salt by a password encoder. Typically something like "username" might be used. + + + + + + + A single value that will be used as the salt for a password encoder. + + + Sets up an ldap authentication provider, optionally with an embedded ldap server @@ -64,7 +157,7 @@ Specifies an IP port number. Used to configure an embedded LDAP server, for example. - + Explicitly specify an ldif file resource to load into the embedded server @@ -380,54 +473,42 @@ - + + + Indicates that the contained user-service should be used as an authentication source. May either refer to an external UserDetailsService bean by id (using the "ref" attribute) or contain a child element which creates the service. + - - - - + + + + + + - + + + Defines a reference to a Spring bean id. + + - - - - Indicates the repository should have an authentication provider created. If unspecified, defaults to true. - - - - - - - - + + - + + + A bean identifier, used for referring to the bean elsewhere in the context. + + + - - - - Defines the type of hashing used on user passwords. If unspecified, "plaintext" is nominated, which indicates that the passwords are not hashed. We recommend strongly against using MD4, as it is a very weak hashing algorithm. - - - - - - - - - - - - - + @@ -460,61 +541,19 @@ Causes creation of a JDBC-based UserDetailsService. + + + A bean identifier, used for referring to the bean elsewhere in the context. + + - - - Defines the type of hashing used on user passwords. If unspecified, "plaintext" is nominated, which indicates that the passwords are not hashed. We recommend strongly against using MD4, as it is a very weak hashing algorithm. - - - - - - - - - - - - - The bean ID of the DataSource which provides the required tables. - - - Represents a UserDetailsService implementation that has been provided by you. Registration here will automate the creation of a DaoAuthenticationProvider that delegates to your UserDetailsService implementation. - - - - - - - - - Defines the type of hashing used on user passwords. If unspecified, "plaintext" is nominated, which indicates that the passwords are not hashed. We recommend strongly against using MD4, as it is a very weak hashing algorithm. - - - - - - - - - - - - - - - - The bean ID of your custom UserDetailsService implementation. - - - diff --git a/core/src/test/java/org/springframework/security/config/CustomUserDetailsService.java b/core/src/test/java/org/springframework/security/config/CustomUserDetailsService.java deleted file mode 100644 index 8914365b4d..0000000000 --- a/core/src/test/java/org/springframework/security/config/CustomUserDetailsService.java +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.security.config; - -import org.springframework.dao.DataAccessException; -import org.springframework.security.GrantedAuthority; -import org.springframework.security.GrantedAuthorityImpl; -import org.springframework.security.userdetails.User; -import org.springframework.security.userdetails.UserDetails; -import org.springframework.security.userdetails.UserDetailsService; -import org.springframework.security.userdetails.UsernameNotFoundException; - - -/** - * @author Ben Alex - * @version $Id: DataSourcePopulator.java 2291 2007-12-03 02:56:52Z benalex $ - */ -public class CustomUserDetailsService implements UserDetailsService { - - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { - if ("rod".equals(username)) { - return new User("rod", "koala", true, true, true, true, new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_FOO")}); - } - throw new UsernameNotFoundException("unsupported by stub"); - } - -} diff --git a/core/src/test/java/org/springframework/security/config/CustomUserDetailsTests.java b/core/src/test/java/org/springframework/security/config/CustomUserDetailsTests.java deleted file mode 100644 index aca8682795..0000000000 --- a/core/src/test/java/org/springframework/security/config/CustomUserDetailsTests.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.springframework.security.config; - -import static org.junit.Assert.assertTrue; - -import java.util.List; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.security.providers.ProviderManager; -import org.springframework.security.providers.dao.DaoAuthenticationProvider; - -/** - * @author Ben Alex - * @version $Id$ - */ -public class CustomUserDetailsTests { - private static ClassPathXmlApplicationContext appContext; - - @BeforeClass - public static void loadContext() { - appContext = new ClassPathXmlApplicationContext("org/springframework/security/config/custom-user-details.xml"); - } - - @AfterClass - public static void closeAppContext() { - if (appContext != null) { - appContext.close(); - } - } - - @Test - public void testUsersFound() { - CustomUserDetailsService mgr = (CustomUserDetailsService) appContext.getBean("myDetails"); - assertTrue(mgr.loadUserByUsername("rod") != null); - } - - @Test - public void testProviderManagerSetup() { - ProviderManager manager = (ProviderManager) appContext.getBean(BeanIds.AUTHENTICATION_MANAGER); - List providers = manager.getProviders(); - assertTrue(providers.size() == 1); - assertTrue(providers.iterator().next() instanceof DaoAuthenticationProvider); - DaoAuthenticationProvider provider = (DaoAuthenticationProvider) providers.iterator().next(); - assertTrue(provider.getUserDetailsService() instanceof CustomUserDetailsService); - } -} diff --git a/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java b/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java index b575962f64..1442581c0c 100644 --- a/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java +++ b/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java @@ -13,6 +13,7 @@ import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter; import org.springframework.security.util.FilterChainProxy; import org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.beans.BeansException; import org.junit.AfterClass; import static org.junit.Assert.assertEquals; @@ -32,7 +33,11 @@ public class HttpSecurityBeanDefinitionParserTests { @BeforeClass public static void loadContext() { - appContext = new ClassPathXmlApplicationContext("org/springframework/security/config/http-security.xml"); + try { + appContext = new ClassPathXmlApplicationContext("org/springframework/security/config/http-security.xml"); + } catch (BeansException e) { + e.printStackTrace(); + } } @AfterClass @@ -70,7 +75,7 @@ public class HttpSecurityBeanDefinitionParserTests { assertTrue(filters.next() instanceof AuthenticationProcessingFilter); assertTrue(filters.next() instanceof DefaultLoginPageGeneratingFilter); assertTrue(filters.next() instanceof BasicProcessingFilter); - assertTrue(filters.next() instanceof SecurityContextHolderAwareRequestFilter); + assertTrue(filters.next() instanceof SecurityContextHolderAwareRequestFilter); assertTrue(filters.next() instanceof RememberMeProcessingFilter); assertTrue(filters.next() instanceof ExceptionTranslationFilter); assertTrue(filters.next() instanceof FilterSecurityInterceptor); diff --git a/core/src/test/java/org/springframework/security/config/JdbcUserDetailsTests.java b/core/src/test/java/org/springframework/security/config/JdbcUserServiceBeanDefinitionParserTests.java similarity index 63% rename from core/src/test/java/org/springframework/security/config/JdbcUserDetailsTests.java rename to core/src/test/java/org/springframework/security/config/JdbcUserServiceBeanDefinitionParserTests.java index 4f88a3c23c..2f71244c65 100644 --- a/core/src/test/java/org/springframework/security/config/JdbcUserDetailsTests.java +++ b/core/src/test/java/org/springframework/security/config/JdbcUserServiceBeanDefinitionParserTests.java @@ -14,9 +14,10 @@ import org.springframework.security.userdetails.jdbc.JdbcUserDetailsManager; /** * @author Ben Alex + * @author Luke Taylor * @version $Id$ */ -public class JdbcUserDetailsTests { +public class JdbcUserServiceBeanDefinitionParserTests { private static ClassPathXmlApplicationContext appContext; @BeforeClass @@ -32,18 +33,13 @@ public class JdbcUserDetailsTests { } @Test - public void testUsersFound() { - JdbcUserDetailsManager mgr = (JdbcUserDetailsManager) appContext.getBean(BeanIds.JDBC_USER_DETAILS_MANAGER); + public void validUsernameIsFound() { + JdbcUserDetailsManager mgr = (JdbcUserDetailsManager) appContext.getBean(BeanIds.USER_DETAILS_SERVICE); assertTrue(mgr.loadUserByUsername("rod") != null); } - + @Test - public void testProviderManagerSetup() { - ProviderManager manager = (ProviderManager) appContext.getBean(BeanIds.AUTHENTICATION_MANAGER); - List providers = manager.getProviders(); - assertTrue(providers.size() == 1); - assertTrue(providers.iterator().next() instanceof DaoAuthenticationProvider); - DaoAuthenticationProvider provider = (DaoAuthenticationProvider) providers.iterator().next(); - assertTrue(provider.getUserDetailsService() instanceof JdbcUserDetailsManager); + public void beanIdIsParsedCorrectly() { + JdbcUserDetailsManager mgr = (JdbcUserDetailsManager) appContext.getBean("customUserService"); } } diff --git a/core/src/test/java/org/springframework/security/config/LdapBeanDefinitionParserTests.java b/core/src/test/java/org/springframework/security/config/LdapBeanDefinitionParserTests.java index b5ee2a4aa0..c8c1e0d486 100644 --- a/core/src/test/java/org/springframework/security/config/LdapBeanDefinitionParserTests.java +++ b/core/src/test/java/org/springframework/security/config/LdapBeanDefinitionParserTests.java @@ -8,11 +8,13 @@ import org.springframework.security.userdetails.ldap.LdapUserDetailsImpl; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.ldap.core.LdapTemplate; import org.springframework.ldap.core.support.BaseLdapPathContextSource; +import org.springframework.beans.BeansException; import org.junit.AfterClass; import static org.junit.Assert.*; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.Assert; /** @@ -24,7 +26,13 @@ public class LdapBeanDefinitionParserTests { @BeforeClass public static void loadContext() { - appContext = new ClassPathXmlApplicationContext("org/springframework/security/config/ldap-embedded-default.xml"); + + try { + appContext = new ClassPathXmlApplicationContext("org/springframework/security/config/ldap-embedded-default.xml"); + } catch (BeansException e) { + e.printStackTrace(); + Assert.fail("Exception loading context " + e); + } } @AfterClass diff --git a/core/src/test/resources/org/springframework/security/config/custom-user-details.xml b/core/src/test/resources/org/springframework/security/config/custom-user-details.xml deleted file mode 100644 index 69da51c164..0000000000 --- a/core/src/test/resources/org/springframework/security/config/custom-user-details.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/core/src/test/resources/org/springframework/security/config/http-security.xml b/core/src/test/resources/org/springframework/security/config/http-security.xml index 93277442c4..c724377644 100644 --- a/core/src/test/resources/org/springframework/security/config/http-security.xml +++ b/core/src/test/resources/org/springframework/security/config/http-security.xml @@ -25,12 +25,12 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc - + - + diff --git a/core/src/test/resources/org/springframework/security/config/jdbc-user-details.xml b/core/src/test/resources/org/springframework/security/config/jdbc-user-details.xml index 3cadc29bd4..e0e8c04d48 100644 --- a/core/src/test/resources/org/springframework/security/config/jdbc-user-details.xml +++ b/core/src/test/resources/org/springframework/security/config/jdbc-user-details.xml @@ -17,8 +17,8 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc - - - + + + \ No newline at end of file diff --git a/core/src/test/resources/org/springframework/security/config/method-security.xml b/core/src/test/resources/org/springframework/security/config/method-security.xml index 38db19b452..d34240fce4 100644 --- a/core/src/test/resources/org/springframework/security/config/method-security.xml +++ b/core/src/test/resources/org/springframework/security/config/method-security.xml @@ -16,11 +16,11 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc - + - + \ No newline at end of file