How to retrieve authorities/roles when authenticating with spring security and LDAP?

177 Views Asked by At

First of all I want to say I don't understand LDAP fully, so if I need to provide more information about the LDAP, please say so.

I have a spring boot application (Java 17, spring boot 3.2.0) which has LDAP authentication configured:

@Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .anyRequest().fullyAuthenticated()
                .and()
                .httpBasic(Customizer.withDefaults());
        return http.build();
    }

    @Bean
    AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
        LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
        factory.setUserSearchFilter("(userPrincipalName={0})");
        factory.setUserDnPatterns("userPrincipalName={0}");

        return factory.createAuthenticationManager();
    }

So far so good. Authentication is working and is very quick. But now I want to retrieve what groups/authorities the user is member of. If viewing the LDAP from an LDAP-client (the one I am using is called LDAP Admin and I am using the same user to authenticate there as I am in the spring boot app) I can see the following: enter image description here

And if I run the the query (member=cn=Eriksson, Viktor - e-bolvier,ou=External Users,ou=....,dc=internal) I get this: enter image description here

So I add these lines:

@Bean
    AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource, LdapAuthoritiesPopulator authorities) {
        .....
        factory.setLdapAuthoritiesPopulator(authorities);
        .....
    }

    @Bean
    LdapAuthoritiesPopulator authorities(BaseLdapPathContextSource contextSource) {
        String groupSearchBase = "";
        DefaultLdapAuthoritiesPopulator authorities =
                new DefaultLdapAuthoritiesPopulator(contextSource, groupSearchBase);
        authorities.setGroupSearchFilter("(member=cn=Eriksson\\, Viktor - e-bolvier,ou=External Users,ou=.....)");

        return authorities;
    }

I know that the filter should be like this (member={0}) but for some reason it encodes my distinguishedName to this: member=cn=Eriksson\5c, Viktor..., so just to be sure it uses the correct value I typed it manually, I dont know how to skip the encoding.

After adding those lines it starts crashing and saying:

org.springframework.ldap.PartialResultException: Unprocessed Continuation Reference(s)
    at 
org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:218) ~[spring-ldap-core-3.2.0.jar:3.2.0]
    at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:375) ~[spring-ldap-core-3.2.0.jar:3.2.0]
....

After some googling I found a couple of answers that suggested to set referral to 'follow'. So I added these lines to the Application-class:

@Autowired
private ApplicationContext applicationContext;

@PostConstruct
    private void setReferralForContext() {
        LdapTemplate ldapTemplate = applicationContext.getBean(LdapTemplate.class);// necessary for LdapContextSource to be created
        LdapContextSource ldapContextSource = applicationContext.getBean(LdapContextSource.class);
        ldapContextSource.setReferral("follow");
        ldapContextSource.afterPropertiesSet();
    }

Now the authentication takes a looooong time, at least 60 seconds. The log is saying something like this:

2023-11-25T23:10:11.600+01:00 DEBUG 27484 : Failed to bind with any user DNs [[email protected]]
2023-11-25T23:10:11.600+01:00 TRACE 27484 : Searching for user using FilterBasedLdapUserSearch [searchFilter=(userPrincipalName={0}); searchBase=; scope=subtree; searchTimeLimit=0; derefLinkFlag=false ]
2023-11-25T23:10:11.600+01:00 TRACE 27484 : Searching for user '[email protected]', with FilterBasedLdapUserSearch [searchFilter=(userPrincipalName={0}); searchBase=; scope=subtree; searchTimeLimit=0; derefLinkFlag=false ]
2023-11-25T23:10:11.640+01:00 TRACE 27484 : Searching for entry under DN 'dc=xxx,dc=internal', base = '', filter = '(userPrincipalName={0})'
2023-11-25T23:10:11.647+01:00 DEBUG 27484 : Found DN: CN=Eriksson\, Viktor - e-bolvier,OU=External Users,OU=xxx,OU=Sweden,OU=Newxxx
2023-11-25T23:10:33.146+01:00 DEBUG 27484 : Found user '[email protected]', with FilterBasedLdapUserSearch [searchFilter=(userPrincipalName={0}); searchBase=; scope=subtree; searchTimeLimit=0; derefLinkFlag=false ]
2023-11-25T23:10:33.146+01:00 TRACE 27484 : Attempting to bind as cn=Eriksson\, Viktor - e-bolvier,ou=External Users,ou=xxx,ou=Sweden,ou=Newxxx,dc=xxx,dc=internal
2023-11-25T23:10:33.183+01:00 DEBUG 27484 : Bound cn=Eriksson\, Viktor - e-bolvier,ou=External Users,ou=xxx,ou=Sweden,ou=Newxxx,dc=xxx,dc=internal
2023-11-25T23:10:33.184+01:00 TRACE 27484 : Searching for roles for user [email protected] with DN cn=Eriksson\, Viktor - e-bolvier,ou=External Users,ou=xxx,ou=Sweden,ou=Newxxx,dc=xxx,dc=internal and filter (member=cn=Eriksson\, Viktor - e-bolvier,ou=External Users,ou=xxx,ou=Sweden,ou=Newxxx,dc=xxx,dc=internal) in search base 
2023-11-25T23:10:33.185+01:00 TRACE 27484 : Using filter: (member=cn=Eriksson\, Viktor - e-bolvier,ou=External Users,ou=xxx,ou=Sweden,ou=Newxxx,dc=xxx,dc=internal)
2023-11-25T23:10:54.453+01:00 DEBUG 27484 : Found roles from search []
2023-11-25T23:10:54.454+01:00 DEBUG 27484 : Retrieved authorities for user cn=Eriksson\, Viktor - e-bolvier,ou=External Users,ou=xxx,ou=Sweden,ou=Newxxx,dc=xxx,dc=internal
2023-11-25T23:10:54.454+01:00 DEBUG 27484 : Mapping user details from context with DN cn=Eriksson\, Viktor - e-bolvier,ou=External Users,ou=xxx,ou=Sweden,ou=Newxxx,dc=xxx,dc=internal
2023-11-25T23:10:54.455+01:00 DEBUG 27484 : Authenticated user

But despite taking a long time it doesnt seem to find any roles.

So my questions.

Why is the LDAP-client so fast and seems to get me the result I want but the code is suuuper slow and doesnt return anything?

Why is my dn encoded with weird characters in it?

1

There are 1 best solutions below

0
Viktor Eriksson On

Not sure what this parameter does but everything started working when I changed it.

authorities.setSearchSubtree(true);

I also added setIgnorePartialResultException(true);

And removed referral=follow.

So now it is fast and maps roles correctly.

My code:

    @Bean
    LdapAuthoritiesPopulator authorities(BaseLdapPathContextSource contextSource) {
        String groupSearchBase = "";
        DefaultLdapAuthoritiesPopulator authorities =
                new DefaultLdapAuthoritiesPopulator(contextSource, groupSearchBase);
        authorities.setGroupSearchFilter("(member={0})");
        authorities.setSearchSubtree(true);
        authorities.setIgnorePartialResultException(true);

        return authorities;
    }