Summary

Although the default search configuration for the user search on LDAP bind is supposed to return all attributes of the user the operational attributes are not included in the search result (may be LDAP server implementation specific. OpenDJ does not include them). As the attributes returned by the user search are not exposed in the LdapAuthenticationProviderConfigurer, they can only be configured by creating a new user search object and setting in the BindLdapAuthenticator using an ObjectPostProcessor.

Actual Behavior

ObjectPostProcessor is required to set returned attributes in user search for LDAP authentication.

Expected Behavior

LdapAuthenticationProviderConfigurer provides a method to set the returned attributes for the user search.

Configuration

With default returned attribute:

    @Autowired
    protected void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
        auth
                .ldapAuthentication()
                .userSearchBase(AuthenticationConfig.LDAP_USR_BASE)
                .userSearchFilter("(|(mail={0})(cn={0}))")
                .groupSearchBase("dc=groups,dc=ID")
                .groupSearchFilter("member={0}")
                .userDetailsContextMapper(new LdapIdentityProvider(contextSource))
                .contextSource(contextSource);
    }

With customized returned attributes:

    @Autowired
    protected void configureGlobal(final AuthenticationManagerBuilder auth)
            throws Exception {
        auth
                .ldapAuthentication()
                .groupSearchBase("dc=groups,dc=ID")
                .groupSearchFilter("member={0}")
                .userDetailsContextMapper(new LdapIdentityProvider(contextSource))
                .contextSource(contextSource)
                .withObjectPostProcessor(new ObjectPostProcessor<BindAuthenticator>() {

                    @Override
                    public BindAuthenticator postProcess(BindAuthenticator object) {
                        object.setUserAttributes(User.attributes()); // This line is probably unnecessary.
                        final FilterBasedLdapUserSearch ldapUserSearch =
                                new FilterBasedLdapUserSearch(AuthenticationConfig.LDAP_USR_BASE,
                                        "(|(mail={0})(cn={0}))", contextSource);
                        ldapUserSearch.setReturningAttributes(User.attributes());
                        object.setUserSearch(ldapUserSearch);
                        return object;
                    }
                });
    }

Version

org.springframework.security:spring-security-config:4.1.4.RELEASE org.springframework.security:spring-security-core:4.1.4.RELEASE org.springframework.security:spring-security-ldap:4.1.4.RELEASE

Sample

Comment From: muety

I came across this issue as well and would really appreciate having an easy, more convenient way to configure the set of attributes to fetch.

Comment From: eleftherias

We recommend users move away from using the WebSecurityConfigurerAdapter and the AuthenticationManagerBuilder. Instead users can expose the appropriate beans for their configuration.

I believe this configuration style is well suited for the issue mentioned above.

The FilterBasedLdapUserSearch can be set on the BindAuthenticator bean without needing an ObjectPostProcessor.

Below is an example:

@Bean
BindAuthenticator authenticator(BaseLdapPathContextSource contextSource) {
    BindAuthenticator authenticator = new BindAuthenticator(contextSource);
    final FilterBasedLdapUserSearch ldapUserSearch =
            new FilterBasedLdapUserSearch("ou=people", "uid={0}", contextSource);
    ldapUserSearch.setReturningAttributes(attrs);
    authenticator.setUserSearch(ldapUserSearch);
    authenticator.setUserDnPatterns(new String[] { "uid={0},ou=people" });
    return authenticator;
}

@Bean
LdapAuthenticationProvider authenticationProvider(LdapAuthenticator authenticator) {
    LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator);
    provider.setUserDetailsContextMapper(new PersonContextMapper());
    return provider;
}

For that reason I'm going to close this issue, but feel free to comment below if this doesn't solve your problem.