diff --git a/docs/requirements/Component-Security.md b/docs/requirements/Component-Security.md index 8aa354cc..44e806a3 100644 --- a/docs/requirements/Component-Security.md +++ b/docs/requirements/Component-Security.md @@ -24,9 +24,9 @@ Central cluster. Sites do not have user-facing interfaces and do not perform ind ## Dev Disable-Login Flag -**`ScadaBridge:Security:Auth:DisableLogin`** (bool, default `false`) — when `true`, the Central UI bypasses the login form entirely and auto-authenticates every request as the user named by `ScadaBridge:Security:Auth:User` (default `multi-role`) with all four roles (Administrator, Designer, Deployer, Viewer) granted system-wide. The mechanism is `AutoLoginAuthenticationHandler`, registered under the cookie scheme via `AddSecurity(disableLogin: true)`; because it sits in the cookie scheme, every existing authorization policy authenticates through it with zero policy changes required. +**`ScadaBridge:Security:Auth:DisableLogin`** (bool, default `false`) — when `true`, the Central UI bypasses the login form entirely and auto-authenticates every request as the user named by `ScadaBridge:Security:Auth:User` (default `multi-role`) with all six roles (Administrator, Designer, Deployer, Viewer, Operator, Verifier) granted system-wide. The mechanism is `AutoLoginAuthenticationHandler`, registered under the cookie scheme via `AddSecurity(disableLogin: true)`; because it sits in the cookie scheme, every existing authorization policy authenticates through it with zero policy changes required. -There is **no environment guard** — a loud startup warning in the application log is the only protection. This disables authentication on a SCADA control surface. +**Environment guard (`DisableLoginGuard`).** Because the flag also grants Operator+Verifier — nullifying the two-person secured-write control — `DisableLogin=true` is **refused outside the Development environment**. `DisableLoginGuard.Validate` runs at the Host composition root (before `AddSecurity`) and throws at startup unless the environment is `Development` (case-insensitive) or the operator sets the explicit second acknowledgement key **`ScadaBridge:Security:Auth:AllowOutsideDevelopment`** (bool, default `false`). The ack key exists only for the rare deliberate case of an operator who fully accepts an unauthenticated deployment; absent it, a non-Development host fails fast rather than booting unauthenticated. > **Dev/test ONLY. Never enable in production.** diff --git a/src/ZB.MOM.WW.ScadaBridge.Host/Program.cs b/src/ZB.MOM.WW.ScadaBridge.Host/Program.cs index 3389d96b..7f75a8eb 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Host/Program.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Host/Program.cs @@ -135,6 +135,14 @@ try var disableLogin = builder.Configuration .GetSection(ZB.MOM.WW.ScadaBridge.Security.Auth.AuthDisableLoginOptions.SectionName) .GetValue(nameof(ZB.MOM.WW.ScadaBridge.Security.Auth.AuthDisableLoginOptions.DisableLogin)); + // Fail-fast guard: DisableLogin outside Development requires the explicit + // AllowOutsideDevelopment acknowledgement key. See DisableLoginGuard. + ZB.MOM.WW.ScadaBridge.Security.Auth.DisableLoginGuard.Validate( + disableLogin, + builder.Environment.EnvironmentName, + builder.Configuration + .GetSection(ZB.MOM.WW.ScadaBridge.Security.Auth.AuthDisableLoginOptions.SectionName) + .GetValue(nameof(ZB.MOM.WW.ScadaBridge.Security.Auth.AuthDisableLoginOptions.AllowOutsideDevelopment))); builder.Services.AddSecurity(disableLogin); builder.Services.AddCentralUI(); builder.Services.AddInboundAPI();