spring-bootspring-securityspring-security-ldap

Spring Security 5.7 LdapUserDetailsMapper Authorties Empty


Before upgrading to Spring Boot 2.7.2 (Spring Security 5.7.2) the following LDAP authentication configuration worked.

Ldap related application properties:

spring.ldap.urls = ldaps://ldap-one:636, ldaps://ldap-two:636, ldaps://ldap-three:636
spring.ldap.base = ******
spring.ldap.username = ******
spring.ldap.password = ******

Security config snippet:

@Value("${active.directory.domain}")
private String activeDirectoryDomain;
     
@Value("#{'${spring.ldap.urls}'.replaceAll(',', '')}") 
private String activeDirectoryLdapUrls;
     
@Autowired 
private CustomLdapUserDetailsMapper customLdapUserDetailsMapper;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(activeDirectoryDomain, activeDirectoryLdapUrls);
  provider.setSearchFilter("(&(objectClass=user)(sAMAccountName={1}))");
  provider.setUserDetailsContextMapper(customLdapUserDetailsMapper);
  auth.authenticationProvider(provider);
}

The ldapAuthorities parameter in our CustomLdapUserDetailsMapper contained all of the group names the user belonged to:

@Component
public class CustomLdapUserDetailsMapper extends LdapUserDetailsMapper implements Serializable {

  @Override
  public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> ldapAuthorities) {
    User user = new User();
    user.setUserDetails(super.mapUserFromContext(ctx, username, ldapAuthorities));
    user.setFirstName(ctx.getStringAttribute("givenName"));
    user.setLastName(ctx.getStringAttribute("sn"));
    user.setEmail(ctx.getStringAttribute("mail"));
    return user;
  }
}

This all worked great.

Attempting to update the security configuration per Spring Security 5.7 docs to:

@Bean
public AuthenticationManager getLdapAuthenticationManager(BaseLdapPathContextSource contextSource, CustomLdapUserDetailsMapper customLdapUserDetailsMapper) {
  LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
  factory.setUserSearchFilter("(&(objectClass=user)(sAMAccountName={0}))");
  factory.setUserDetailsContextMapper(customLdapUserDetailsMapper);
  return factory.createAuthenticationManager();
}

I've discovered that the ldapAuthorities in CustomLdapUserDetailsMapper are now empty (I'm relying on a particular authority to be present when configuring the SecurityFilterChain - not shown).

How do I resolve this problem?


Solution

  • I just needed to keep reading the docs (dang it)!

    The solution was to expose an ActiveDirectoryLdapAuthenticationProvider bean configured identically to the original configuration:

    @Bean
    public ActiveDirectoryLdapAuthenticationProvider authenticationProvider( @Value("${active.directory.domain}") String domain, @Value("#{'${spring.ldap.urls}'.replaceAll(',', '')}") String urls, CustomLdapUserDetailsMapper customLdapUserDetailsMapper) {
      ActiveDirectoryLdapAuthenticationProvider  authProvider = new ActiveDirectoryLdapAuthenticationProvider(domain, urls);
      authProvider.setSearchFilter("(&(objectClass=user)(sAMAccountName={1}))");
      authProvider.setUserDetailsContextMapper(customLdapUserDetailsMapper);
      return authProvider;
    }