Spring Security LDAP authentication user must be a member of an AD group
Asked Answered
M

3

6

I've configured the Spring Boot Security as per: https://spring.io/guides/gs/securing-web/

I am able to login using my credentials perfectly. However, I need to add a checking that the AD user must also belong to a specific AD group (ie. AD-this-is-a-specific-group). On login, if the user does not belong to the specific AD group, then it should return a login error.

I've been searching for hours now and cannot seem to find a clear way to do this in the WebSecurityConfigurerAdapter , am I using the auth.groupSearchFilter correctly?

Here is my code:

@Configuration 
@EnableWebSecurity    
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
Environment env;

public LdapContextSource contextSource () {
    LdapContextSource contextSource= new LdapContextSource();

    contextSource.setUrl(env.getRequiredProperty("ldap.url"));
    contextSource.setBase(env.getRequiredProperty("ldap.baseDn"));
    contextSource.setUserDn(env.getRequiredProperty("ldap.bindDn"));
    contextSource.setPassword(env.getRequiredProperty("ldap.batchPassword"));
    contextSource.afterPropertiesSet();
    return contextSource;
}

@Override
protected void configure(AuthenticationManagerBuilder auth)
        throws Exception {
     auth.ldapAuthentication()
        .userSearchFilter("(cn={0})")           
        .groupSearchBase("OU=Account Groups,OU=ITS Security")
        .groupSearchFilter("(cn=AD-this-is-a-specific-group)") 
        .contextSource(contextSource()); 
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().anyRequest().fullyAuthenticated()
        .and()
        .formLogin();
}
Musket answered 12/7, 2016 at 2:57 Comment(0)
M
6

Not sure if this is the best way to do this (in terms of Spring Security's lifecycle), but basically I provided my own DefaultLdapAuthoritiesPopulator, where I only override the getGroupMembershipRoles.

First thing though, I have wrong auth.groupSearchFilter above, it should be:

    .groupSearchFilter("(member={0})") 

Second, I've created an anonymous class with overridden method (that calls the super and checks for a the membership in the list of roles):

auth
        .ldapAuthentication()
        .ldapAuthoritiesPopulator(new DefaultLdapAuthoritiesPopulator(contextSource, "OU=Account Groups,OU=ITS Security") {

            @Override
            public Set<GrantedAuthority> getGroupMembershipRoles(String userDn, String username) {
                Set<GrantedAuthority> groupMembershipRoles = super.getGroupMembershipRoles(userDn, username);

                boolean isMemberOfSpecificAdGroup = false;
                for (GrantedAuthority grantedAuthority : groupMembershipRoles) {

                    if ("ROLE_AD-this-is-a-specific-group".equals(grantedAuthority.toString())) {                                                       
                        isMemberOfSpecificAdGroup = true;
                        break;
                    }
                }

                if (!isMemberOfSpecificAdGroup ) {

                    throw new BadCredentialsException("User must be a member of " + "AD-this-is-a-specific-group");
                }
                return groupMembershipRoles;
            }
        })
        .userSearchFilter("(cn={0})")           
        .groupSearchBase("OU=Account Groups,OU=ITS Security")
        .groupSearchFilter("(member={0})") 
        .contextSource(contextSource); 
Musket answered 12/7, 2016 at 3:37 Comment(0)
I
7

I am sorry for beeing 5 years late for the party but I had the exact same problem with my very simple LDAP authentication implemented in Spring Boot.

I only wanted this: - Is it the correct username? - Is it the correct password? - If yes, is the usr in group MYGROUP?

So my configure method now looks really small. I added the populator in a separate bean, just realize that I needed to add it in "auth.ldapAuthentication" so it would be called.

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {

auth.ldapAuthentication()
        .userSearchFilter("uid={0}")
        .ldapAuthoritiesPopulator(ldapAuthoritiesPopulator())
        .groupSearchFilter("(member={0})") 
        .contextSource(contextSource());
}

@Bean
public LdapAuthoritiesPopulator ldapAuthoritiesPopulator() {

DefaultLdapAuthoritiesPopulator populi = new DefaultLdapAuthoritiesPopulator(contextSource(), "") {

    @Override
    public Set<GrantedAuthority> getGroupMembershipRoles(String userDn, String username) {
        Set<GrantedAuthority> groupMembershipRoles = super.getGroupMembershipRoles(userDn, username);

        boolean isMemberOfSpecificAdGroup = false;
        for (GrantedAuthority grantedAuthority : groupMembershipRoles) {

            if ("ROLE_MYGROUP".equals(grantedAuthority.toString())) {
                isMemberOfSpecificAdGroup = true;
                break;
            }
        }

        if (!isMemberOfSpecificAdGroup) {

            throw new BadCredentialsException("User must be a member of " + "ROLE_MYGROUP");
        }
        return groupMembershipRoles;
    }
};

    return populi;
}

@Bean
public DefaultSpringSecurityContextSource contextSource() {
    return new DefaultSpringSecurityContextSource("ldap://blabla-some-url:389/dc=something,dc=something,dc=ch");
    }

And by the way: The url did not work like mentioned in the Spring Boot guide it only worked like this, like everything in one line:

return new DefaultSpringSecurityContextSource("ldap://blabla-some-url:389/dc=something,dc=something,dc=ch");

And by the way for everyone following that guide: If you connect to an already existing LDAP server you don't need all those "spring.ldap.embedded" application properties.

So thank you alot for your help!

Irs answered 21/7, 2017 at 13:5 Comment(2)
Thanks a lot for this comment. My call to getGroupMembershipRoles always return a List of size(0). What are the requirement in order to get authorities here?Idioplasm
As fas as I see in my project the only requirement is that your LDAP server needs to have the exact same information as you provide in the two parameters userDn and username. So userDn needs to provide the full Dn for a user and this Dn and the username must exist within your LDAP server. Actually I realize that you may need to add the parameter "groupSearchBase" -> that is just "" in the example above. You might add the groupSearchBase if you desire so in the line DefaultLdapAuthoritiesPopulator populi = new DefaultLdapAuthoritiesPopulator(contextSource(), groupSearchBase) {Irs
M
6

Not sure if this is the best way to do this (in terms of Spring Security's lifecycle), but basically I provided my own DefaultLdapAuthoritiesPopulator, where I only override the getGroupMembershipRoles.

First thing though, I have wrong auth.groupSearchFilter above, it should be:

    .groupSearchFilter("(member={0})") 

Second, I've created an anonymous class with overridden method (that calls the super and checks for a the membership in the list of roles):

auth
        .ldapAuthentication()
        .ldapAuthoritiesPopulator(new DefaultLdapAuthoritiesPopulator(contextSource, "OU=Account Groups,OU=ITS Security") {

            @Override
            public Set<GrantedAuthority> getGroupMembershipRoles(String userDn, String username) {
                Set<GrantedAuthority> groupMembershipRoles = super.getGroupMembershipRoles(userDn, username);

                boolean isMemberOfSpecificAdGroup = false;
                for (GrantedAuthority grantedAuthority : groupMembershipRoles) {

                    if ("ROLE_AD-this-is-a-specific-group".equals(grantedAuthority.toString())) {                                                       
                        isMemberOfSpecificAdGroup = true;
                        break;
                    }
                }

                if (!isMemberOfSpecificAdGroup ) {

                    throw new BadCredentialsException("User must be a member of " + "AD-this-is-a-specific-group");
                }
                return groupMembershipRoles;
            }
        })
        .userSearchFilter("(cn={0})")           
        .groupSearchBase("OU=Account Groups,OU=ITS Security")
        .groupSearchFilter("(member={0})") 
        .contextSource(contextSource); 
Musket answered 12/7, 2016 at 3:37 Comment(0)
N
1

I'll put this here since I think it's the easier way without overriding any method.

In user search filter (i'll use yours) add the following if it corresponds to your LDAP structure

Original:

.userSearchFilter("(cn={0})") 

Modified to search roles:

.userSearchFilter("(&(cn={0})(memberOf=CN=MYGROUP,OU=GROUP,DC=com,DC=company)")

This searches both the user and the membership

In my case I had to do this because I have 3 possible roles:

(&(cn={0})(|(group1)(group2)(group3)))

As you can see it searches user AND 1 OR more roles

Credit to this question's answer: Spring Security Ldap, log in only users in specified group

Nasalize answered 28/8, 2019 at 20:36 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.