Tamás Marcinkovics created KNOX-3406:
----------------------------------------

             Summary: Fix intermittent LDAP proxy search truncation
                 Key: KNOX-3406
                 URL: https://issues.apache.org/jira/browse/KNOX-3406
             Project: Apache Knox
          Issue Type: Task
          Components: Server
    Affects Versions: 3.0.0
            Reporter: Tamás Marcinkovics
            Assignee: David Han


Problem: CI intermittently fails test_search_all_groups_by_objectclass — an LDAP
subtree search for (objectClass=groupOfNames) returns only ['level1']
instead of all 6 groups.

[https://github.com/apache/knox/actions/runs/30453311904/attempts/1]


test_knox_ldap_proxy_search.py::TestKnoxLdapProxySearch::test_search_all_groups_by_objectclass
ests-1 | =================================== FAILURES 
=================================== 
tests-1 | ________ 
TestKnoxLdapProxySearch.test_search_all_groups_by_objectclass _________ 
tests-1 | 
tests-1 | self = <test_knox_ldap_proxy_search.TestKnoxLdapProxySearch 
testMethod=test_search_all_groups_by_objectclass> 
tests-1 | 
tests-1 | def test_search_all_groups_by_objectclass(self) -> None: 
tests-1 | """All groupOfNames entries under ou=groups are returned.""" 
tests-1 | groups = self.rdn_values(GROUPS_BASE, "(objectClass=groupOfNames)") 
tests-1 | for expected in ("analyst", "scientist", "admin", "level1", "level2", 
"level3"): 
tests-1 | > self.assertIn(expected, groups) 
tests-1 | E AssertionError: 'analyst' not found in ['level1'] 
tests-1 | 
tests-1 | test_knox_ldap_proxy_search.py:97: AssertionError
 
I could reproduce this on an ubuntu vm.
 
Root cause: 
LdapProxyBackend.search() opens an EntryCursor
over the outer search, then inside the while (cursor.next()) loop calls
addGroupMemberships(entry, connection, ...) which, when
useMemberOf=false, calls getUserGroupsInternal(connection, ...)
, opening a second EntryCursor on the same LdapConnection. 

The Apache Directory LDAP client is not designed for two concurrent 
SearchRequests on one connection: the SearchResultDone PDU from the inner 
search is consumed
by the outer cursor, which interprets it as "no more entries" and
terminates early after only 1 result.

This is a nested/concurrent cursor problem. It is intermittent because the
inner search's response must race ahead of the outer cursor's next() call —
which is CPU/network-scheduling dependent and more likely in CI's
faster-burst containers.

Fix: 
Drain the outer cursor first, then enrich.

In search(), collect all raw entries into a list before calling
addGroupMemberships on any of them. This ensures the outer EntryCursor is
fully consumed and closed before any secondary LDAP operation is issued on
the connection.

Three methods need fixing:

1. searchUsers  — outer while (cursor.next()) loop calls addGroupMemberships on 
each iteration. Fix: collect all sourceEntry objects into a list inside the 
cursor block, then iterate that list after the cursor closes.
2. search — same pattern as searchUsers. Same fix.
3. getUser — outer cursor does cursor.next() then calls addGroupMemberships 
before the cursor is closed. Since it only reads one entry though, this is 
lower risk: the inner search fires after cursor.next() returns, while the outer 
cursor is still open. The fix: copy the entry out, close the cursor (let the 
try-with-resources do it naturally by moving addGroupMemberships after the try 
block), then call addGroupMemberships.

getUserGroupsEntries also open cursors, but those are called from 
addGroupMemberships — they are the inner cursors, not the outer ones. This does 
not need to be changed.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to