diff --git a/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java b/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java new file mode 100644 index 0000000000..b794a4641d --- /dev/null +++ b/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java @@ -0,0 +1,101 @@ +package org.springframework.security.core.authority.mapping; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.util.Assert; + +import java.util.*; + +/** + * Simple one-to-one {@code GrantedAuthoritiesMapper} which allows for case conversion of the authority name + * and the addition of a string prefix (which defaults to {@code ROLE_}). + * + * @author Luke Taylor + * @since 3.1 + */ +public final class SimpleAuthorityMapper implements GrantedAuthoritiesMapper, InitializingBean { + private GrantedAuthority defaultAuthority; + private String prefix = "ROLE_"; + private boolean convertToUpperCase = false; + private boolean convertToLowerCase = false; + + public void afterPropertiesSet() throws Exception { + Assert.isTrue(!(convertToUpperCase && convertToLowerCase), + "Either convertToUpperCase or convertToLowerCase can be set to true, but not both"); + } + + /** + * Creates a mapping of the supplied authorities based on the case-conversion and prefix settings. + * The mapping will be one-to-one unless duplicates are produced during the conversion. If a default + * authority has been set, this will also be assigned to each mapping. + * + * @param authorities the original authorities + * + * @return the converted set of authorities + */ + public Set mapAuthorities(Collection authorities) { + HashSet mapped = new HashSet(authorities.size()); + for (GrantedAuthority authority : authorities) { + mapped.add(mapAuthority(authority.getAuthority())); + } + + if (defaultAuthority != null) { + mapped.add(defaultAuthority); + } + + return mapped; + } + + private GrantedAuthority mapAuthority(String name) { + if (convertToUpperCase) { + name = name.toUpperCase(); + } else if (convertToLowerCase) { + name = name.toLowerCase(); + } + + if (prefix.length() > 0 && !name.startsWith(prefix)) { + name = prefix + name; + } + + return new SimpleGrantedAuthority(name); + } + + /** + * Sets the prefix which should be added to the authority name (if it doesn't already exist) + * + * @param prefix the prefix, typically to satisfy the behaviour of an {@code AccessDecisionVoter}. + */ + public void setPrefix(String prefix) { + Assert.notNull(prefix, "prefix cannot be null"); + this.prefix = prefix; + } + + /** + * Whether to convert the authority value to upper case in the mapping. + * + * @param convertToUpperCase defaults to {@code false} + */ + public void setConvertToUpperCase(boolean convertToUpperCase) { + this.convertToUpperCase = convertToUpperCase; + } + + /** + * Whether to convert the authority value to lower case in the mapping. + * + * @param convertToLowerCase defaults to {@code false} + */ + public void setConvertToLowerCase(boolean convertToLowerCase) { + this.convertToLowerCase = convertToLowerCase; + } + + /** + * Sets a default authority to be assigned to all users + * + * @param authority the name of the authority to be assigned to all users. + */ + public void setDefaultAuthority(String authority) { + Assert.hasText(authority, "The authority name cannot be set to an empty value"); + this.defaultAuthority = new SimpleGrantedAuthority(authority); + } +} diff --git a/core/src/test/java/org/springframework/security/core/authority/mapping/SimpleAuthoritiesMapperTests.java b/core/src/test/java/org/springframework/security/core/authority/mapping/SimpleAuthoritiesMapperTests.java new file mode 100644 index 0000000000..58c6d61f38 --- /dev/null +++ b/core/src/test/java/org/springframework/security/core/authority/mapping/SimpleAuthoritiesMapperTests.java @@ -0,0 +1,76 @@ +package org.springframework.security.core.authority.mapping; + + +import static org.junit.Assert.*; + +import org.junit.*; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; + +import java.util.*; + +/** + * @author Luke Taylor + */ +public class SimpleAuthoritiesMapperTests { + + @Test(expected = IllegalArgumentException.class) + public void rejectsInvalidCaseConversionFlags() throws Exception { + SimpleAuthorityMapper mapper = new SimpleAuthorityMapper(); + mapper.setConvertToLowerCase(true); + mapper.setConvertToUpperCase(true); + mapper.afterPropertiesSet(); + } + + @Test + public void defaultPrefixIsCorrectlyApplied() { + SimpleAuthorityMapper mapper = new SimpleAuthorityMapper(); + Set mapped = AuthorityUtils.authorityListToSet( + mapper.mapAuthorities(AuthorityUtils.createAuthorityList("AaA", "ROLE_bbb"))); + assertTrue(mapped.contains("ROLE_AaA")); + assertTrue(mapped.contains("ROLE_bbb")); + } + + @Test + public void caseIsConvertedCorrectly() { + SimpleAuthorityMapper mapper = new SimpleAuthorityMapper(); + mapper.setPrefix(""); + List toMap = AuthorityUtils.createAuthorityList("AaA", "Bbb"); + Set mapped = AuthorityUtils.authorityListToSet(mapper.mapAuthorities(toMap)); + assertEquals(2, mapped.size()); + assertTrue(mapped.contains("AaA")); + assertTrue(mapped.contains("Bbb")); + + mapper.setConvertToLowerCase(true); + mapped = AuthorityUtils.authorityListToSet(mapper.mapAuthorities(toMap)); + assertEquals(2, mapped.size()); + assertTrue(mapped.contains("aaa")); + assertTrue(mapped.contains("bbb")); + + mapper.setConvertToLowerCase(false); + mapper.setConvertToUpperCase(true); + mapped = AuthorityUtils.authorityListToSet(mapper.mapAuthorities(toMap)); + assertEquals(2, mapped.size()); + assertTrue(mapped.contains("AAA")); + assertTrue(mapped.contains("BBB")); + } + + @Test + public void duplicatesAreRemoved() { + SimpleAuthorityMapper mapper = new SimpleAuthorityMapper(); + mapper.setConvertToUpperCase(true); + + Set mapped = AuthorityUtils.authorityListToSet( + mapper.mapAuthorities(AuthorityUtils.createAuthorityList("AaA", "AAA"))); + assertEquals(1, mapped.size()); + } + + @Test + public void defaultAuthorityIsAssignedIfSet() throws Exception { + SimpleAuthorityMapper mapper = new SimpleAuthorityMapper(); + mapper.setDefaultAuthority("ROLE_USER"); + Set mapped = AuthorityUtils.authorityListToSet(mapper.mapAuthorities(AuthorityUtils.NO_AUTHORITIES)); + assertEquals(1, mapped.size()); + assertTrue(mapped.contains("ROLE_USER")); + } +}