diff --git a/CLAUDE.md b/CLAUDE.md index 369c120..37165b0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -116,7 +116,7 @@ External analysis sources referenced by design docs: Gateway gRPC clients authenticate with an API key in metadata: `authorization: Bearer mxgw__`. Keys are stored hashed (with a peppered SHA) in a gateway-owned SQLite DB (default `C:\ProgramData\MxGateway\gateway-auth.db`). Scopes (`session`, `invoke`, `event`, `metadata`, `admin`) gate specific RPCs; missing → `Unauthenticated`, insufficient → `PermissionDenied`. The `apikey` subcommand on the server exe manages keys; see `src/ZB.MOM.WW.MxGateway.Server/Security/Authentication/`. -Dashboard auth is LDAP-backed (separate from the gRPC API-key model). `/login` binds against `MxGateway:Ldap` and maps the user's LDAP groups to `Admin` or `Viewer` via `MxGateway:Dashboard:GroupToRole`, then issues an HTTP-only secure `__Host-MxGatewayDashboard` cookie. SignalR hubs at `/hubs/{snapshot,alarms,events}` accept either the cookie or a 30-minute bearer minted at `/hubs/token`. `Dashboard:AllowAnonymousLocalhost` bypasses auth on loopback when enabled. +Dashboard auth is LDAP-backed (separate from the gRPC API-key model). `/login` binds against `MxGateway:Ldap` and maps the user's LDAP groups to `Admin` or `Viewer` via `MxGateway:Dashboard:GroupToRole`, then issues an HTTP-only secure `__Host-MxGatewayDashboard` cookie. SignalR hubs at `/hubs/{snapshot,alarms,events}` accept either the cookie or a 30-minute bearer minted at `/hubs/token`. `Dashboard:AllowAnonymousLocalhost` bypasses auth on loopback when enabled. `Dashboard:DisableLogin` (default `false`) auto-authenticates every dashboard request — including remote browsers — as `Dashboard:AutoLoginUser` (default `multi-role`) with both Admin and Viewer roles; dev/test only, never enable in production. ## Process / Platform Notes diff --git a/docs/GatewayConfiguration.md b/docs/GatewayConfiguration.md index 73cea43..3453c63 100644 --- a/docs/GatewayConfiguration.md +++ b/docs/GatewayConfiguration.md @@ -171,6 +171,8 @@ events (a "gap") and must re-snapshot; whatever is still retained is replayed. | `MxGateway:Dashboard:RecentSessionLimit` | `200` | Maximum number of session summaries projected into each dashboard snapshot. | | `MxGateway:Dashboard:ShowTagValues` | `false` | Reserved display control for tag values. The dashboard does not show full tag values by default. | | `MxGateway:Dashboard:GroupToRole` | _(empty)_ | LDAP group → dashboard role mapping. Keys are LDAP group names (short CN or full DN — leading-RDN match). Values must be `Admin` (read/write, API-key CRUD) or `Viewer` (read-only). A user whose LDAP groups don't intersect this map cannot sign in; with no mapping at all, only the loopback bypass admits anyone. | +| `MxGateway:Dashboard:DisableLogin` | `false` | Dev/test only. When `true`, replaces the cookie authentication handler with `DashboardAutoLoginAuthenticationHandler`, which auto-authenticates every dashboard request — including requests from remote browsers, not just loopback — as `AutoLoginUser` holding both `Administrator` and `Viewer` roles. No login form, LDAP bind, or cookie is involved. A loud one-time startup warning is logged. Differs from `AllowAnonymousLocalhost`: `DisableLogin` mints a real authenticated principal (so role-gated write affordances appear), whereas `AllowAnonymousLocalhost` satisfies the authorization requirement on loopback only without minting a principal (write affordances stay hidden). Never enable in production. | +| `MxGateway:Dashboard:AutoLoginUser` | `multi-role` | Username stamped on the synthetic principal when `DisableLogin` is `true`. Has no effect when `DisableLogin` is `false`. | `SnapshotIntervalMilliseconds` must be greater than zero. `RecentFaultLimit` and `RecentSessionLimit` must be greater than or equal to zero. diff --git a/docs/GatewayDashboardDesign.md b/docs/GatewayDashboardDesign.md index d7e6baf..ed49e4b 100644 --- a/docs/GatewayDashboardDesign.md +++ b/docs/GatewayDashboardDesign.md @@ -442,6 +442,37 @@ authorizes every request, and `MxGateway:Dashboard:AllowAnonymousLocalhost` requests always require an authenticated principal carrying at least the Viewer role. +### DisableLogin dev bypass + +`MxGateway:Dashboard:DisableLogin` (default `false`) is a third bypass for +dev and test environments where LDAP is unavailable or irrelevant. + +When the flag is `true`, the `DashboardAuthenticator`-backed cookie handler is +replaced by `DashboardAutoLoginAuthenticationHandler`, registered under the +same scheme name (`MxGateway.Dashboard`). The handler auto-authenticates every +incoming request — including requests from remote browsers, not just loopback — +as a principal for `MxGateway:Dashboard:AutoLoginUser` (default `multi-role`) +holding both the `Administrator` and `Viewer` role claims. + +The same-scheme-name swap is intentional: every authorization policy +(`MxGateway.Dashboard.Viewer`, `MxGateway.Dashboard.Admin`, +`MxGateway.Dashboard.HubClients`) resolves the `MxGateway.Dashboard` scheme, +so the handler replacement requires zero changes to policies, Razor page +attributes, or hub authorization attributes. `UseAuthentication()` stamps the +principal on `HttpContext.User` for the full HTTP pipeline, the Blazor circuit, +and the SignalR hubs uniformly — there is no separate path for each surface. + +This differs from `AllowAnonymousLocalhost`: that flag satisfies the Viewer +authorization requirement on loopback without minting an authenticated +principal, so role-gated write affordances (Admin-only API-key CRUD, Close/Kill +controls) stay hidden. `DisableLogin` mints a real multi-role principal, so +those affordances appear — which is the point for dev scenarios where a +developer needs the full Admin surface without standing up LDAP. + +A loud one-time startup warning is logged when `DisableLogin` is `true`. The +gRPC API-key authentication path is untouched; only the dashboard cookie +surface is affected. Never enable in production. + ### Hub bearer flow SignalR connections cannot reuse the `__Host-` cookie when the JS client