In our environment we have installed a geoserver version 2.16.1 running on Apache Tomcat 7.0.94 with openjdk version 1.8.0_212 upon a Red Hat Enterprise Linux Server 7.7 64bit. The geoserver is protected by external authentication (Shibboleth IdP). The authorizations to access workspaces and layers are based on a Active Directory-LDAP role service. The hierarchical groups search is enabled in the role service.
By accessing a protected resource of the geoserver, after a successful authentication, we get a HTTP 500 Internal Server Error page showing the following stack trace:
java.util.ConcurrentModificationException
java.util.TreeMap$PrivateEntryIterator.nextEntry(TreeMap.java:1211)
java.util.TreeMap$KeyIterator.next(TreeMap.java:1265)
org.geoserver.security.ldap.LDAPRoleService.getRolesForUser(LDAPRoleService.java:181)
org.geoserver.security.impl.RoleCalculator.calculateRoles(RoleCalculator.java:109)
org.geoserver.security.impl.RoleCalculator.calculateRoles(RoleCalculator.java:81)
org.geoserver.security.filter.GeoServerPreAuthenticatedUserNameFilter.getRolesFromRoleService(GeoServerPreAuthenticatedUserNameFilter.java:188)
org.geoserver.security.filter.GeoServerPreAuthenticatedUserNameFilter.getRoles(GeoServerPreAuthenticatedUserNameFilter.java:150)
org.geoserver.security.filter.GeoServerPreAuthenticationFilter.doAuthenticate(GeoServerPreAuthenticationFilter.java:116)
org.geoserver.security.filter.GeoServerPreAuthenticationFilter.doFilter(GeoServerPreAuthenticationFilter.java:56)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
org.geoserver.security.filter.GeoServerCompositeFilter$NestedFilterChain.doFilter(GeoServerCompositeFilter.java:70)
org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:158)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.geoserver.security.filter.GeoServerCompositeFilter$NestedFilterChain.doFilter(GeoServerCompositeFilter.java:74)
org.geoserver.security.filter.GeoServerCompositeFilter.doFilter(GeoServerCompositeFilter.java:91)
org.geoserver.security.filter.GeoServerBasicAuthenticationFilter.doFilter(GeoServerBasicAuthenticationFilter.java:81)
The error occurs only if the Hierarchical groups search of the role service is enabled.
From the logs of the geoserver we can see that the role service correctly gets the groups the user is member of and, recursively, the parent-groups the user's groups are member of.
It seems that, while the role service recursively searches for the groups a specific group is member of, another thread iterates the group list the user is member of. But this is just a supposition.
Below are some configuration of the LDAP Role Service:
<name>ldap-AD-roles</name>
<className>org.geoserver.security.ldap.LDAPRoleService</className>
<serverURL>ldap://comune.it/dc=comune,dc=it</serverURL>
<groupSearchBase>OU=Gruppi,OU=My Company</groupSearchBase>
<allGroupsSearchFilter>objectClass=group</allGroupsSearchFilter>
<groupSearchFilter>member={1},dc=comune,dc=it</groupSearchFilter>
<userFilter>(userPrincipalName={0}@comune.it)</userFilter>
<useNestedParentGroups>true</useNestedParentGroups>
<maxGroupSearchLevel>5</maxGroupSearchLevel>
<nestedGroupSearchFilter>member={1}</nestedGroupSearchFilter>
Please note that we had to append the base DN dc=comune,dc=it
to the groupSearchFilter but not to the nestedGroupSearchFilter in order to make the role service find the parent groups.
What could be the reason of this error?
Update
We tried to use a LDAP User/Group Service (with hierarchical groups search enabled) as Role Source instead of the LDAP Role Service. The same error occurred: HTTP 500 Internal Server Error caused by a ConcurrentModificationException
with the following stack trace:
java.util.ConcurrentModificationException
java.util.TreeMap$PrivateEntryIterator.nextEntry(TreeMap.java:1211)
java.util.TreeMap$KeyIterator.next(TreeMap.java:1265)
org.geoserver.security.ldap.LDAPUserGroupService.getGroupsForUser(LDAPUserGroupService.java:411)
org.geoserver.security.impl.RoleCalculator.calculateRoles(RoleCalculator.java:114)
org.geoserver.security.ldap.LDAPUserGroupService.loadUserByUsername(LDAPUserGroupService.java:203)
org.geoserver.security.filter.GeoServerPreAuthenticatedUserNameFilter.getRolesFromUserGroupService(GeoServerPreAuthenticatedUserNameFilter.java:207)
org.geoserver.security.filter.GeoServerPreAuthenticatedUserNameFilter.getRoles(GeoServerPreAuthenticatedUserNameFilter.java:152)
org.geoserver.security.filter.GeoServerPreAuthenticationFilter.doAuthenticate(GeoServerPreAuthenticationFilter.java:116)
org.geoserver.security.filter.GeoServerPreAuthenticationFilter.doFilter(GeoServerPreAuthenticationFilter.java:56)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
org.geoserver.security.filter.GeoServerCompositeFilter$NestedFilterChain.doFilter(GeoServerCompositeFilter.java:70)
org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:158)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.geoserver.security.filter.GeoServerCompositeFilter$NestedFilterChain.doFilter(GeoServerCompositeFilter.java:74)
org.geoserver.security.filter.GeoServerCompositeFilter.doFilter(GeoServerCompositeFilter.java:91)
org.geoserver.security.filter.GeoServerBasicAuthenticationFilter.doFilter(GeoServerBasicAuthenticationFilter.java:81)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
org.geoserver.security.filter.GeoServerCompositeFilter$NestedFilterChain.doFilter(GeoServerCompositeFilter.java:70)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
org.geoserver.security.filter.GeoServerSecurityContextPersistenceFilter$1.doFilter(GeoServerSecurityContextPersistenceFilter.java:52)
org.geoserver.security.filter.GeoServerCompositeFilter$NestedFilterChain.doFilter(GeoServerCompositeFilter.java:74)
org.geoserver.security.filter.GeoServerCompositeFilter.doFilter(GeoServerCompositeFilter.java:91)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
org.geoserver.security.GeoServerSecurityFilterChainProxy.doFilter(GeoServerSecurityFilterChainProxy.java:142)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
org.geoserver.filters.LoggingFilter.doFilter(LoggingFilter.java:101)
org.geoserver.filters.XFrameOptionsFilter.doFilter(XFrameOptionsFilter.java:79)
org.geoserver.filters.GZIPFilter.doFilter(GZIPFilter.java:53)
org.geoserver.filters.SessionDebugFilter.doFilter(SessionDebugFilter.java:46)
org.geoserver.filters.FlushSafeFilter.doFilter(FlushSafeFilter.java:42)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
In order to give some additional information, the following is part of the content of the AD-LDAP directory (in LDIF format):
dn: cn=dummyusr,ou=Users,ou=SYSTEM,ou=My Company,dc=comune,dc=it
objectClass: person
userPrincipalName: [email protected]
dn: cn=ALF_APPLICATIVI,ou=Gruppi applicazioni,ou=Gruppi,ou=My Company,dc=comune,dc=it
objectClass: group
member: cn=dummyusr,ou=Users,ou=SYSTEM,ou=My Company,dc=comune,dc=it
dn: cn=GEO_READ,ou=Gruppi applicazioni,ou=Gruppi,ou=My Company,dc=comune,dc=it
objectClass: group
member: cn=ALF_APPLICATIVI,ou=Gruppi applicazioni,ou=Gruppi,ou=My Company,dc=comune,dc=it
and here below is the log of the geoserver just before the error occurs:
13:25:12,688 DEBUG [support.AbstractContextSource] - Using LDAP pooling.
13:25:12,689 DEBUG [support.AbstractContextSource] - Trying provider Urls: ldap://comune.it/dc=comune,dc=it
13:25:12,689 DEBUG [ldap.DefaultSpringSecurityContextSource] - Removing pooling flag for user [email protected]
13:25:12,695 DEBUG [support.AbstractContextSource] - Got Ldap context on server 'ldap://comune.it/dc=comune,dc=it'
13:25:12,697 DEBUG [ldap.SpringSecurityLdapTemplate] - Searching for entry under DN 'dc=comune,dc=it', base = 'ou=My Company', filter = 'cn={0}'
13:25:12,698 DEBUG [ldap.SpringSecurityLdapTemplate] - Found DN: CN=dummyusr,OU=Users,OU=SYSTEM,OU=My Company
13:25:12,698 DEBUG [geoserver.security] - AuthenticationCache has no entry for HTTP_Shibboleth, dummyusr
13:25:12,698 DEBUG [geoserver.security] - preAuthenticatedPrincipal = dummyusr, trying to authenticate
13:25:12,698 DEBUG [support.AbstractContextSource] - Using LDAP pooling.
13:25:12,698 DEBUG [support.AbstractContextSource] - Trying provider Urls: ldap://comune.it/dc=comune,dc=it
13:25:12,698 DEBUG [ldap.DefaultSpringSecurityContextSource] - Removing pooling flag for user [email protected]
13:25:12,703 DEBUG [support.AbstractContextSource] - Got Ldap context on server 'ldap://comune.it/dc=comune,dc=it'
13:25:12,705 DEBUG [ldap.SpringSecurityLdapTemplate] - Searching for entry under DN 'dc=comune,dc=it', base = 'ou=My Company', filter = 'cn={0}'
13:25:12,705 DEBUG [ldap.SpringSecurityLdapTemplate] - Found DN: CN=dummyusr,OU=Users,OU=SYSTEM,OU=My Company
13:25:12,705 DEBUG [support.AbstractContextSource] - Using LDAP pooling.
13:25:12,705 DEBUG [support.AbstractContextSource] - Trying provider Urls: ldap://comune.it/dc=comune,dc=it
13:25:12,705 DEBUG [ldap.DefaultSpringSecurityContextSource] - Removing pooling flag for user [email protected]
13:25:12,710 DEBUG [support.AbstractContextSource] - Got Ldap context on server 'ldap://comune.it/dc=comune,dc=it'
13:25:12,710 DEBUG [support.AbstractContextSource] - Using LDAP pooling.
13:25:12,710 DEBUG [support.AbstractContextSource] - Trying provider Urls: ldap://comune.it/dc=comune,dc=it
13:25:12,710 DEBUG [ldap.DefaultSpringSecurityContextSource] - Removing pooling flag for user [email protected]
13:25:12,714 DEBUG [support.AbstractContextSource] - Got Ldap context on server 'ldap://comune.it/dc=comune,dc=it'
13:25:12,716 DEBUG [ldap.SpringSecurityLdapTemplate] - Searching for entry under DN 'dc=comune,dc=it', base = '', filter = 'cn={0}'
13:25:12,716 DEBUG [ldap.SpringSecurityLdapTemplate] - Found DN: CN=dummyusr,OU=Users,OU=SYSTEM,OU=My Company
13:25:12,717 INFO [ldap.SpringSecurityLdapTemplate] - Ignoring PartialResultException
13:25:12,717 DEBUG [ldap.SpringSecurityLdapTemplate] - Using filter: member=CN=dummyusr,OU=Users,OU=SYSTEM,OU=My Company,dc=comune,dc=it
13:25:12,717 DEBUG [core.LdapTemplate] - The returnObjFlag of supplied SearchControls is not set but a ContextMapper is used - setting flag to true
13:25:12,720 DEBUG [support.AbstractContextSource] - Using LDAP pooling.
13:25:12,721 DEBUG [support.AbstractContextSource] - Trying provider Urls: ldap://comune.it/dc=comune,dc=it
13:25:12,721 DEBUG [ldap.DefaultSpringSecurityContextSource] - Removing pooling flag for user [email protected]
13:25:12,726 DEBUG [support.AbstractContextSource] - Got Ldap context on server 'ldap://comune.it/dc=comune,dc=it'
13:25:12,728 DEBUG [ldap.SpringSecurityLdapTemplate] - Searching for entry under DN 'dc=comune,dc=it', base = 'ou=Gruppi,ou=My Company', filter = 'cn={0}'
13:25:12,728 DEBUG [ldap.SpringSecurityLdapTemplate] - Found DN: CN=ALF_APPLICATIVI,OU=Gruppi applicazioni,OU=Gruppi,OU=My Company
13:25:12,730 DEBUG [support.AbstractContextSource] - Using LDAP pooling.
13:25:12,730 DEBUG [support.AbstractContextSource] - Trying provider Urls: ldap://comune.it/dc=comune,dc=it
13:25:12,730 DEBUG [ldap.DefaultSpringSecurityContextSource] - Removing pooling flag for user [email protected]
13:25:12,734 DEBUG [support.AbstractContextSource] - Got Ldap context on server 'ldap://comune.it/dc=comune,dc=it'
13:25:12,735 DEBUG [ldap.SpringSecurityLdapTemplate] - Using filter: member=CN=ALF_APPLICATIVI,OU=Gruppi applicazioni,OU=Gruppi,OU=My Company,dc=comune,dc=it
13:25:12,735 DEBUG [core.LdapTemplate] - The returnObjFlag of supplied SearchControls is not set but a ContextMapper is used - setting flag to true
13:25:12,737 DEBUG [support.AbstractContextSource] - Using LDAP pooling.
13:25:12,737 DEBUG [support.AbstractContextSource] - Trying provider Urls: ldap://comune.it/dc=comune,dc=it
13:25:12,737 DEBUG [ldap.DefaultSpringSecurityContextSource] - Removing pooling flag for user [email protected]
13:25:12,741 DEBUG [support.AbstractContextSource] - Got Ldap context on server 'ldap://comune.it/dc=comune,dc=it'
13:25:12,743 DEBUG [ldap.SpringSecurityLdapTemplate] - Searching for entry under DN 'dc=comune,dc=it', base = 'ou=Gruppi,ou=My Company', filter = 'cn={0}'
13:25:12,743 DEBUG [ldap.SpringSecurityLdapTemplate] - Found DN: CN=GEO_READ,OU=Gruppi applicazioni,OU=Gruppi,OU=My Company
13:25:12,743 DEBUG [support.AbstractContextSource] - Using LDAP pooling.
13:25:12,743 DEBUG [support.AbstractContextSource] - Trying provider Urls: ldap://comune.it/dc=comune,dc=it
13:25:12,743 DEBUG [ldap.DefaultSpringSecurityContextSource] - Removing pooling flag for user [email protected]
13:25:12,756 DEBUG [support.AbstractContextSource] - Got Ldap context on server 'ldap://comune.it/dc=comune,dc=it'
13:25:12,756 DEBUG [ldap.SpringSecurityLdapTemplate] - Using filter: member=CN=GEO_READ,OU=Gruppi applicazioni,OU=Gruppi,OU=My Company,dc=comune,dc=it
13:25:12,756 DEBUG [core.LdapTemplate] - The returnObjFlag of supplied SearchControls is not set but a ContextMapper is used - setting flag to true