Skip to content

feat(#10): role-based access control with named API tokens - #58

Merged
shpak-e merged 1 commit into
masterfrom
feat/10-rbac
Jul 25, 2026
Merged

feat(#10): role-based access control with named API tokens#58
shpak-e merged 1 commit into
masterfrom
feat/10-rbac

Conversation

@shpak-e

@shpak-e shpak-e commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Roadmap #10 — RBAC

Adds viewer / operator / admin roles. Every /api/v1 request now resolves to an Identity(actor, role): reads need any identity, writes need ≥ operator, and token administration needs admin. Fully backwards-compatible — nothing that worked before breaks.

Auth (api/auth.py)

resolve_identity checks credentials in order:

  1. Legacy env tokens (win, constant-time): ACTIONSPLANE_API_TOKENadmin, ACTIONSPLANE_API_READ_TOKENviewer.
  2. Named DB tokens: the presented bearer is SHA-256'd and looked up; the row's role applies.
  3. Open mode (nothing configured anywhere) → viewer, so local-dev reads work and every write still fails closed.

Dependencies: require_identity / require_token, a require_role factory, require_operator (kept as the alias require_configured_operate so the ~20 existing write endpoints need no change), and require_admin. The env-only helpers (classify_actor/token_ok) are retained. The write-audit actor now names the credential — operate | read | token:<name>.

Tokens

  • ApiToken model + migration 0015 — only the hash and a non-secret prefix are stored, never the secret.
  • Admin-gated endpoints: GET/POST/DELETE /access/tokens (create returns the plaintext exactly once, audit-logged as token.create/token.revoke), plus GET /access/me.

Frontend

  • Settings "API access" tab = real token management (admin-gated create/list/revoke, role picker, secret shown once with copy-to-clipboard). "Single sign-on" stays an honest OIDC-later scaffold that maps onto the same roles.
  • useIdentity hook (/access/me), types, and API methods.

Tests

Rewrote test_api_auth.py (12 tests): env→DB→open resolution, DB-token role mapping, role gating (viewer blocked from writes, operator blocked from token admin), and a hermetic end-to-end token lifecycle (create → use → /access/me → revoke → 401) using a sqlite get_session override — no external DB needed. Migration round-tripped on Postgres. Suite 364 → 365, ruff + format + tsc + build clean.

🤖 Generated with Claude Code

Adds viewer/operator/admin roles. Every /api/v1 request resolves to an
Identity(actor, role); reads need any identity, writes need >= operator, and
token administration needs admin. Fully backwards-compatible.

Auth (api/auth.py):
- resolve_identity: env tokens win (ACTIONSPLANE_API_TOKEN -> admin,
  ACTIONSPLANE_API_READ_TOKEN -> viewer, constant-time), then DB tokens
  (SHA-256 hash lookup -> row role), then open mode (nothing configured) ->
  viewer so reads work and writes still fail closed.
- require_identity/require_token, require_role factory, require_operator
  (= require_configured_operate, so the ~20 existing write endpoints are
  unchanged), require_admin. Env-only helpers (classify_actor/token_ok) kept.
- The audit-log actor now names the credential (operate | read | token:<name>).

Tokens:
- ApiToken model + migration 0015 (only the hash + a non-secret prefix stored,
  never the secret). repository create/list/get-by-hash/count/delete.
- Admin-gated endpoints: GET/POST/DELETE /access/tokens (create returns the
  secret exactly once, audit-logged), GET /access/me.

Frontend:
- Settings "API access" tab = real token management (admin-gated create/list/
  revoke, role select, secret shown once with copy); "Single sign-on" stays an
  honest OIDC-later scaffold. useIdentity hook + types + api methods.

Tests: rewrote test_api_auth.py (12) for env/DB/open resolution, role gating,
and hermetic end-to-end token-admin flow (sqlite get_session override).
Migration round-trips on Postgres. Suite 364 -> 365.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shpak-e
shpak-e merged commit 48609bf into master Jul 25, 2026
4 checks passed
@shpak-e
shpak-e deleted the feat/10-rbac branch July 25, 2026 09:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant