Add configurable mapping function to map authorities

This commit is contained in:
Giovanni Lovato 2018-10-11 18:28:17 +02:00 committed by Eleftheria Stein-Kousathana
parent 2d26be9446
commit 63607ee213
2 changed files with 63 additions and 14 deletions

View File

@ -18,9 +18,12 @@ package org.springframework.security.ldap.userdetails;
import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.junit.*; import org.junit.*;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.ContextSource; import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DirContextAdapter;
@ -29,11 +32,10 @@ import org.springframework.ldap.core.DistinguishedName;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.ldap.ApacheDsContainerConfig; import org.springframework.security.ldap.ApacheDsContainerConfig;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import java.util.*;
/** /**
* *
* @author Luke Taylor * @author Luke Taylor
@ -185,4 +187,25 @@ public class DefaultLdapAuthoritiesPopulatorTests {
assertThat(authorities).as("Should have 1 role").hasSize(1); assertThat(authorities).as("Should have 1 role").hasSize(1);
assertThat(authorities.contains("ROLE_MANAGER")).isTrue(); assertThat(authorities.contains("ROLE_MANAGER")).isTrue();
} }
@Test
public void customAuthoritiesMappingFunction() {
populator.setAuthorityMapper(record -> {
String dn = record.get(SpringSecurityLdapTemplate.DN_KEY).get(0);
String role = record.get(populator.getGroupRoleAttribute()).get(0);
return new LdapAuthority(role, dn);
});
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName(
"cn=mouse\\, jerry,ou=people,dc=springframework,dc=org"));
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx, "notused");
assertThat(authorities).allMatch(LdapAuthority.class::isInstance);
}
@Test(expected = IllegalArgumentException.class)
public void customAuthoritiesMappingFunctionThrowsIfNull() {
populator.setAuthorityMapper(null);
}
} }

View File

@ -20,13 +20,14 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import javax.naming.directory.SearchControls; import javax.naming.directory.SearchControls;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.ldap.core.ContextSource; import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextOperations; import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.LdapTemplate; import org.springframework.ldap.core.LdapTemplate;
@ -137,15 +138,22 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
* The pattern to be used for the user search. {0} is the user's DN * The pattern to be used for the user search. {0} is the user's DN
*/ */
private String groupSearchFilter = "(member={0})"; private String groupSearchFilter = "(member={0})";
/** /**
* The role prefix that will be prepended to each role name * The role prefix that will be prepended to each role name
*/ */
private String rolePrefix = "ROLE_"; private String rolePrefix = "ROLE_";
/** /**
* Should we convert the role name to uppercase * Should we convert the role name to uppercase
*/ */
private boolean convertToUpperCase = true; private boolean convertToUpperCase = true;
/**
* The mapping function to be used to populate authorities.
*/
private Function<Map<String, List<String>>, GrantedAuthority> authorityMapper;
// ~ Constructors // ~ Constructors
// =================================================================================================== // ===================================================================================================
@ -171,6 +179,16 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
logger.info( logger.info(
"groupSearchBase is empty. Searches will be performed from the context source base"); "groupSearchBase is empty. Searches will be performed from the context source base");
} }
this.authorityMapper = record -> {
String role = record.get(this.groupRoleAttribute).get(0);
if (this.convertToUpperCase) {
role = role.toUpperCase();
}
return new SimpleGrantedAuthority(this.rolePrefix + role);
};
} }
// ~ Methods // ~ Methods
@ -238,21 +256,18 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
+ " in search base '" + getGroupSearchBase() + "'"); + " in search base '" + getGroupSearchBase() + "'");
} }
Set<String> userRoles = getLdapTemplate().searchForSingleAttributeValues( Set<Map<String, List<String>>> userRoles = getLdapTemplate()
getGroupSearchBase(), this.groupSearchFilter, .searchForMultipleAttributeValues(getGroupSearchBase(),
new String[] { userDn, username }, this.groupRoleAttribute); this.groupSearchFilter,
new String[] { userDn, username },
new String[] { this.groupRoleAttribute });
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Roles from search: " + userRoles); logger.debug("Roles from search: " + userRoles);
} }
for (String role : userRoles) { for (Map<String, List<String>> role : userRoles) {
authorities.add(authorityMapper.apply(role));
if (this.convertToUpperCase) {
role = role.toUpperCase();
}
authorities.add(new SimpleGrantedAuthority(this.rolePrefix + role));
} }
return authorities; return authorities;
@ -325,6 +340,17 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
getLdapTemplate().setIgnorePartialResultException(ignore); getLdapTemplate().setIgnorePartialResultException(ignore);
} }
/**
* Sets the mapping function which will be used to create instances of {@link GrantedAuthority}
* given the context record.
*
* @param authorityMapper the mapping function
*/
public void setAuthorityMapper(Function<Map<String, List<String>>, GrantedAuthority> authorityMapper) {
Assert.notNull(authorityMapper, "authorityMapper must not be null");
this.authorityMapper = authorityMapper;
}
/** /**
* Returns the current LDAP template. Method available so that classes extending this * Returns the current LDAP template. Method available so that classes extending this
* can override the template used * can override the template used