ldap-sync: resolver and plan (reconciliation core)
Pure reconciliation logic with no I/O — the highest-stakes correctness piece in the sync pipeline.
-
resolver.py: implements the T419622 access policy. Membership in the
analytics-privatedata-usersPOSIX group is a hard precondition — users not in this group get NO_ACCESS regardless of any Bitu group membership. This check is NOT a participant in max() across matching Bitu rules; including it in max() would let ADMIN (rank 3) silently override NO_ACCESS (rank 0).compute_target_state() returns a TargetState dataclass bundling the role with audit provenance (rule number, source groups) so the diff stage can log decisions without re-deriving them.
-
plan.py: Action dataclass with audit provenance; ActionKind enum (DELETE, DOWNGRADE, UPGRADE, GRANT, NOOP, SKIPPED, PENDING_SSO); Plan with diff(), evaluate_thresholds(), and sorted_actions().
diff() takes per-user dataclasses (CurrentMember, TargetState). Apply order is fail-closed: deletions → downgrades → upgrades → grants. Within each kind, sort by current_role descending then email so the most over-privileged users are neutered first if a run is interrupted.
Threshold defaults: max(5, 10%) for both deletes and downgrades. PENDING_SSO and SKIPPED actions are excluded from threshold population.
59 tests covering the resolver truth table, diff classification, threshold evaluation, sort order, and edge cases.
Bug: T420691