dashboard: clear deferred items — EventsHub publisher + doc refresh
EventsHub publisher (closes the v2.1 follow-up flagged in the previous commit)
EventStreamService now mirrors every MxEvent it forwards to a gRPC client
into the `EventsHub` group for the session. The fan-out goes through a new
singleton `IDashboardEventBroadcaster`:
* IDashboardEventBroadcaster — abstraction so EventStreamService doesn't
take a direct dependency on SignalR.
* DashboardEventBroadcaster — singleton implementation that hands the
SendAsync to IHubContext<EventsHub> as fire-and-forget. Errors are
logged at debug and dropped so the source gRPC stream is never
blocked.
EventStreamService now takes IDashboardEventBroadcaster as a ctor parameter
and calls Publish(sessionId, publicEvent) once per event after sequence
filtering, before the bounded queue write. Test fixtures and the live
integration harness pass NullDashboardEventBroadcaster.Instance so the
broadcaster is a no-op in unit tests.
SessionDetailsPage adds a "Recent events" panel:
* implements IAsyncDisposable
* opens a second HubConnection via DashboardHubConnectionFactory targeting
/hubs/events
* calls SubscribeSession(SessionId) on Start
* renders the most recent 50 events in a small table (worker seq, family,
server/item handle, alarm reference when the event is OnAlarmTransition)
* shows a live/offline conn-pill driven by HubConnection.Closed /
Reconnected events
The dashboard mirror is intentionally passive — events appear only while a
gRPC client is also consuming that session's events. Documented as such in
the empty-state copy and in GatewayDashboardDesign.md.
Documentation refresh
Every doc that referenced the retired options (PathBase, RequireAdminScope,
RequiredGroup) and the old API-key-cookie auth flow is updated to describe
the new model:
* CLAUDE.md — Authentication section now explains LDAP bind +
GroupToRole + HubToken bearer flow.
* gateway.md — Dashboard section: root-mounted routes, snapshot/alarms/
events SignalR hubs, LDAP cookie + bearer scheme.
* docs/GatewayConfiguration.md — drop PathBase / RequireAdminScope rows,
add GroupToRole row, append "Authorization policies" and "SignalR hubs"
subsections describing the three policies and the /hubs/* endpoints.
* docs/GatewayDashboardDesign.md — hosting model (root mount, new
endpoint layout), Realtime Updates rewritten as a hub table
(DashboardSnapshotHub / AlarmsHub / EventsHub with producers, payloads,
and routing), Authentication And Authorization rewritten around LDAP +
role mapping + the hub bearer flow, Configuration block updated.
* docs/GatewayProcessDesign.md — security-section dashboard paragraph
and the example config block both refreshed to LDAP/role auth.
* docs/ImplementationPlanGateway.md — dashboard-auth deliverable list
updated (LDAP bind + GroupToRole + /hubs/token bearer mint replace the
API-key login flow).
* docs/GatewayTesting.md — DashboardLdapLiveTests blurb describes the
GroupToRole fixture (`{ GwAdmin: Admin }`) instead of the retired
RequiredGroup default; success-path assertion explains the role-claim
check.
Verification: 475 server tests, 275 worker tests (+ 9 dev-rig skips), 18
integration tests (live MxAccess + LDAP + Galaxy) all pass — including the
live worker smoke test fixture that now constructs EventStreamService with
the new broadcaster parameter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -289,11 +289,14 @@ Default refresh policy:
|
||||
- periodic metrics refresh every 1 second,
|
||||
- event-rate windows updated every 1 second.
|
||||
|
||||
Dashboard access should require API-key-backed authentication with `admin` scope
|
||||
when enabled. A simple `/dashboard/login` form can validate an API key and issue
|
||||
an HTTP-only secure cookie for dashboard pages. Do not put API keys in query
|
||||
strings. Anonymous localhost access may exist only behind an explicit
|
||||
configuration option that defaults to false.
|
||||
Dashboard access requires LDAP-backed authentication with role mapping when
|
||||
enabled. A simple `/login` form binds against the configured directory, maps
|
||||
the user's groups to `Admin` or `Viewer` via
|
||||
`MxGateway:Dashboard:GroupToRole`, and issues an HTTP-only secure cookie.
|
||||
SignalR hub connections accept either that cookie or a short-lived
|
||||
data-protected bearer minted at `/hubs/token`. Anonymous localhost access is
|
||||
gated by `MxGateway:Dashboard:AllowAnonymousLocalhost` (defaults to true for
|
||||
local development; remote requests always require auth).
|
||||
|
||||
## Session State Machine
|
||||
|
||||
@@ -674,15 +677,17 @@ server-streaming calls and stores the authenticated `ApiKeyIdentity` in
|
||||
`Authentication:Mode` set to `Disabled` bypasses API-key verification for local
|
||||
development only.
|
||||
|
||||
Dashboard authentication reuses the API-key verifier and scope model. The
|
||||
dashboard login endpoint accepts the key in a form post, checks `admin` scope
|
||||
when `Dashboard:RequireAdminScope` is enabled, and signs in with the
|
||||
`ZB.MOM.WW.MxGateway.Dashboard` cookie scheme. The cookie is HTTP-only, secure, strict
|
||||
SameSite, and scoped with the `__Host-MxGatewayDashboard` name. Logout clears
|
||||
that cookie. Login and logout posts use anti-forgery validation, and dashboard
|
||||
API keys are not accepted in query strings. `Dashboard:AllowAnonymousLocalhost`
|
||||
allows only loopback requests to bypass the dashboard cookie requirement and
|
||||
defaults to `true`.
|
||||
Dashboard authentication uses LDAP bind + role mapping (separate from the
|
||||
API-key model used on the gRPC API). The login endpoint accepts username and
|
||||
password in a form post, calls `DashboardAuthenticator` to bind against
|
||||
`MxGateway:Ldap`, resolves the user's LDAP groups through
|
||||
`MxGateway:Dashboard:GroupToRole` to one of `Admin` / `Viewer`, and signs in
|
||||
with the `MxGateway.Dashboard` cookie scheme. The cookie is HTTP-only,
|
||||
secure, strict SameSite, and named `__Host-MxGatewayDashboard`. Logout
|
||||
clears it. Login and logout posts validate antiforgery tokens. SignalR
|
||||
connections additionally accept a 30-minute data-protected bearer minted at
|
||||
`/hubs/token`. `Dashboard:AllowAnonymousLocalhost` permits loopback requests
|
||||
to bypass the cookie requirement and defaults to `true`.
|
||||
|
||||
Recommended scopes:
|
||||
|
||||
@@ -870,13 +875,15 @@ Suggested configuration shape:
|
||||
},
|
||||
"Dashboard": {
|
||||
"Enabled": true,
|
||||
"PathBase": "/dashboard",
|
||||
"RequireAdminScope": true,
|
||||
"AllowAnonymousLocalhost": true,
|
||||
"SnapshotIntervalMilliseconds": 1000,
|
||||
"RecentFaultLimit": 100,
|
||||
"RecentSessionLimit": 200,
|
||||
"ShowTagValues": false
|
||||
"ShowTagValues": false,
|
||||
"GroupToRole": {
|
||||
"GwAdmin": "Admin",
|
||||
"GwReader": "Viewer"
|
||||
}
|
||||
},
|
||||
"Protocol": {
|
||||
"WorkerProtocolVersion": 1
|
||||
@@ -968,7 +975,7 @@ The first gateway slice should implement:
|
||||
13. Blazor Server dashboard with Bootstrap assets.
|
||||
14. Dashboard home, sessions, and workers pages.
|
||||
15. Dashboard realtime snapshot refresh.
|
||||
16. Dashboard API-key login with admin-scope check.
|
||||
16. Dashboard LDAP login mapped to Admin / Viewer roles.
|
||||
17. Basic structured logs.
|
||||
|
||||
This proves the process model before the full command surface is implemented.
|
||||
|
||||
Reference in New Issue
Block a user