Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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();
}
like image 746
Carlos Jaime C. De Leon Avatar asked Jul 12 '16 02:07

Carlos Jaime C. De Leon


People also ask

How do I authenticate someone using LDAP?

In order to authenticate a user with an LDAP directory you first need to obtain their DN as well as their password. With a login form, people typically enter a simple identifier such as their username or email address. You don't expect them to memorise the DN of their directory entry.


3 Answers

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!

like image 129
Invest Avatar answered Nov 06 '22 05:11

Invest


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); 
like image 36
Carlos Jaime C. De Leon Avatar answered Nov 06 '22 04:11

Carlos Jaime C. De Leon


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

like image 25
DarioJ Avatar answered Nov 06 '22 04:11

DarioJ