25bc5bcd3f7e66f61a597b0954f406ca635d4566
Reported by a user who is a direct member of the configured group and was being refused with "Invalid username or password". Get-ADUser confirmed the membership, so the fault was here. The connection is built with raise_exceptions=False. A search that FAILS therefore returns False and leaves conn.entries empty - which is indistinguishable from "no match" if you only inspect conn.entries, which is all member_of did. Every possible failure of the extensible-match filter presented to the user as "you are not in the group" while they plainly were, and produced no log line saying otherwise. Now: - conn.search()'s return value is checked. A failed search raises LookupError(GROUP_CHECK_FAILED) and logs conn.result together with the filter that produced it. GROUP_CHECK_FAILED counts as a config problem, so login() answers 503 rather than 401 - our fault, not the user's, and reported as such. - If the transitive query matches nothing, a plain memberOf equality check runs for DIRECT membership. If THAT matches, the person is a member and is let in: refusing a real member is the worse error. It logs a WARNING naming the matching rule, because that outcome means nested membership is silently not working on this connection and needs a human. The failure mode this replaces is the one that hurts most: correct configuration, correct credential, real membership, and a refusal that blames the password. Same shape as the two logging faults fixed just before it - the information existed and could not be read. ldap_auth_check still 32/32; the fake directory exercises both branches. A step-by-step diagnostic (bind, account lookup, memberOf dump, the nested query, the direct query, group resolution) is in the session scratchpad rather than the repo - it is a one-off aid, not a deliverable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Description
No description provided
Languages
Python
49.3%
JavaScript
32.6%
CSS
8.9%
HTML
8.7%
Shell
0.4%