[Geoserver-devel] [JIRA] (GEOS-9488) LDAP hierarchical group search generates error due to ConcurrentModificationException

Giovanni Spigoni created an issue

GeoServer / BugGEOS-9488

LDAP hierarchical group search generates error due to ConcurrentModificationException

Issue Type:

BugBug

Affects Versions:

2.16.1, 2.16.2

Assignee:

Unassigned

Components:

Security

Created:

06/Feb/20 5:38 PM

Priority:

MediumMedium

Reporter:

Giovanni Spigoni

The LDAP hierarchical groups search in geoserver 2.16.x with Tomcat 7 and openjdk 1.8 generates an error preventing access to the geoserver’s resources.
The error occurs both in LDAP Role Service and in LDAP User/Group Service only when the hierarchical group search is enabled. The error given by the geoserver is a HTTP 500 Internal Server Error and the following stack trace is displayed:

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)

By analysing the code of the LDAPRoleService class and the related error it comes up it is a bug in the code.
Considering the following code of the LDAPRoleService class, I see that the object roles of type TreeSet is modified by the searchNestedParetRoles method while the getRolesForUsers method iterates on it.

    @Override
    public SortedSet<GeoServerRole> getRolesForUser(final String username) throws IOException {
        final SortedSet<GeoServerRole> roles = new TreeSet<GeoServerRole>();
        //...
        //...
        if (useNestedGroups) {
            for (GeoServerRole erole : roles) {    // <--------this line (181) generates the error
                searchNestedParentRoles(erole, roles, 1);
            }
        }
        return Collections.unmodifiableSortedSet(roles);
    }

    private void searchNestedParentRoles(GeoServerRole role, Set<GeoServerRole> roles, int depth) {
        if (isOutOfDepthBounds(depth)) return;
        for (GeoServerRole erole : getParentRolesbyMember(role)) {
            if (!roles.contains(erole)) {
                roles.add(erole);
                searchNestedParentRoles(erole, roles, depth + 1);
            }
        }
    }

The Oracle’s documentation about the TreeSet class states that “The iterators returned by this class’s iterator method are fail-fast: if the set is modified at any time after the iterator is created, in any way except through the iterator’s own remove method, the iterator will throw a ConcurrentModificationException”. I think our case is the one described by the documentation and indeed we get the ConcurrentModificationException.

We could avoid the error by adding two line of code in the getRolesForUser method:

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

        }
        return Collections.unmodifiableSortedSet(roles);
    }

To reproduce the error please configure the LDAP Role Service in order to enable Hierarchical Group Search like following:

      <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>

and populate a LDAP directory with some content like following:

dn: cn=dummyusr,ou=Users,ou=SYSTEM,ou=My Company,dc=comune,dc=it
objectClass: person
userPrincipalName: dummyusr@anonymised.com

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

Trying to access the geoserver with dummyusr should reproduce the error.

Add Comment

Add Comment

Get Jira notifications on your phone! Download the Jira Cloud app for Android or iOS


This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100118-sha1:ad4f3b4)

Atlassian logo