Cody Schaefer 25bc5bcd3f member_of could not tell a failed search from a non-member
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>
2026-08-24 11:37:59 -05:00
Description
No description provided
8.9 MiB
Languages
Python 49.3%
JavaScript 32.6%
CSS 8.9%
HTML 8.7%
Shell 0.4%