feat(api): cover nodes, access, members, audit #11

Merged
ryan.mcgee merged 32 commits from feat/api-coverage into main 2026-10-15 18:07:49 +00:00
Owner

Summary

  • The integration API now covers what the web interface can do for nodes, access control, members and the audit log. Stacked on #10 (feat/blueprints).

Changes

  • Nodes: POST /nodes, PATCH/DELETE /nodes/:id, POST /nodes/:id/token. A token is returned only by the response that creates the node or replaces its token, never in a listing; the OpenAPI schema has separate Node and NodeWithToken shapes, and the contract test checks that nothing else carries a token.
  • Access: GET/PATCH /resources/:id/access (sso, password, pin; text sets, null removes; empty and non-text values are 422). Passwords and PINs are write-only: reads show password_set and pin_set. Rules (GET, POST, PATCH, DELETE) and share links (GET, POST, DELETE; the link's token only in the creating response). Raw TCP/UDP resources answer 422.
  • Members: GET /members and DELETE /members/:id (the last admin is refused with 422). There is deliberately no way to add or promote a member: a key must not be able to grant a person access to the organization, the same reasoning that keeps keys from managing keys. A test pins the route set.
  • Audit: GET /audit-events with category, limit (1 to 100) and an opaque before cursor, readable by read keys too so a log collector needs no more. This reverses a decision of #9 (audit log readable by admin users only); Audit.list_events/2 now accepts an API-key scope. get_event! and subscribe stay admin-user only. The cursor is built from plain text, never a serialized term.
  • Share links by API keys: create_share_link no longer requires a person (created_by_id is nullable, nilified on user delete). A write key can already clear a resource's password, so this grants no new reach. Scope docs updated.
  • Contract tests: spec equals router (now 32 routes), response schemas equal serializer fields, enums equal the code's, every documented input is honored, query parameters appear only on reads.

Verification

  • mix format --check-formatted, mix compile --warnings-as-errors, mix credo --strict (no issues), mix dialyzer (0 errors).
  • mix test: 877 passed on SQLite and on PostgreSQL. No migration.
  • Live smoke on the compose stack with a write and a read key: node create, token regeneration (the new token authenticates a real node poll, a bogus one gets 401), read key refused with 403, access settings set and cleared, rule and share link create/delete, member list, audit pagination with the cursor, invalid category 422. The dev database was restored afterwards.
## Summary - The integration API now covers what the web interface can do for nodes, access control, members and the audit log. Stacked on #10 (`feat/blueprints`). ## Changes - **Nodes:** `POST /nodes`, `PATCH`/`DELETE /nodes/:id`, `POST /nodes/:id/token`. A token is returned only by the response that creates the node or replaces its token, never in a listing; the OpenAPI schema has separate `Node` and `NodeWithToken` shapes, and the contract test checks that nothing else carries a token. - **Access:** `GET`/`PATCH /resources/:id/access` (`sso`, `password`, `pin`; text sets, `null` removes; empty and non-text values are `422`). Passwords and PINs are write-only: reads show `password_set` and `pin_set`. Rules (`GET`, `POST`, `PATCH`, `DELETE`) and share links (`GET`, `POST`, `DELETE`; the link's token only in the creating response). Raw TCP/UDP resources answer `422`. - **Members:** `GET /members` and `DELETE /members/:id` (the last admin is refused with `422`). There is deliberately no way to add or promote a member: a key must not be able to grant a person access to the organization, the same reasoning that keeps keys from managing keys. A test pins the route set. - **Audit:** `GET /audit-events` with `category`, `limit` (1 to 100) and an opaque `before` cursor, readable by read keys too so a log collector needs no more. **This reverses a decision of #9** (audit log readable by admin users only); `Audit.list_events/2` now accepts an API-key scope. `get_event!` and `subscribe` stay admin-user only. The cursor is built from plain text, never a serialized term. - **Share links by API keys:** `create_share_link` no longer requires a person (`created_by_id` is nullable, nilified on user delete). A write key can already clear a resource's password, so this grants no new reach. `Scope` docs updated. - Contract tests: spec equals router (now 32 routes), response schemas equal serializer fields, enums equal the code's, every documented input is honored, query parameters appear only on reads. ## Verification - `mix format --check-formatted`, `mix compile --warnings-as-errors`, `mix credo --strict` (no issues), `mix dialyzer` (0 errors). - `mix test`: 877 passed on SQLite and on PostgreSQL. No migration. - Live smoke on the compose stack with a write and a read key: node create, token regeneration (the new token authenticates a real node poll, a bogus one gets 401), read key refused with 403, access settings set and cleared, rule and share link create/delete, member list, audit pagination with the cursor, invalid category `422`. The dev database was restored afterwards.
- nodes: create, update, delete and token replacement; a token is
  only in the response that creates or replaces it
- access settings (write-only password and PIN), rules and share
  links per resource
- members: list and remove; no invitations or role changes, so a
  key cannot grant a person access
- audit-events: cursor-paged, filterable, readable by any key
- share links no longer need a person (created_by is nullable)
- OpenAPI entries and contract tests for every new endpoint
ryan.mcgee changed target branch from feat/blueprints to main 2026-10-15 18:07:40 +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
White_Ghost_Cyber_Systems/traefik-control!11
No description provided.