4

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: dummyusr@comune.it

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 admin@comune.it
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 admin@comune.it
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 admin@comune.it
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 admin@comune.it
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 admin@comune.it
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 admin@comune.it
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 admin@comune.it
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 admin@comune.it
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
0

The exception is caused by an error in the code. A TreeSet java object is modified while a for loop iterates on it and this generates the ConcurrentModificationException as described by the javadoc.

For details please see the issue I created on JIRA-GeoServer ticket tracker. https://osgeo-org.atlassian.net/browse/GEOS-9488

I solved, at least in test environment, by modifying the code and by recompiling the classes affected by the error. Thus I generated a new gs-sec-ldap-2.16-SNAPSHOT.jar file and replaced the gs-sec-ldap-1.16.1.jar file in the lib folder of the geoserver.

More in detail I modified the LDAPRoleService.getRolesForUser and the LDAPUserGroupService.getGroupsForUser methods, as also described in the JIRA ticket, as follows:

   public SortedSet<GeoServerRole> getRolesForUser(final String username) throws IOException 
        final SortedSet<GeoServerRole> roles = new TreeSet<GeoServerRole>();
        // new set containing only nested parent roles
        final SortedSet<GeoServerRole> parentRoles = new TreeSet<GeoServerRole>();
        //...
        if (useNestedGroups) {
            for (GeoServerRole erole : roles) { 
                searchNestedParentRoles(erole, parentRoles, 1);   
            }
            roles.addAll(parentRoles);

        }
        return Collections.unmodifiableSortedSet(roles);
    }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.