73 lines
3.4 KiB
Markdown
73 lines
3.4 KiB
Markdown
# Quality gates — roles and rights
|
|
|
|
LLM-verified gates for `architecture/roles-auth.md`. Automatic tests
|
|
live in `crabidy-server/src/auth.rs`, `crabidy-server/src/main.rs`
|
|
(hash helper) and `cbd-tui/src/rpc.rs`.
|
|
|
|
## Security posture
|
|
|
|
- [x] Fail-closed everywhere: an unknown/future gRPC method requires
|
|
owner; a present-but-malformed `crabidy-server.toml` aborts server
|
|
startup instead of silently running open; a missing file (or empty
|
|
`[auth]`) is the documented open mode.
|
|
- [x] Guarding order enforced: valid guarded sets are prefixes of
|
|
`[owner, queue_owner, queue_appender]`. A config guarding a lower
|
|
role while a higher one is open (`queue_owner` without `owner`, or
|
|
`queue_appender` without `queue_owner`) aborts startup, and
|
|
`crabidy-server guard` refuses to write it.
|
|
- [x] A present-but-wrong credential is denied (`UNAUTHENTICATED`),
|
|
never silently downgraded to the anonymous role.
|
|
- [x] No panics on request input: malformed `authorization` headers
|
|
(bad base64, missing colon, non-UTF-8, wrong scheme), unknown
|
|
role names, and oversized values all produce `UNAUTHENTICATED`,
|
|
never a panic.
|
|
- [x] Indistinguishable failures: wrong user and wrong password both
|
|
answer plain `UNAUTHENTICATED` with the same message.
|
|
- [x] Secrets redacted: passwords, authorization header values, and
|
|
PHC hashes never appear in logs, traces, or error messages
|
|
(including the `hash-password` helper and TUI logs).
|
|
- [x] Authorization is enforced in exactly one place (the tower
|
|
layer), before any handler runs; RPC handlers did not change.
|
|
|
|
## Semantics
|
|
|
|
- [x] Rights matrix implemented as specified: appender = reads +
|
|
`Append` + `CreateLibraryNode`; queue-owner adds every queue and
|
|
playback verb; owner-only = `CaptureLibraryNode`, `SaveQueue`,
|
|
`RenameLibraryNode`, `DeleteLibraryNode`. Higher roles include
|
|
lower ones. A test pins the full method list of the proto service
|
|
so an unmapped new RPC fails the suite.
|
|
- [x] A caller (anonymous or credentialed) below a method's minimum
|
|
role gets `PERMISSION_DENIED` (not `UNAUTHENTICATED`).
|
|
- [x] Anonymous callers inherit the highest *unguarded* role: nothing
|
|
guarded ⇒ owner (today's open server); `owner` guarded ⇒
|
|
queue-owner; `owner`+`queue_owner` guarded ⇒ queue-appender; all
|
|
guarded ⇒ nothing (`UNAUTHENTICATED`). A credential elevates above
|
|
that floor.
|
|
- [x] No `[auth]` hashes ⇒ exactly today's behavior: no header
|
|
required, all methods allowed.
|
|
- [x] A role without a configured hash cannot authenticate (a login
|
|
attempt as it is denied; anonymous callers may still inherit it).
|
|
|
|
## Performance
|
|
|
|
- [x] Argon2 verification runs once per credential: successful
|
|
verifications are cached (header value → role) and the cache is
|
|
only fed by successes, keeping it bounded by the number of valid
|
|
credentials.
|
|
|
|
## Client
|
|
|
|
- [x] Without configured credentials the TUI sends no header
|
|
(zero-config local use unchanged); with credentials, every
|
|
request — including update-stream reconnects — carries the same
|
|
`authorization: Basic` header.
|
|
- [x] The client config documents that `password` is plaintext and the
|
|
file must be kept private.
|
|
|
|
## Tooling
|
|
|
|
- [x] `crabidy-server hash-password` reads the password from stdin
|
|
(nothing echoed by the tool itself), prints only the PHC string,
|
|
and its output verifies against the same server code path.
|