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:
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user