auth.adminGroups: IdP-managed admin (closes the config-edit UX gap; refs #51 #52) #53

Merged
Cordy merged 3 commits from feat/admin-groups into main 2026-07-29 02:18:45 +00:00
Owner

Admin can now be granted by directory group membership instead of editing the config secret.

Config: auth.adminGroups: ["cairn-admins"] — any authenticated user carrying a listed group (OIDC groups claim or LDAP memberOf; identical pipeline since v0.3.1) is an operator. Grant/revoke = add/remove from the group in the IdP, effective at next sign-in. auth.admins (usernames) stays as bootstrap + break-glass.

Mechanism: auth.WithAdminFlag provider wrapper evaluates the flag once per authentication and sets User.Admin; all admin checks now honor it —

  • scope overlay (d.admins[u.Username] || u.Admin) → unscoped backend view
  • trash isAdmin → raw /.trash access, real deletes inside it
  • license seat gate (WrapProvider) → admins always pass
  • /api/v1/admin/* and /api/v1/license gates
  • admin surfaces are registered when either admins or adminGroups is non-empty
  • GET /api/v1/me now reports "admin" (future UI use)

No constructor signatures changed — existing components keep their username maps, so all existing tests pass untouched; new adminflag_test.go covers list/group/no-match/nil/error-passthrough. Design rationale in issues #51/#52: privileges live where people are managed; the trust decision (which group means admin) stays in Cairn's config.

gofmt + vet + build + full test suite ran green on the runner before push.

Admin can now be granted by directory group membership instead of editing the config secret. **Config**: `auth.adminGroups: ["cairn-admins"]` — any authenticated user carrying a listed group (OIDC groups claim or LDAP `memberOf`; identical pipeline since v0.3.1) is an operator. Grant/revoke = add/remove from the group in the IdP, effective at next sign-in. `auth.admins` (usernames) stays as bootstrap + break-glass. **Mechanism**: `auth.WithAdminFlag` provider wrapper evaluates the flag once per authentication and sets `User.Admin`; all admin checks now honor it — - scope overlay (`d.admins[u.Username] || u.Admin`) → unscoped backend view - trash `isAdmin` → raw `/.trash` access, real deletes inside it - license seat gate (`WrapProvider`) → admins always pass - `/api/v1/admin/*` and `/api/v1/license` gates - admin surfaces are registered when either `admins` or `adminGroups` is non-empty - `GET /api/v1/me` now reports `"admin"` (future UI use) No constructor signatures changed — existing components keep their username maps, so all existing tests pass untouched; new `adminflag_test.go` covers list/group/no-match/nil/error-passthrough. Design rationale in issues #51/#52: privileges live where people are managed; the trust decision (which group means admin) stays in Cairn's config. gofmt + vet + build + full test suite ran green on the runner before push.
Cordy merged commit 8c8b95d1b0 into main 2026-07-29 02:18:45 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/Cairn#53
No description provided.