From dfc541df2b8a135de6b69417ab0a5806a5d9d23d Mon Sep 17 00:00:00 2001 From: Fernando Marino Date: Wed, 30 Apr 2025 07:19:34 +0000 Subject: [PATCH 1/2] allow multiple roles to be fetched from group role attribute --- .../DefaultLdapAuthoritiesPopulator.java | 39 +++++++------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java b/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java index 458218ed13..3988ce630d 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java +++ b/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java @@ -16,13 +16,7 @@ package org.springframework.security.ldap.userdetails; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.function.Function; import javax.naming.directory.SearchControls; @@ -150,7 +144,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator /** * The mapping function to be used to populate authorities. */ - private Function>, GrantedAuthority> authorityMapper; + private Function>, List> authorityMapper; /** * Constructor for group search scenarios. userRoleAttributes may still be @@ -171,18 +165,15 @@ else if (groupSearchBase.isEmpty()) { logger.info("Will perform group search from the context source base since groupSearchBase is empty."); } this.authorityMapper = (record) -> { - List roles = record.get(this.groupRoleAttribute); - if (CollectionUtils.isEmpty(roles)) { - return null; - } - String role = roles.get(0); - if (role == null) { - return null; - } - if (this.convertToUpperCase) { - role = role.toUpperCase(Locale.ROOT); - } - return new SimpleGrantedAuthority(this.rolePrefix + role); + List roles = record.getOrDefault(this.groupRoleAttribute, Collections.EMPTY_LIST); + return roles.stream().filter(r -> r != null).map(role -> { + if (this.convertToUpperCase) { + return role.toUpperCase(Locale.ROOT); + } + else { + return role; + } + }).map(role -> new SimpleGrantedAuthority(this.rolePrefix + role)).toList(); }; } @@ -234,10 +225,7 @@ public Set getGroupMembershipRoles(String userDn, String usern new String[] { this.groupRoleAttribute }); logger.debug(LogMessage.of(() -> "Found roles from search " + userRoles)); for (Map> role : userRoles) { - GrantedAuthority authority = this.authorityMapper.apply(role); - if (authority != null) { - authorities.add(authority); - } + authorities.addAll(this.authorityMapper.apply(role)); } return authorities; } @@ -311,7 +299,8 @@ public void setIgnorePartialResultException(boolean ignore) { * {@link GrantedAuthority} given the context record. * @param authorityMapper the mapping function */ - public void setAuthorityMapper(Function>, GrantedAuthority> authorityMapper) { + public void setAuthorityMapper( + Function>, List> authorityMapper) { Assert.notNull(authorityMapper, "authorityMapper must not be null"); this.authorityMapper = authorityMapper; } From 05817c1f9c332ab639c0c30310ee6d5827506cbd Mon Sep 17 00:00:00 2001 From: Fernando Marino Date: Wed, 30 Apr 2025 07:36:12 +0000 Subject: [PATCH 2/2] fix integration test and generics usage --- .../DefaultLdapAuthoritiesPopulatorTests.java | 3 ++- .../DefaultLdapAuthoritiesPopulator.java | 15 ++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java index 1d6e2d6664..179367820e 100644 --- a/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java +++ b/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java @@ -17,6 +17,7 @@ package org.springframework.security.ldap.userdetails; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -188,7 +189,7 @@ public void customAuthoritiesMappingFunction() { this.populator.setAuthorityMapper((record) -> { String dn = record.get(SpringSecurityLdapTemplate.DN_KEY).get(0); String role = record.get(this.populator.getGroupRoleAttribute()).get(0); - return new LdapAuthority(role, dn); + return Collections.singletonList(new LdapAuthority(role, dn)); }); DirContextAdapter ctx = new DirContextAdapter( diff --git a/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java b/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java index 3988ce630d..f1835f1c17 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java +++ b/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java @@ -16,14 +16,8 @@ package org.springframework.security.ldap.userdetails; -import java.util.*; -import java.util.function.Function; - -import javax.naming.directory.SearchControls; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.core.log.LogMessage; import org.springframework.ldap.core.ContextSource; import org.springframework.ldap.core.DirContextOperations; @@ -32,7 +26,10 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.ldap.SpringSecurityLdapTemplate; import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; + +import javax.naming.directory.SearchControls; +import java.util.*; +import java.util.function.Function; /** * The default strategy for obtaining user role information from the directory. @@ -165,8 +162,8 @@ else if (groupSearchBase.isEmpty()) { logger.info("Will perform group search from the context source base since groupSearchBase is empty."); } this.authorityMapper = (record) -> { - List roles = record.getOrDefault(this.groupRoleAttribute, Collections.EMPTY_LIST); - return roles.stream().filter(r -> r != null).map(role -> { + List roles = record.getOrDefault(this.groupRoleAttribute, Collections.emptyList()); + return roles.stream().filter(Objects::nonNull).map(role -> { if (this.convertToUpperCase) { return role.toUpperCase(Locale.ROOT); }