fix(management): honor DisableLogin on the Basic-Auth CLI surfaces

DisableLogin only swapped the cookie auth scheme (AutoLoginAuthenticationHandler),
which covers the interactive UI. The CLI authenticates POST /management, the audit
REST endpoints, and the SignalR debug-stream hub with HTTP Basic, and each ran its
own hardcoded Basic->LDAP check that ignored DisableLogin. In a login-disabled (e.g.
no-LDAP) deployment that locked the CLI out: every call returned 401 AUTH_FAILED.

Add ManagementAuthenticator, which centralizes the management/CLI auth flow:
when ScadaBridge:Security:Auth:DisableLogin is true it synthesizes the same dev
principal as AutoLoginAuthenticationHandler (configured user, all roles, system-wide)
and bypasses Basic->LDAP; otherwise the unchanged Basic->LDAP flow runs. Wired into
ManagementEndpoints (delegates), AuditEndpoints (delegates), and DebugStreamHub
(bypass branch). +6 unit tests; ManagementService.Tests green (140).
This commit is contained in:
Joseph Doherty
2026-06-16 17:12:17 -04:00
parent cdf0a199cb
commit d312dfb139
5 changed files with 258 additions and 100 deletions
@@ -75,6 +75,21 @@ public class DebugStreamHub : Hub
return;
}
// Dev/test: when login is disabled, auto-authenticate the debug stream as the configured
// dev user (ALL roles incl. Deployer, system-wide) — mirroring the cookie/management
// bypass so the CLI debug stream is usable in a login-disabled (e.g. no-LDAP) deployment.
// See ManagementAuthenticator.
var bypassUser = ManagementAuthenticator.TryDisableLoginUser(httpContext);
if (bypassUser is not null)
{
Context.Items[RolesKey] = bypassUser.Roles;
Context.Items[PermittedSiteIdsKey] = bypassUser.PermittedSiteIds;
_logger.LogInformation(
"DebugStreamHub connection established for {Username} (login disabled)", bypassUser.Username);
await base.OnConnectedAsync();
return;
}
// Extract Basic Auth credentials
var authHeader = httpContext.Request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase))