Fix policy form roles; add JSON edit mode

- Replace stale "service" role option with correct set:
  admin, user, guest, viewer, editor, commenter (matches model.go)
- Add Form/JSON tab toggle to policy create form
- JSON tab accepts raw RuleBody JSON with description/priority
- Handler detects rule_json field and parses/validates it
  directly, falling back to field-by-field form mode otherwise
This commit is contained in:
Claude Opus 4.6
2026-03-16 15:21:26 -07:00
committed by Kyle Isom
parent 7db560dae4
commit 19fa0c9a8e
7 changed files with 422 additions and 150 deletions

View File

@@ -5,7 +5,19 @@ See ARCHITECTURE.md for design rationale.
---
## Phase 0 — Repository Bootstrap
## Status
**v1.0.0 tagged (2026-03-15). All phases complete.**
All packages pass `go test ./...`; `golangci-lint run ./...` clean.
See PROGRESS.md for the detailed development log.
Phases 09 match the original plan. Phases 1013 document significant
features implemented beyond the original plan scope.
---
## Phase 0 — Repository Bootstrap **[COMPLETE]**
### Step 0.1: Go module and dependency setup
**Acceptance criteria:**
@@ -23,7 +35,7 @@ See ARCHITECTURE.md for design rationale.
---
## Phase 1 — Foundational Packages
## Phase 1 — Foundational Packages **[COMPLETE]**
### Step 1.1: `internal/model` — shared data types
**Acceptance criteria:**
@@ -69,7 +81,7 @@ See ARCHITECTURE.md for design rationale.
---
## Phase 2 — Authentication Core
## Phase 2 — Authentication Core **[COMPLETE]**
### Step 2.1: `internal/token` — JWT issuance and validation
**Acceptance criteria:**
@@ -107,7 +119,7 @@ See ARCHITECTURE.md for design rationale.
---
## Phase 3 — HTTP Server
## Phase 3 — HTTP Server **[COMPLETE]**
### Step 3.1: `internal/middleware` — HTTP middleware
**Acceptance criteria:**
@@ -143,6 +155,7 @@ See ARCHITECTURE.md for design rationale.
- `POST /v1/auth/totp/confirm` — confirms TOTP enrollment
- `DELETE /v1/auth/totp` — admin; removes TOTP from account
- `GET|PUT /v1/accounts/{id}/pgcreds` — get/set Postgres credentials
- `GET /v1/pgcreds` — list all accessible credentials (owned + granted)
- Credential fields (password hash, TOTP secret, Postgres password) are
**never** included in any API response
- Tests: each endpoint happy path; auth middleware applied correctly; invalid
@@ -160,7 +173,7 @@ See ARCHITECTURE.md for design rationale.
---
## Phase 4 — Admin CLI
## Phase 4 — Admin CLI **[COMPLETE]**
### Step 4.1: `cmd/mciasctl` — admin CLI
**Acceptance criteria:**
@@ -177,6 +190,7 @@ See ARCHITECTURE.md for design rationale.
- `mciasctl role revoke -id UUID -role ROLE`
- `mciasctl token issue -id UUID` (system accounts)
- `mciasctl token revoke -jti JTI`
- `mciasctl pgcreds list`
- `mciasctl pgcreds set -id UUID -host H -port P -db D -user U`
- `mciasctl pgcreds get -id UUID`
- `mciasctl auth login`
@@ -191,7 +205,7 @@ See ARCHITECTURE.md for design rationale.
---
## Phase 5 — End-to-End Tests and Hardening
## Phase 5 — End-to-End Tests and Hardening **[COMPLETE]**
### Step 5.1: End-to-end test suite
**Acceptance criteria:**
@@ -228,7 +242,7 @@ See ARCHITECTURE.md for design rationale.
---
## Phase 6 — mciasdb: Database Maintenance Tool
## Phase 6 — mciasdb: Database Maintenance Tool **[COMPLETE]**
See ARCHITECTURE.md §16 for full design rationale, trust model, and command
surface.
@@ -314,9 +328,7 @@ surface.
---
---
## Phase 7 — gRPC Interface
## Phase 7 — gRPC Interface **[COMPLETE]**
See ARCHITECTURE.md §17 for full design rationale, proto definitions, and
transport security requirements.
@@ -324,7 +336,8 @@ transport security requirements.
### Step 7.1: Protobuf definitions and generated code
**Acceptance criteria:**
- `proto/mcias/v1/` directory contains `.proto` files for all service groups:
`auth.proto`, `token.proto`, `account.proto`, `admin.proto`
`auth.proto`, `token.proto`, `account.proto`, `policy.proto`, `admin.proto`,
`common.proto`
- All RPC methods mirror the REST API surface (see ARCHITECTURE.md §8 and §17)
- `proto/generate.go` contains a `//go:generate protoc ...` directive that
produces Go stubs under `gen/mcias/v1/` using `protoc-gen-go` and
@@ -357,10 +370,11 @@ transport security requirements.
- gRPC server uses the same TLS certificate and key as the REST server (loaded
from config); minimum TLS 1.2 enforced via `tls.Config`
- Unary server interceptor chain:
1. Request logger (method name, peer IP, status, duration)
2. Auth interceptor (extracts Bearer token, validates, injects claims into
1. Sealed interceptor (blocks all RPCs when vault sealed, except Health)
2. Request logger (method name, peer IP, status, duration)
3. Auth interceptor (extracts Bearer token, validates, injects claims into
`context.Context`)
3. Rate-limit interceptor (per-IP token bucket, same parameters as REST)
4. Rate-limit interceptor (per-IP token bucket, same parameters as REST)
- No credential material logged by any interceptor
- Tests: interceptor chain applied correctly; rate-limit triggers after burst
@@ -396,7 +410,7 @@ transport security requirements.
---
## Phase 8 — Operational Artifacts
## Phase 8 — Operational Artifacts **[COMPLETE]**
See ARCHITECTURE.md §18 for full design rationale and artifact inventory.
@@ -461,7 +475,10 @@ See ARCHITECTURE.md §18 for full design rationale and artifact inventory.
- `generate``go generate ./...` (proto stubs from Phase 7)
- `man` — build compressed man pages
- `install` — run `dist/install.sh`
- `clean` — remove `bin/` and generated artifacts
- `docker``docker build -t mcias:$(VERSION) -t mcias:latest .`
- `docker-clean` — remove local `mcias:$(VERSION)` and `mcias:latest` images;
prune dangling images with the mcias label
- `clean` — remove `bin/`, compressed man pages, and local Docker images
- `dist` — build release tarballs for linux/amd64 and linux/arm64 (using
`GOOS`/`GOARCH` cross-compilation)
- `make build` works from a clean checkout after `go mod download`
@@ -483,13 +500,10 @@ See ARCHITECTURE.md §18 for full design rationale and artifact inventory.
- `dist/mcias.conf.docker.example` — config template suitable for container
deployment: `listen_addr = "0.0.0.0:8443"`, `grpc_addr = "0.0.0.0:9443"`,
`db_path = "/data/mcias.db"`, TLS cert/key paths under `/etc/mcias/`
- `Makefile` gains a `docker` target: `docker build -t mcias:$(VERSION) .`
where `VERSION` defaults to the output of `git describe --tags --always`
- Tests:
- `docker build .` completes without error (run in CI if Docker available;
skip gracefully if not)
- `docker run --rm mcias:latest mciassrv --help` exits 0
- Image size documented in PROGRESS.md (target: under 50 MB)
### Step 8.7: Documentation
**Acceptance criteria:**
@@ -501,7 +515,7 @@ See ARCHITECTURE.md §18 for full design rationale and artifact inventory.
---
## Phase 9 — Client Libraries
## Phase 9 — Client Libraries **[COMPLETE]**
See ARCHITECTURE.md §19 for full design rationale, API surface, and per-language
implementation notes.
@@ -606,6 +620,130 @@ implementation notes.
---
## Phase 10 — Web UI (HTMX) **[COMPLETE]**
Not in the original plan. Implemented alongside and after Phase 3.
See ARCHITECTURE.md §8 (Web Management UI) for design details.
### Step 10.1: `internal/ui` — HTMX web interface
**Acceptance criteria:**
- Go `html/template` pages embedded at compile time via `web/embed.go`
- CSRF protection: HMAC-signed double-submit cookie (`mcias_csrf`)
- Session: JWT stored as `HttpOnly; Secure; SameSite=Strict` cookie
- Security headers: `Content-Security-Policy: default-src 'self'`,
`X-Frame-Options: DENY`, `Referrer-Policy: strict-origin`
- Pages: login, dashboard, account list/detail, role editor, tag editor,
pgcreds, audit log viewer, policy rules, user profile, service-accounts
- HTMX partial-page updates for mutations (role updates, tag edits, policy
toggles, access grants)
- Empty-state handling on all list pages (zero records case tested)
### Step 10.2: Swagger UI at `/docs`
**Acceptance criteria:**
- `GET /docs` serves Swagger UI for `openapi.yaml`
- swagger-ui-bundle.js and swagger-ui.css bundled locally in `web/static/`
(CDN blocked by CSP `default-src 'self'`)
- `GET /docs/openapi.yaml` serves the OpenAPI spec
- `openapi.yaml` kept in sync with REST API surface
---
## Phase 11 — Authorization Policy Engine **[COMPLETE]**
Not in the original plan (CLI subcommands for policy were planned in Phase 4,
but the engine itself was not a discrete plan phase).
See ARCHITECTURE.md §20 for full design, evaluation algorithm, and built-in
default rules.
### Step 11.1: `internal/policy` — in-process ABAC engine
**Acceptance criteria:**
- Pure evaluation: `Evaluate(input PolicyInput, rules []Rule) (Effect, *Rule)`
- Deny-wins: any explicit deny overrides all allows
- Default-deny: no matching rule → deny
- Built-in default rules (IDs -1 … -7) compiled in; reproduce previous
binary admin/non-admin behavior exactly; cannot be disabled via API
- Match fields: roles, account types, subject UUID, actions, resource type,
owner-matches-subject, service names, required tags (all ANDed; zero value
= wildcard)
- Temporal constraints on DB-backed rules: `not_before`, `expires_at`
- `Engine` wrapper: caches rule set in memory; reloads on policy mutations
- Tests: all built-in rules; deny-wins over allow; default-deny fallback;
temporal filtering; concurrent access
### Step 11.2: Middleware and REST integration
**Acceptance criteria:**
- `RequirePolicy(engine, action, resourceType)` middleware replaces
`RequireRole("admin")` where policy-gated
- Every explicit deny produces a `policy_deny` audit event
- REST endpoints: `GET|POST /v1/policy/rules`, `GET|PATCH|DELETE /v1/policy/rules/{id}`
- DB schema: `policy_rules` and `account_tags` tables (migrations 000004,
000006)
- `PATCH /v1/policy/rules/{id}` supports updating `priority`, `enabled`,
`not_before`, `expires_at`
---
## Phase 12 — Vault Seal/Unseal Lifecycle **[COMPLETE]**
Not in the original plan.
See ARCHITECTURE.md §8 (Vault Endpoints) for the API surface.
### Step 12.1: `internal/vault` — master key lifecycle
**Acceptance criteria:**
- Thread-safe `Vault` struct with `sync.RWMutex`-protected state
- Methods: `IsSealed()`, `Unseal(passphrase)`, `Seal()`, `MasterKey()`,
`PrivKey()`, `PubKey()`
- `Seal()` zeroes all key material before nilling (memguard-style cleanup)
- `DeriveFromPassphrase()` and `DecryptSigningKey()` extracted to `derive.go`
for reuse by unseal handlers
- Tests: state transitions; key zeroing verified; concurrent read/write safety
### Step 12.2: REST and UI integration
**Acceptance criteria:**
- `POST /v1/vault/unseal` — rate-limited (3/s burst 5); derives key, unseals
- `GET /v1/vault/status` — always accessible; returns `{"sealed": bool}`
- `POST /v1/vault/seal` — admin only; zeroes key material
- `GET /v1/health` returns `{"status":"sealed"}` when sealed
- All other `/v1/*` endpoints return 503 `vault_sealed` when sealed
- UI redirects all paths to `/unseal` when sealed (except `/static/`)
- gRPC: `sealedInterceptor` first in chain; blocks all RPCs except Health
- Startup: server may start in sealed state if passphrase env var is absent
- Audit events: `vault_sealed`, `vault_unsealed`
---
## Phase 13 — Token Delegation and pgcred Access Grants **[COMPLETE]**
Not in the original plan.
See ARCHITECTURE.md §21 (Token Issuance Delegation) for design details.
### Step 13.1: Service account token delegation
**Acceptance criteria:**
- DB migration 000008: `service_account_delegates` table
- `POST /accounts/{id}/token/delegates` — admin grants delegation
- `DELETE /accounts/{id}/token/delegates/{grantee}` — admin revokes delegation
- `POST /accounts/{id}/token` — accepts admin or delegate (not admin-only)
- One-time token download: nonce stored in `sync.Map` with 5-minute TTL;
`GET /token/download/{nonce}` serves token as attachment, deletes nonce
- `/service-accounts` page for non-admin delegates
- Audit events: `token_delegate_granted`, `token_delegate_revoked`
### Step 13.2: pgcred fine-grained access grants
**Acceptance criteria:**
- DB migration 000005: `pgcred_access_grants` table
- `POST /accounts/{id}/pgcreds/access` — owner grants read access to grantee
- `DELETE /accounts/{id}/pgcreds/access/{grantee}` — owner revokes access
- `GET /v1/pgcreds` — lists all credentials accessible to caller (owned +
granted); includes credential ID for reference
- Grantees may view connection metadata; password is never decrypted for them
- Audit events: `pgcred_access_granted`, `pgcred_access_revoked`
---
## Implementation Order
```
@@ -618,6 +756,11 @@ Phase 0 → Phase 1 (1.1, 1.2, 1.3, 1.4 in parallel or sequence)
→ Phase 7 (7.1 → 7.2 → 7.3 → 7.4 → 7.5 → 7.6)
→ Phase 8 (8.1 → 8.2 → 8.3 → 8.4 → 8.5 → 8.6)
→ Phase 9 (9.1 → 9.2 → 9.3 → 9.4 → 9.5 → 9.6)
→ Phase 10 (interleaved with Phase 3 and later phases)
→ Phase 11 (interleaved with Phase 34)
→ Phase 12 (post Phase 3)
→ Phase 13 (post Phase 3 and 11)
```
Each step must have passing tests before the next step begins.
All phases complete as of v1.0.0 (2026-03-15).