How to get additional user attributes from LDAP in Spring Security?

14,199

Solution 1

Solved it. The problem was not in the application code, but in the LDAP configuration.

Because I was using the default BindAuthenticator, Spring Security tried to bind to LDAP with the user specified in the login form. Unfortunately, all users under ou=users only had a search permission in the LDAP configuration. Changing search to read (see point 8.2.3 http://www.openldap.org/doc/admin24/access-control.html) solved this issue for me.

Note that the login/bind itself was still successful (because the auth permission is a subset of search), but any retrieval of attributes failed, as this required the read permission.

Solution 2

In advance for @mpm example:

Hope it will be helpful for somebody. Here is my decorator for UserDetails

public class CustomUserDetails implements LdapUserDetails {
    private String iin;
    private String colvirId;
    private LdapUserDetails details;

    public CustomUserDetails(LdapUserDetails details) {
        this.details = details;
    }

    public String getIin() {
        return iin;
    }

    public void setIin(String iin) {
        this.iin = iin;
    }

    public String getColvirId() {
        return colvirId;
    }

    public void setColvirId(String colvirId) {
        this.colvirId = colvirId;
    }

    @Override
    public String getDn() {
        return details.getDn();
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return details.getAuthorities();
    }

    @Override
    public String getPassword() {
        return details.getPassword();
    }

    @Override
    public String getUsername() {
        return details.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return details.isAccountNonExpired();
    }

    @Override
    public boolean isAccountNonLocked() {
        return details.isAccountNonLocked();
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return details.isCredentialsNonExpired();
    }

    @Override
    public boolean isEnabled() {
        return details.isEnabled();
    }
}
Share:
14,199
Alex
Author by

Alex

Updated on June 04, 2022

Comments

  • Alex
    Alex almost 2 years

    I'm currently trying to develop a Spring Boot application whose purpose it will be to manage user entries in our LDAP directory.

    LDAP login already works; so does the lookup of groups a user is member of.

    Additionally, I'd like to populate the Spring Security Principal object with some more LDAP attributes of that user. I've read several posts on SO and also the official Spring documentation, but simply cannot get this to work.

    Here is my current code: Class AuthenticationConfiguration

    @Configuration
    class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
    
        private String ldapUrl = "ldap://127.0.0.1:10389/dc=corp,dc=org";
        private String bindUser = "cn=spring,ou=users,dc=corp,dc=org";
        private String bindPW = "<password>";
        private String groupSearchBase = "ou=groups";
        private String groupSearchFilter = "(member={0})";
        private String userDnPattern = "uid={0},ou=users";
        private Log log = LogFactory.getLog(this.getClass());
    
        @Override
        public void init(AuthenticationManagerBuilder auth) throws Exception {
    
            DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(ldapUrl);
    
            contextSource.setUserDn(bindUser);
            contextSource.setPassword(bindPW);
            contextSource.afterPropertiesSet();
            log.info(contextSource.getReadOnlyContext().getAttributes("uid=testuser,ou=users")); // returns all LDAP attributes from that user
    
            DefaultLdapAuthoritiesPopulator populator = new DefaultLdapAuthoritiesPopulator(contextSource, groupSearchBase);
            populator.setGroupSearchFilter(groupSearchFilter);
            populator.setSearchSubtree(true);
            populator.setIgnorePartialResultException(true);
    
            auth
            .ldapAuthentication()
            .ldapAuthoritiesPopulator(populator)
            .contextSource(contextSource)
            .userDetailsContextMapper(userDetailsContextMapper())
            .userDnPatterns(userDnPattern)
            ;
        }
    
        @Bean
        public UserDetailsContextMapper userDetailsContextMapper() {
            return new CustomUserDetailsContextMapper();
        }
    
    }
    

    As you can see, I'm using the userDetailsContextMapper() method to return an instance of my CustomUserDetailsContextMapper:

    @Configuration
    public class CustomUserDetailsContextMapper extends LdapUserDetailsMapper implements UserDetailsContextMapper {
    
        private Log log = LogFactory.getLog(this.getClass());
    
        @Override
        public LdapUserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
    
            LdapUserDetailsImpl details = (LdapUserDetailsImpl) super.mapUserFromContext(ctx, username, authorities);
            log.info("DN from ctx: " + ctx.getDn()); // return correct DN
            log.info("Attributes size: " + ctx.getAttributes().size()); // always returns 0
    
            return new CustomUserDetails(details);
        }
    
        @Override
        public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
            // default
        }
    }
    

    Now, when you look at that first log statement in AuthenticationConfiguration, Spring will actually print the whole user object to stdout, correctly showing all LDAP attributes from the user: {displayname=displayName: Test User, givenname=givenName: Test, objectclass=objectClass: posixAccount, top [...]}

    However, the log statements in my CustomUserDetailsContextMapper class do not. Although the first one correctly displays the DN of the logged in user, the second one just display 0, i.e. ctx does not seem to contain any attributes from the current user.

    I also tried directly querying for attributes via ctx.getAttribute("attribute"), ctx.getStringAttribute("attribute") or ctx.getObjectAttribute("attribute"), to no avail.

    How can I access LDAP attributes from within the mapUserFromContext method?

    I'm really out of ideas, so any help would be greatly appreciated :-)