浏览代码

Add configurable mapping function to map authorities

Giovanni Lovato 6 年之前
父节点
当前提交
63607ee213

+ 26 - 3
ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java

@@ -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);
+	}
 }
 }

+ 37 - 11
ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java

@@ -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(
-				getGroupSearchBase(), this.groupSearchFilter,
-				new String[] { userDn, username }, this.groupRoleAttribute);
+		Set<Map<String, List<String>>> userRoles = getLdapTemplate()
+				.searchForMultipleAttributeValues(getGroupSearchBase(),
+						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) {
-
-			if (this.convertToUpperCase) {
-				role = role.toUpperCase();
-			}
-
-			authorities.add(new SimpleGrantedAuthority(this.rolePrefix + role));
+		for (Map<String, List<String>> role : userRoles) {
+			authorities.add(authorityMapper.apply(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