I am trying to use LDAP for spring authentication and authorization. Even though I am successfully authenticating users, I couldn't get roles from LDAP yet.
dn: ou=Roles,dc=example,dc=org
objectClass: top
objectClass: organizationalUnit
ou: Roles
dn: ou=Users,dc=example,dc=org
objectClass: organizationalUnit
ou: Users
dn: cn=user2,ou=Users,dc=example,dc=org
objectClass: person
objectClass: top
cn: user1
sn: user1
userPassword:: bGRhcFBhc3N3b3Jk
dn: cn=user2,ou=Users,dc=example,dc=org
objectClass: person
objectClass: top
cn: user2
sn: user2
userPassword:: bGRhcFBhc3N3b3Jk
dn: cn=ROLE_ACL_ADMIN,ou=Roles,dc=example,dc=org
objectClass: groupOfUniqueNames
cn: ROLE_ACL_ADMIN
uniqueMember: cn=user1,ou=Users,dc=example,dc=org
uniqueMember: cn=user2,ou=Users,dc=example,dc=org
Here is my spring configuration. I think I am putting the wrong value to groupSearchBase but I am not sure what else can I put there (also tried dc=example,dc=org).
I can search roles with this ldap query on terminal ldapsearch -xLLL -b "dc=example,dc=org" "(&(cn=*)(objectClass=groupOfUniqueNames)(uniqueMember=cn=user1,ou=Users,dc=example,dc=org))" -D "cn=admin,dc=example,dc=org" -w admin
auth.ldapAuthentication()
.userDetailsContextMapper(userDetailsContextMapper())
.userDnPatterns("cn={0},ou=Users,dc=example,dc=org")
.userSearchFilter("(cn={0})")
.groupSearchBase("ou=Roles,dc=example,dc=org")
.groupSearchFilter("(&(cn=*)(objectClass=groupOfUniqueNames)(uniqueMember=cn={0}))")
.contextSource()
.url("ldap://127.0.0.1:389/dc=example,dc=org")
.managerDn("cn=admin,dc=example,dc=org")
.managerPassword("admin");
Error Log
org.springframework.ldap.NameNotFoundException: [LDAP: error code 32 - No Such Object]; nested exception is javax.naming.NameNotFoundException: [LDAP: error code 32 - No Such Object]; remaining name 'cn=*,ou=Roles,dc=example,dc=org'
at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:183) ~[spring-ldap-core-2.2.0.RELEASE.jar:2.2.0.RELEASE]
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:376) ~[spring-ldap-core-2.2.0.RELEASE.jar:2.2.0.RELEASE]
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:328) ~[spring-ldap-core-2.2.0.RELEASE.jar:2.2.0.RELEASE]
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:629) ~[spring-ldap-core-2.2.0.RELEASE.jar:2.2.0.RELEASE]
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:570) ~[spring-ldap-core-2.2.0.RELEASE.jar:2.2.0.RELEASE]
at org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForMultipleAttributeValues(SpringSecurityLdapTemplate.java:237) ~[spring-security-ldap-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleAttributeValues(SpringSecurityLdapTemplate.java:164) ~[spring-security-ldap-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator.getGroupMembershipRoles(DefaultLdapAuthoritiesPopulator.java:241) ~[spring-security-ldap-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator.getGrantedAuthorities(DefaultLdapAuthoritiesPopulator.java:210) ~[spring-security-ldap-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.loadUserAuthorities(LdapAuthenticationProvider.java:213) ~[spring-security-ldap-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider.authenticate(AbstractLdapAuthenticationProvider.java:89) ~[spring-security-ldap-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:175) ~[spring-security-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:195) ~[spring-security-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:95) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at com.nerodata.commons.web.security.filter.CredentialsExpiredRedirectFilter.doFilterInternal(CredentialsExpiredRedirectFilter.java:79) ~[commons-web-2.8.0-SNAPSHOT.jar:na]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at com.nerodata.commons.web.security.filter.CsrfHeaderFilter.doFilterInternal(CsrfHeaderFilter.java:67) ~[commons-web-2.8.0-SNAPSHOT.jar:na]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:141) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:171) ~[spring-session-1.3.5.RELEASE.jar:na]
at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:80) ~[spring-session-1.3.5.RELEASE.jar:na]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108) ~[spring-boot-actuator-2.2.0.RELEASE.jar:2.2.0.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1579) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.27.jar:9.0.27]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_301]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_301]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.27.jar:9.0.27]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_301]
Caused by: javax.naming.NameNotFoundException: [LDAP: error code 32 - No Such Object]
at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3284) ~[na:1.8.0_301]
at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3205) ~[na:1.8.0_301]
at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2996) ~[na:1.8.0_301]
at com.sun.jndi.ldap.LdapCtx.searchAux(LdapCtx.java:1875) ~[na:1.8.0_301]
at com.sun.jndi.ldap.LdapCtx.c_search(LdapCtx.java:1798) ~[na:1.8.0_301]
at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_search(ComponentDirContext.java:392) ~[na:1.8.0_301]
at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.search(PartialCompositeDirContext.java:358) ~[na:1.8.0_301]
at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.search(PartialCompositeDirContext.java:341) ~[na:1.8.0_301]
at javax.naming.directory.InitialDirContext.search(InitialDirContext.java:267) ~[na:1.8.0_301]
at org.springframework.ldap.core.LdapTemplate$4.executeSearch(LdapTemplate.java:322) ~[spring-ldap-core-2.2.0.RELEASE.jar:2.2.0.RELEASE]
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:363) ~[spring-ldap-core-2.2.0.RELEASE.jar:2.2.0.RELEASE]
... 68 common frames omitted
Is anyone knows what is the problem here? I would appreciate any pointers.
There is an issue with the second condition in the group search filter, it should be (uniqueMember={0})
(the cn=
is part of the substituted dn). Also, the (cn=*)
is not necessary because cn
is a mandatory attribute for all groupOfUniqueNames
entries.
Since the context source url contains a base DN (dc=example,dc=org
) used for searching and authenticating users, you don't need to set this part in the user/group search parameters (which are relative to that base).
userSearchFilter()
is not necessary when using userDnPatterns
(@see this post)
auth.ldapAuthentication()
.userDetailsContextMapper(userDetailsContextMapper())
.userDnPatterns("cn={0},ou=Users")
.groupSearchBase("ou=Roles")
.groupSearchFilter("(&(objectClass=groupOfUniqueNames)(uniqueMember={0}))")
.contextSource()
.url("ldap://127.0.0.1:389/dc=example,dc=org")
.managerDn("cn=admin,dc=example,dc=org")
.managerPassword("admin");