Implement dashboard and audit log templates, add paginated audit log support
- Added `web/templates/{dashboard,audit,base,accounts,account_detail}.html` for a consistent UI.
- Implemented new audit log endpoint (`GET /v1/audit`) with filtering and pagination via `ListAuditEventsPaged`.
- Extended `AuditQueryParams`, added `AuditEventView` for joined actor/target usernames.
- Updated configuration (`goimports` preference), linting rules, and E2E tests.
- No logic changes to existing APIs.
This commit is contained in:
269
PROJECT_PLAN.md
269
PROJECT_PLAN.md
@@ -305,6 +305,272 @@ surface.
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## Phase 7 — gRPC Interface
|
||||
|
||||
See ARCHITECTURE.md §17 for full design rationale, proto definitions, and
|
||||
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`
|
||||
- All RPC methods mirror the REST API surface (see ARCHITECTURE.md §8 and §17)
|
||||
- `buf.yaml` / `buf.gen.yaml` configured; `buf generate` produces Go stubs under
|
||||
`gen/mcias/v1/`
|
||||
- Protobuf field conventions: `snake_case` field names, `google.protobuf.Timestamp`
|
||||
for all time fields, no credential fields in response messages (same exclusion
|
||||
rules as JSON API)
|
||||
- `go generate ./...` re-runs `buf generate` idempotently
|
||||
- Tests: generated code compiles cleanly; `buf lint` passes with zero warnings
|
||||
|
||||
### Step 7.2: `internal/grpcserver` — gRPC handler implementations
|
||||
**Acceptance criteria:**
|
||||
- Package `internal/grpcserver` implements all generated gRPC service interfaces
|
||||
- Handlers delegate to the same `internal/auth`, `internal/token`, and
|
||||
`internal/db` packages used by the REST server — no duplicated logic
|
||||
- Authentication: Bearer token extracted from gRPC metadata key `authorization`,
|
||||
validated via `internal/middleware` logic (same JWT validation path)
|
||||
- Admin RPCs require the `admin` role (checked identically to REST middleware)
|
||||
- Errors mapped to canonical gRPC status codes:
|
||||
- Unauthenticated → `codes.Unauthenticated`
|
||||
- Forbidden → `codes.PermissionDenied`
|
||||
- Not found → `codes.NotFound`
|
||||
- Invalid input → `codes.InvalidArgument`
|
||||
- Internal error → `codes.Internal` (no internal detail leaked to caller)
|
||||
- Tests: unit tests for each RPC handler; unauthenticated calls rejected;
|
||||
credential fields absent from all response messages
|
||||
|
||||
### Step 7.3: TLS and interceptors
|
||||
**Acceptance criteria:**
|
||||
- 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
|
||||
`context.Context`)
|
||||
3. 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
|
||||
|
||||
### Step 7.4: `cmd/mciassrv` integration — dual-stack server
|
||||
**Acceptance criteria:**
|
||||
- mciassrv starts both the REST listener and the gRPC listener on separate ports
|
||||
(gRPC port configurable: `[server] grpc_addr`; defaults to same host, port+1)
|
||||
- Both listeners share the same signing key, DB connection, and config
|
||||
- Graceful shutdown drains both servers within the configured drain window
|
||||
- Config: `grpc_addr` added to `[server]` section (optional; if absent, gRPC
|
||||
listener is disabled)
|
||||
- Integration test: start server, connect gRPC client, call `Health` RPC,
|
||||
assert OK response
|
||||
|
||||
### Step 7.5: `cmd/mciasgrpcctl` — gRPC admin CLI (optional companion)
|
||||
**Acceptance criteria:**
|
||||
- Binary at `cmd/mciasgrpcctl/main.go` with subcommands mirroring `mciasctl`
|
||||
- Uses generated gRPC client stubs; connects to `--server` address with TLS
|
||||
- Auth via `--token` flag or `MCIAS_TOKEN` env var (sent as gRPC metadata)
|
||||
- Custom CA cert support identical to mciasctl
|
||||
- Tests: flag parsing; missing required flags → error; help text complete
|
||||
|
||||
### Step 7.6: Documentation and commit
|
||||
**Acceptance criteria:**
|
||||
- ARCHITECTURE.md §17 written (proto service layout, transport security,
|
||||
interceptor chain, dual-stack operation)
|
||||
- README.md updated with gRPC section: enabling gRPC, connecting clients,
|
||||
example `grpcurl` invocations
|
||||
- `.gitignore` updated to exclude `mciasgrpcctl` binary and `gen/` directory
|
||||
(generated code committed separately or excluded per project convention)
|
||||
- `PROGRESS.md` updated to reflect Phase 7 complete
|
||||
|
||||
---
|
||||
|
||||
## Phase 8 — Operational Artifacts
|
||||
|
||||
See ARCHITECTURE.md §18 for full design rationale and artifact inventory.
|
||||
|
||||
### Step 8.1: systemd service unit
|
||||
**Acceptance criteria:**
|
||||
- `dist/mcias.service` — a hardened systemd unit for mciassrv:
|
||||
- `Type=notify` with `sd_notify` support (or `Type=simple` with explicit
|
||||
`ExecStart` if notify is not implemented)
|
||||
- `User=mcias` / `Group=mcias` — dedicated service account
|
||||
- `CapabilityBoundingSet=` — no capabilities required (port > 1024 assumed;
|
||||
document this assumption)
|
||||
- `ProtectSystem=strict`, `ProtectHome=true`, `PrivateTmp=true`
|
||||
- `ReadWritePaths=/var/lib/mcias` for the SQLite file
|
||||
- `EnvironmentFile=/etc/mcias/env` for `MCIAS_MASTER_PASSPHRASE`
|
||||
- `Restart=on-failure`, `RestartSec=5`
|
||||
- `LimitNOFILE=65536`
|
||||
- `dist/mcias.env.example` — template environment file with comments explaining
|
||||
each variable
|
||||
- Tests: `systemd-analyze verify dist/mcias.service` exits 0 (run in CI if
|
||||
systemd available; skip gracefully if not)
|
||||
|
||||
### Step 8.2: Default configuration file
|
||||
**Acceptance criteria:**
|
||||
- `dist/mcias.conf.example` — fully-commented TOML config covering all sections
|
||||
from ARCHITECTURE.md §11, including the new `grpc_addr` field from Phase 7
|
||||
- Comments explain every field, acceptable values, and security implications
|
||||
- Defaults match the ARCHITECTURE.md §11 reference config
|
||||
- A separate `dist/mcias-dev.conf.example` suitable for local development
|
||||
(localhost address, relaxed timeouts, short token expiry for testing)
|
||||
|
||||
### Step 8.3: Installation script
|
||||
**Acceptance criteria:**
|
||||
- `dist/install.sh` — POSIX shell script (no bash-isms) that:
|
||||
- Creates `mcias` system user and group (idempotent)
|
||||
- Copies binaries to `/usr/local/bin/`
|
||||
- Creates `/etc/mcias/` and `/var/lib/mcias/` with correct ownership/perms
|
||||
- Installs `dist/mcias.service` to `/etc/systemd/system/`
|
||||
- Prints post-install instructions (key generation, first-run steps)
|
||||
- Does **not** start the service automatically
|
||||
- Script is idempotent (safe to re-run after upgrades)
|
||||
- Tests: shellcheck passes with zero warnings
|
||||
|
||||
### Step 8.4: Man pages
|
||||
**Acceptance criteria:**
|
||||
- `man/man1/mciassrv.1` — man page for the server binary; covers synopsis,
|
||||
description, options, config file format, signals, exit codes, and files
|
||||
- `man/man1/mciasctl.1` — man page for the admin CLI; covers all subcommands
|
||||
with examples
|
||||
- `man/man1/mciasdb.1` — man page for the DB maintenance tool; includes trust
|
||||
model and safety warnings
|
||||
- `man/man1/mciasgrpcctl.1` — man page for the gRPC CLI (Phase 7)
|
||||
- Man pages written in `mdoc` format (BSD/macOS compatible) or `troff` (Linux)
|
||||
- `make man` target in Makefile generates compressed `.gz` versions
|
||||
- Tests: `man --warnings -l man/man1/*.1` exits 0 (or equivalent lint)
|
||||
|
||||
### Step 8.5: Makefile
|
||||
**Acceptance criteria:**
|
||||
- `Makefile` at repository root with targets:
|
||||
- `build` — compile all binaries to `bin/`
|
||||
- `test` — `go test -race ./...`
|
||||
- `lint` — `golangci-lint run ./...`
|
||||
- `generate` — `go generate ./...` (proto stubs from Phase 7)
|
||||
- `man` — build compressed man pages
|
||||
- `install` — run `dist/install.sh`
|
||||
- `clean` — remove `bin/` and generated artifacts
|
||||
- `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`
|
||||
- Tests: `make build` produces binaries; `make test` passes; `make lint` passes
|
||||
|
||||
### Step 8.6: Documentation
|
||||
**Acceptance criteria:**
|
||||
- `README.md` updated with: quick-start section referencing the install script,
|
||||
links to man pages, configuration walkthrough
|
||||
- ARCHITECTURE.md §18 written (operational artifact inventory, file locations,
|
||||
systemd integration notes)
|
||||
- `PROGRESS.md` updated to reflect Phase 8 complete
|
||||
|
||||
---
|
||||
|
||||
## Phase 9 — Client Libraries
|
||||
|
||||
See ARCHITECTURE.md §19 for full design rationale, API surface, and per-language
|
||||
implementation notes.
|
||||
|
||||
### Step 9.1: API surface definition and shared test fixtures
|
||||
**Acceptance criteria:**
|
||||
- `clients/README.md` — defines the canonical client API surface that all
|
||||
language implementations must expose:
|
||||
- `Client` type: configured with server URL, optional CA cert, optional token
|
||||
- `Client.Login(username, password, totp_code) → (token, expires_at, error)`
|
||||
- `Client.Logout() → error`
|
||||
- `Client.RenewToken() → (token, expires_at, error)`
|
||||
- `Client.ValidateToken(token) → (claims, error)`
|
||||
- `Client.GetPublicKey() → (public_key_jwk, error)`
|
||||
- `Client.Health() → error`
|
||||
- Account management methods (admin only): `CreateAccount`, `ListAccounts`,
|
||||
`GetAccount`, `UpdateAccount`, `DeleteAccount`
|
||||
- Role management: `GetRoles`, `SetRoles`
|
||||
- Token management: `IssueServiceToken`, `RevokeToken`
|
||||
- PG credentials: `GetPGCreds`, `SetPGCreds`
|
||||
- `clients/testdata/` — shared JSON fixtures for mock server responses (used
|
||||
by all language test suites)
|
||||
- A mock MCIAS server (`test/mock/`) in Go that can be started by integration
|
||||
tests in any language via subprocess or a pre-built binary
|
||||
|
||||
### Step 9.2: Go client library
|
||||
**Acceptance criteria:**
|
||||
- `clients/go/` — Go module `git.wntrmute.dev/kyle/mcias/clients/go`
|
||||
- Package `mciasgoclient` exposes the canonical API surface from Step 9.1
|
||||
- Uses `net/http` with `crypto/tls`; custom CA cert supported via `x509.CertPool`
|
||||
- Token stored in-memory; `Client.Token()` accessor returns current token
|
||||
- Thread-safe: concurrent calls from multiple goroutines are safe
|
||||
- All JSON decoding uses `DisallowUnknownFields`
|
||||
- Tests:
|
||||
- Unit tests with `httptest.Server` for all methods
|
||||
- Integration test against mock server (Step 9.1) covering full login →
|
||||
validate → logout flow
|
||||
- `go test -race ./...` passes with zero race conditions
|
||||
- `go doc` comments on all exported types and methods
|
||||
|
||||
### Step 9.3: Rust client library
|
||||
**Acceptance criteria:**
|
||||
- `clients/rust/` — Rust crate `mcias-client`
|
||||
- Uses `reqwest` (async, TLS-enabled) and `serde` / `serde_json`
|
||||
- Exposes the canonical API surface from Step 9.1 as an async Rust API
|
||||
(`tokio`-compatible)
|
||||
- Custom CA cert supported via `reqwest::Certificate`
|
||||
- Token stored in `Arc<Mutex<Option<String>>>` for async-safe sharing
|
||||
- Errors: typed `MciasError` enum covering `Unauthenticated`, `Forbidden`,
|
||||
`NotFound`, `InvalidInput`, `Transport`, `Server`
|
||||
- Tests:
|
||||
- Unit tests with `wiremock` or `mockito` for all methods
|
||||
- Integration test against mock server covering full login → validate → logout
|
||||
- `cargo test` passes; `cargo clippy -- -D warnings` passes
|
||||
- `cargo doc` with `#[doc]` comments on all public items
|
||||
|
||||
### Step 9.4: Common Lisp client library
|
||||
**Acceptance criteria:**
|
||||
- `clients/lisp/` — ASDF system `mcias-client`
|
||||
- Uses `dexador` for HTTP and `yason` (or `cl-json`) for JSON
|
||||
- TLS handled by the underlying Dexador/Usocket stack; custom CA cert
|
||||
documented (platform-specific)
|
||||
- Exposes the canonical API surface from Step 9.1 as synchronous functions
|
||||
with keyword arguments
|
||||
- Errors signalled as conditions: `mcias-error`, `mcias-unauthenticated`,
|
||||
`mcias-forbidden`, `mcias-not-found`
|
||||
- Token stored as a slot on the `mcias-client` CLOS object
|
||||
- Tests:
|
||||
- Unit tests using `fiveam` with mock HTTP responses (via `dexador` mocking
|
||||
or a local test server)
|
||||
- Integration test against mock server covering full login → validate → logout
|
||||
- Tests run with SBCL; `(asdf:test-system :mcias-client)` passes
|
||||
- Docstrings on all exported symbols; `(describe 'mcias-client)` is informative
|
||||
|
||||
### Step 9.5: Python client library
|
||||
**Acceptance criteria:**
|
||||
- `clients/python/` — Python package `mcias_client`; supports Python 3.11+
|
||||
- Uses `httpx` (sync and async variants) or `requests` (sync-only acceptable
|
||||
for v1)
|
||||
- Exposes the canonical API surface from Step 9.1 as a `MciasClient` class
|
||||
- Custom CA cert supported via `ssl.create_default_context()` with `cafile`
|
||||
- Token stored as an instance attribute; `client.token` property
|
||||
- Errors: `MciasError` base class with subclasses `MciasAuthError`,
|
||||
`MciasForbiddenError`, `MciasNotFoundError`
|
||||
- Typed: full `py.typed` marker; all public symbols annotated with PEP 526
|
||||
type annotations; `mypy --strict` passes
|
||||
- Tests:
|
||||
- Unit tests with `pytest` and `respx` (httpx mock) or `responses` (requests)
|
||||
for all methods
|
||||
- Integration test against mock server covering full login → validate → logout
|
||||
- `pytest` passes; `ruff check` and `mypy --strict` pass
|
||||
- Docstrings on all public classes and methods; `help(MciasClient)` is
|
||||
informative
|
||||
|
||||
### Step 9.6: Documentation and commit
|
||||
**Acceptance criteria:**
|
||||
- Each client library has its own `README.md` with: installation instructions,
|
||||
quickstart example, API reference summary, error handling guide
|
||||
- ARCHITECTURE.md §19 written (client library design, per-language notes,
|
||||
versioning strategy)
|
||||
- `PROGRESS.md` updated to reflect Phase 9 complete
|
||||
|
||||
---
|
||||
|
||||
## Implementation Order
|
||||
|
||||
```
|
||||
@@ -314,6 +580,9 @@ Phase 0 → Phase 1 (1.1, 1.2, 1.3, 1.4 in parallel or sequence)
|
||||
→ Phase 4
|
||||
→ Phase 5
|
||||
→ Phase 6 (6.1 → 6.2 → 6.3 → 6.4 → 6.5 → 6.6 → 6.7)
|
||||
→ 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)
|
||||
```
|
||||
|
||||
Each step must have passing tests before the next step begins.
|
||||
|
||||
Reference in New Issue
Block a user