Add policy-based authz and token delegation

- Replace requireAdmin (role-based) guards on all REST endpoints
  with RequirePolicy middleware backed by the existing policy engine;
  built-in admin wildcard rule (-1) preserves existing admin behaviour
  while operator rules can now grant targeted access to non-admin
  accounts (e.g. a system account allowed to list accounts)
- Wire policy engine into Server: loaded from DB at startup,
  reloaded after every policy-rule create/update/delete so changes
  take effect immediately without a server restart
- Add service_account_delegates table (migration 000008) so a human
  account can be delegated permission to issue tokens for a specific
  system account without holding the admin role
- Add token-download nonce mechanism: a short-lived (5 min),
  single-use random nonce is stored server-side after token issuance;
  the browser downloads the token as a file via
  GET /token/download/{nonce} (Content-Disposition: attachment)
  instead of copying from a flash message
- Add /service-accounts UI page for non-admin delegates
- Add TestPolicyEnforcement and TestPolicyDenyRule integration tests

Security:
- Policy engine uses deny-wins, default-deny semantics; admin wildcard
  is a compiled-in built-in and cannot be deleted via the API
- Token download nonces are 128-bit crypto/rand values, single-use,
  and expire after 5 minutes; a background goroutine evicts stale entries
- alg header validation and Ed25519 signing unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-15 14:40:16 -07:00
parent d6cc82755d
commit d4e8ef90ee
14 changed files with 1066 additions and 77 deletions

View File

@@ -2,7 +2,42 @@
Source of truth for current development state.
---
All phases complete. **v1.0.0 tagged.** All packages pass `go test ./...`; `golangci-lint run ./...` clean.
All phases complete. **v1.0.0 tagged.** All packages pass `go test ./...`; `golangci-lint run ./...` clean (pre-existing warnings only).
### 2026-03-15 — Service account token delegation and download
**Problem:** Only admins could issue tokens for service accounts, and the only way to retrieve the token was a flash message (copy-paste). There was no delegation mechanism for non-admin users.
**Solution:** Added token-issue delegation and a one-time secure file download flow.
**DB (`internal/db/`):**
- Migration `000008`: new `service_account_delegates` table — tracks which human accounts may issue tokens for a given system account
- `GrantTokenIssueAccess`, `RevokeTokenIssueAccess`, `ListTokenIssueDelegates`, `HasTokenIssueAccess`, `ListDelegatedServiceAccounts` functions
**Model (`internal/model/`):**
- New `ServiceAccountDelegate` type
- New audit event constants: `EventTokenDelegateGranted`, `EventTokenDelegateRevoked`
**UI (`internal/ui/`):**
- `handleIssueSystemToken`: now allows admins and delegates (not just admins); after issuance stores token in a short-lived (5 min) single-use download nonce; returns download link in the HTMX fragment
- `handleDownloadToken`: serves the token as `Content-Disposition: attachment` via the one-time nonce; nonce deleted on first use to prevent replay
- `handleGrantTokenDelegate` / `handleRevokeTokenDelegate`: admin-only endpoints to manage delegate access for a system account
- `handleServiceAccountsPage`: new `/service-accounts` page for non-admin delegates to see their assigned service accounts and issue tokens
- New `tokenDownloads sync.Map` in `UIServer` with background cleanup goroutine
**Routes:**
- `POST /accounts/{id}/token` — changed from admin-only to authed+CSRF, authorization checked in handler
- `GET /token/download/{nonce}` — new, authed
- `POST /accounts/{id}/token/delegates` — new, admin-only
- `DELETE /accounts/{id}/token/delegates/{grantee}` — new, admin-only
- `GET /service-accounts` — new, authed (delegates' token management page)
**Templates:**
- `token_list.html`: shows download link after issuance
- `token_delegates.html`: new fragment for admin delegate management
- `account_detail.html`: added "Token Issue Access" section for system accounts
- `service_accounts.html`: new page listing delegated service accounts with issue button
- `base.html`: non-admin nav now shows "Service Accounts" link
### 2026-03-14 — Vault seal/unseal lifecycle