diff --git a/config/src/main/java/org/springframework/security/config/authentication/UserServiceBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/authentication/UserServiceBeanDefinitionParser.java index e3ccf386f4..d3c8ac13d7 100644 --- a/config/src/main/java/org/springframework/security/config/authentication/UserServiceBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/authentication/UserServiceBeanDefinitionParser.java @@ -58,7 +58,7 @@ public class UserServiceBeanDefinitionParser extends AbstractUserDetailsServiceB return; } - if(CollectionUtils.isEmpty(userElts)) { + if (CollectionUtils.isEmpty(userElts)) { throw new BeanDefinitionStoreException("You must supply user definitions, either with <" + ELT_USER + "> child elements or a " + "properties file (using the '" + ATT_PROPERTIES + "' attribute)" ); } diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc index 5e4266a07a..77f5f076a8 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc +++ b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc @@ -560,7 +560,7 @@ ap.attlist &= user-service-ref? user-service = - ## Creates an in-memory UserDetailsService from a properties file or a list of "user" child elements. + ## Creates an in-memory UserDetailsService from a properties file or a list of "user" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required. element user-service {id? & (properties-file | (user*))} properties-file = attribute properties {xsd:token}? diff --git a/config/src/test/java/org/springframework/security/config/authentication/UserServiceBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/authentication/UserServiceBeanDefinitionParserTests.java index 703647e12b..ed77c5c65b 100644 --- a/config/src/test/java/org/springframework/security/config/authentication/UserServiceBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/authentication/UserServiceBeanDefinitionParserTests.java @@ -72,21 +72,34 @@ public class UserServiceBeanDefinitionParserTests { Long.parseLong(joe.getPassword()); } + @Test + public void worksWithOpenIDUrlsAsNames() { + setContext( + "" + + " " + + " " + + ""); + UserDetailsService userService = (UserDetailsService) appContext.getBean("service"); + assertEquals("http://joe.myopenid.com/", userService.loadUserByUsername("http://joe.myopenid.com/").getUsername()); + assertEquals("https://www.google.com/accounts/o8/id?id=MPtOaenBIk5yzW9n7n9", + userService.loadUserByUsername("https://www.google.com/accounts/o8/id?id=MPtOaenBIk5yzW9n7n9").getUsername()); + } + @Test public void disabledAndEmbeddedFlagsAreSupported() { setContext( "" + " " + - " " + + " " + ""); UserDetailsService userService = (UserDetailsService) appContext.getBean("service"); UserDetails joe = userService.loadUserByUsername("joe"); assertFalse(joe.isAccountNonLocked()); + // Check case-sensitive lookup SEC-1432 UserDetails bob = userService.loadUserByUsername("bob"); assertFalse(bob.isEnabled()); } - @Test(expected=FatalBeanException.class) public void userWithBothPropertiesAndEmbeddedUsersThrowsException() { setContext( diff --git a/core/src/main/java/org/springframework/security/core/userdetails/memory/UserMap.java b/core/src/main/java/org/springframework/security/core/userdetails/memory/UserMap.java index 2554d2762b..1d3c99c629 100644 --- a/core/src/main/java/org/springframework/security/core/userdetails/memory/UserMap.java +++ b/core/src/main/java/org/springframework/security/core/userdetails/memory/UserMap.java @@ -18,15 +18,18 @@ package org.springframework.security.core.userdetails.memory; import java.util.HashMap; import java.util.Map; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.util.Assert; /** * Used by {@link InMemoryDaoImpl} to store a list of users and their corresponding granted authorities. + *

+ * Usernames are used as the lookup key and are stored in lower case, to allow case-insensitive lookups. So this class + * should not be used if usernames need to be case-sensitive. * * @author Ben Alex */ @@ -37,7 +40,7 @@ public class UserMap { //~ Instance fields ================================================================================================ - private Map userMap = new HashMap(); + private final Map userMap = new HashMap(); //~ Methods ======================================================================================================== @@ -90,6 +93,9 @@ public class UserMap { * @since 1.1 */ public void setUsers(Map users) { - this.userMap = users; + userMap.clear(); + for (Map.Entry entry : users.entrySet()) { + userMap.put(entry.getKey().toLowerCase(), entry.getValue()); + } } }