docs: cover admin dashboard actions + API key Delete

Update the design docs so they match the implemented Admin-only
dashboard surface. GatewayDashboardDesign now documents the Close
session / Kill worker controls and the new Delete action on revoked
API keys, plus the ConfirmDialog gate for every destructive action.
Sessions.md adds the SessionManager.KillWorkerAsync entry alongside
CloseSessionAsync and explains the immediate-kill semantics. Authentication.md adds the IApiKeyAdminStore.DeleteAsync write path
and the dashboard-delete-key audit event. DashboardInterfaceDesign
drops the "read-only until admin workflows have a separate design"
line in favor of the confirm-before-act invariant.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-24 07:35:25 -04:00
parent 24cc5fd0f0
commit e80f3c70b6
4 changed files with 62 additions and 20 deletions
+40 -14
View File
@@ -241,10 +241,20 @@ Show:
- active server handles and item counts if gateway shadow state has them,
- latest faults,
- last heartbeat payload,
- close/kill controls only if admin actions are later enabled.
- admin Close session / Kill worker controls (Admin role only).
For v1, details should be read-only unless an explicit admin action design is
added.
The Sessions list, the Workers list, and this details page all render the same
admin controls when the signed-in principal carries the `Admin` role; viewers
and the localhost-anonymous bypass see no action affordances and the server
re-checks the role on every invocation. Every destructive admin action is
gated by a confirmation dialog before it reaches `ISessionManager`.
- **Close session** routes through `ISessionManager.CloseSessionAsync`: the
worker is asked to shut down gracefully and is killed only as a fallback if
shutdown fails.
- **Kill worker** routes through `ISessionManager.KillWorkerAsync`: the worker
is killed immediately with no graceful-shutdown attempt. The session is
removed from the registry and the open-session slot is released either way.
### Workers page
@@ -346,29 +356,39 @@ for what each constraint means and how it is enforced on the gRPC path.
#### Management actions
Create, Rotate, and Revoke controls render only when the signed-in user is
authorized. `DashboardApiKeyAuthorization.CanManage` requires an authenticated
principal carrying the `Admin` role claim (resolved at login from the user's
LDAP groups via `MxGateway:Dashboard:GroupToRole`). A `Viewer` role can read
the table but sees no action controls, and an anonymous localhost session
shows the same read-only view.
Create, Rotate, Revoke, and Delete controls render only when the signed-in
user is authorized. `DashboardApiKeyAuthorization.CanManage` requires an
authenticated principal carrying the `Admin` role claim (resolved at login
from the user's LDAP groups via `MxGateway:Dashboard:GroupToRole`). A
`Viewer` role can read the table but sees no action controls, and an
anonymous localhost session shows the same read-only view.
- **Create** opens a dialog for the key id, display name, scope checkboxes
(the `GatewayScopes` catalog), and the optional constraint fields: read and
write subtrees, read and write tag globs, browse subtrees, max write
classification, and the read-alarm-only / read-historized-only flags.
- **Rotate** issues a new secret for an existing key id and invalidates the
old one.
old one. Active keys only — rotating a revoked key would un-revoke it, so
the button is not shown on revoked rows.
- **Revoke** marks a key revoked; a revoked key cannot be un-revoked.
- **Delete** permanently removes a key row from the auth database, but only
when the key is already revoked. `IApiKeyAdminStore.DeleteAsync` rejects
active keys (returns false) so the revoke event lands in the audit log
before the row disappears. Revoked rows show a Delete button in place of
the previous "No actions" placeholder.
Every destructive action (Rotate / Revoke / Delete) is gated by the shared
`ConfirmDialog` component before reaching the service; Create uses its own
form modal as the implicit confirmation step.
Create and Rotate return the assembled `mxgw_<keyId>_<secret>` token **once**,
in a one-time banner. It is never shown again, so the operator must copy it
immediately. This mirrors the `apikey create-key` / `rotate-key` CLI.
Every management action appends an `api_key_audit` entry
(`dashboard-create-key`, `dashboard-rotate-key`, `dashboard-revoke-key`) with
the key id and the caller's remote address. Secrets and pepper values are never
logged.
(`dashboard-create-key`, `dashboard-rotate-key`, `dashboard-revoke-key`,
`dashboard-delete-key`) with the key id and the caller's remote address.
Secrets and pepper values are never logged.
### Settings page
@@ -540,7 +560,7 @@ The first dashboard slice implements:
2. local Bootstrap static assets.
3. dashboard configuration binding.
4. dashboard auth using LDAP bind + role-mapped HTTP-only cookie.
5. read-only `DashboardSnapshotService`.
5. `DashboardSnapshotService` projecting gateway state for read views.
6. home page with metric cards.
7. sessions page with active session table and session details.
8. workers page with worker table.
@@ -550,6 +570,12 @@ The first dashboard slice implements:
12. route-mapping tests, disabled-dashboard tests, auth tests, and snapshot
projection/redaction tests.
Subsequent slices added Admin-gated destructive actions: API-key
Create/Rotate/Revoke (and Delete on revoked keys), and session/worker
Close/Kill via `IDashboardSessionAdminService``ISessionManager`. Every
destructive action passes through the shared `ConfirmDialog` component
before reaching its service.
## Related Documentation
- [Dashboard Interface Design](./DashboardInterfaceDesign.md)