refactor: rename ScadaLink → ZB.MOM.WW.ScadaBridge (code + projects + namespaces)

Solution + 23 src projects + 26 test projects renamed; folders, csproj,
namespaces, and ScadaLinkDbContext/ScadaBridgeDbContext class updated.
ActorSystem "scadalink" → "scadabridge", Akka seed-node URLs migrated.
SQL roles/logins, LDAP domains, CLI command name, and CLI config dir
(~/.scadalink → ~/.scadabridge) also renamed.

Build green; 5 Host.Tests fail awaiting SQL login rename in next commit.
Pre-existing StaleTagMonitor timing flakes unchanged.

Rename script committed at tools/rename-to-scadabridge.sh.
This commit is contained in:
Joseph Doherty
2026-05-28 09:37:45 -04:00
parent 6d87ee3c3b
commit 7b0b9c7365
1531 changed files with 11180 additions and 11054 deletions
@@ -0,0 +1,101 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace ZB.MOM.WW.ScadaBridge.Security;
public static class ServiceCollectionExtensions
{
/// <summary>
/// Registers LDAP authentication, JWT token service, role mapper, cookie authentication, and authorization policies.
/// </summary>
/// <param name="services">The service collection to register into.</param>
public static IServiceCollection AddSecurity(this IServiceCollection services)
{
services.AddScoped<LdapAuthService>();
services.AddScoped<JwtTokenService>();
services.AddScoped<RoleMapper>();
// Security-020: register the IValidateOptions<SecurityOptions> so a
// missing/empty LdapServer or LdapSearchBase fails fast at startup
// with a clear, key-naming message rather than a generic LDAP error
// on the first real login. ValidateOnStart() forces the validation to
// run during host startup rather than lazily on the first
// IOptions<SecurityOptions> resolve. TryAddEnumerable so multiple
// AddSecurity calls (or future additional validators) don't pile up.
services.AddOptions<SecurityOptions>().ValidateOnStart();
services.TryAddEnumerable(
ServiceDescriptor.Singleton<IValidateOptions<SecurityOptions>, SecurityOptionsValidator>());
// Register ASP.NET Core authentication with cookie scheme
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/login";
options.LogoutPath = "/auth/logout";
options.Cookie.Name = "ZB.MOM.WW.ScadaBridge.Auth";
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
// Cookie.SecurePolicy is set in the PostConfigure block below so it
// can honour SecurityOptions.RequireHttpsCookie.
});
// CentralUI-005: configure the cookie session as a sliding window so the
// code matches the documented policy ("sliding refresh, 30-minute idle
// timeout"). ASP.NET cookie auth exposes a single ExpireTimeSpan plus a
// SlidingExpiration flag — it cannot natively model a 15-minute sliding
// token AND a separate 30-minute absolute idle cap. The faithful
// interpretation: the cookie window IS the idle timeout
// (SecurityOptions.IdleTimeoutMinutes, default 30) and SlidingExpiration
// renews it on activity (the middleware re-issues the cookie once past
// the halfway mark of the window). An active user is therefore kept
// signed in; an idle user is signed out after the idle timeout. The
// 15-minute JwtExpiryMinutes governs the lifetime of the embedded JWT
// itself (see JwtTokenService) — a separate layer from the cookie
// session window. Bound here via PostConfigure so SecurityOptions
// (configured by the Host after AddSecurity) is honoured.
services.AddOptions<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme)
.Configure<IOptions<SecurityOptions>, ILoggerFactory>((cookieOptions, securityOptions, loggerFactory) =>
{
var idleMinutes = securityOptions.Value.IdleTimeoutMinutes;
cookieOptions.ExpireTimeSpan = TimeSpan.FromMinutes(idleMinutes);
cookieOptions.SlidingExpiration = true;
// The cookie carries the embedded JWT bearer credential. Production
// keeps it HTTPS-only (Always); an HTTP-only deployment (e.g. the
// local Docker dev cluster) opts out via RequireHttpsCookie=false and
// uses SameAsRequest — still Secure on any HTTPS request.
cookieOptions.Cookie.SecurePolicy = securityOptions.Value.RequireHttpsCookie
? Microsoft.AspNetCore.Http.CookieSecurePolicy.Always
: Microsoft.AspNetCore.Http.CookieSecurePolicy.SameAsRequest;
// Security-021: when the operator opts out of HTTPS-only cookies,
// log a Warning so an HTTP-only deployment is at least audible in
// the startup log. The cookie carries the embedded JWT bearer
// credential — over plain HTTP that travels in cleartext on every
// request. The default is true; this branch fires only on an
// explicit opt-out (typically the dev Docker cluster).
if (!securityOptions.Value.RequireHttpsCookie)
{
loggerFactory.CreateLogger("ZB.MOM.WW.ScadaBridge.Security").LogWarning(
"SecurityOptions:RequireHttpsCookie is DISABLED — auth cookie SecurePolicy is SameAsRequest. The cookie-embedded JWT will be transmitted in cleartext over plain HTTP. This setting is intended for local dev only — set SecurityOptions:RequireHttpsCookie=true in production.");
}
});
services.AddScadaBridgeAuthorization();
return services;
}
/// <summary>
/// Registers security-related Akka actors (placeholder for future actor registrations).
/// </summary>
/// <param name="services">The service collection to register into.</param>
public static IServiceCollection AddSecurityActors(this IServiceCollection services)
{
// Phase 0: placeholder for Akka actor registration
return services;
}
}