155 lines
8.6 KiB
C#
155 lines
8.6 KiB
C#
using Microsoft.AspNetCore.Authentication;
|
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using ZB.MOM.WW.Auth.Abstractions.Roles;
|
|
using ZB.MOM.WW.Auth.AspNetCore;
|
|
using ZB.MOM.WW.MxGateway.Server.Configuration;
|
|
using ZB.MOM.WW.MxGateway.Server.Security.Audit;
|
|
|
|
namespace ZB.MOM.WW.MxGateway.Server.Dashboard;
|
|
|
|
/// <summary>
|
|
/// Extension methods for configuring the gateway dashboard services.
|
|
/// </summary>
|
|
public static class DashboardServiceCollectionExtensions
|
|
{
|
|
/// <summary>
|
|
/// Registers all dashboard services, authentication, and Razor components.
|
|
/// </summary>
|
|
/// <param name="services">Service collection to register services.</param>
|
|
/// <param name="configuration">
|
|
/// Application configuration, used to bind the shared LDAP provider's options
|
|
/// from the <c>MxGateway:Ldap</c> section. Also read to select the dashboard
|
|
/// authentication scheme via the <c>MxGateway:Dashboard:DisableLogin</c> dev flag.
|
|
/// </param>
|
|
public static IServiceCollection AddGatewayDashboard(
|
|
this IServiceCollection services,
|
|
IConfiguration configuration)
|
|
{
|
|
// Dashboard logins delegate bind/search to the shared ZB.MOM.WW.Auth.Ldap
|
|
// provider. Its LdapOptions bind straight from MxGateway:Ldap (the gateway's
|
|
// LdapOptions field names match the shared options: Transport / AllowInsecure /
|
|
// SearchBase / ServiceAccount* / *Attribute). AddZbLdapAuth also adds a
|
|
// ValidateOnStart() so an insecure-transport misconfiguration fails fast at boot.
|
|
services.AddZbLdapAuth(configuration, "MxGateway:Ldap");
|
|
|
|
services.AddSingleton<IDashboardSnapshotService, DashboardSnapshotService>();
|
|
services.AddSingleton<IDashboardLiveDataService, DashboardLiveDataService>();
|
|
services.AddSingleton<IDashboardAuthenticator, DashboardAuthenticator>();
|
|
services.AddSingleton<IGroupRoleMapper<string>, DashboardGroupRoleMapper>();
|
|
services.AddSingleton<DashboardApiKeyAuthorization>();
|
|
services.AddSingleton<IDashboardApiKeyManagementService, DashboardApiKeyManagementService>();
|
|
services.AddSingleton<IDashboardSessionAdminService, DashboardSessionAdminService>();
|
|
services.AddSingleton<HubTokenService>();
|
|
services.AddScoped<Hubs.DashboardHubConnectionFactory>();
|
|
services.AddScoped<IDashboardBrowseService, DashboardBrowseService>();
|
|
services.AddSingleton<Hubs.IDashboardEventBroadcaster, Hubs.DashboardEventBroadcaster>();
|
|
services.AddHostedService<Hubs.DashboardSnapshotPublisher>();
|
|
services.AddHostedService<Hubs.AlarmsHubPublisher>();
|
|
services.AddHttpContextAccessor();
|
|
services.AddSingleton<IAuditActorAccessor, HttpAuditActorAccessor>();
|
|
services.AddAntiforgery();
|
|
services.AddCascadingAuthenticationState();
|
|
services.AddRazorComponents()
|
|
.AddInteractiveServerComponents();
|
|
services.AddSignalR();
|
|
|
|
// DEV/TEST ONLY. Read directly from configuration here because authentication scheme
|
|
// registration runs before options binding. Key mirrors DashboardOptions.DisableLogin.
|
|
bool disableLogin = configuration.GetValue<bool>("MxGateway:Dashboard:DisableLogin");
|
|
|
|
AuthenticationBuilder authentication =
|
|
services.AddAuthentication(DashboardAuthenticationDefaults.AuthenticationScheme);
|
|
|
|
if (disableLogin)
|
|
{
|
|
// Register an always-authenticating handler UNDER the cookie scheme name, so the
|
|
// Viewer/Admin/HubClients policies (which all resolve this scheme) authenticate
|
|
// through it as the multi-role dev user — zero policy or page changes.
|
|
authentication.AddScheme<AuthenticationSchemeOptions, DashboardAutoLoginAuthenticationHandler>(
|
|
DashboardAuthenticationDefaults.AuthenticationScheme,
|
|
_ => { });
|
|
|
|
// Loud warning, emitted on first resolution of GatewayOptions (i.e. on the first
|
|
// request/options access, not guaranteed at process start). Dev-only safety notice.
|
|
services.AddOptions<GatewayOptions>().PostConfigure<ILoggerFactory>((gatewayOptions, loggerFactory) =>
|
|
loggerFactory
|
|
.CreateLogger("ZB.MOM.WW.MxGateway.Server.Dashboard.DisableLogin")
|
|
.LogWarning(
|
|
"DASHBOARD LOGIN DISABLED (MxGateway:Dashboard:DisableLogin=true) — every request is "
|
|
+ "authenticated as '{User}' with full permissions ({Roles}). Dev/test only; never "
|
|
+ "enable in production.",
|
|
string.IsNullOrWhiteSpace(gatewayOptions.Dashboard.AutoLoginUser)
|
|
? DashboardAutoLoginAuthenticationHandler.DefaultUser
|
|
: gatewayOptions.Dashboard.AutoLoginUser!.Trim(),
|
|
$"{DashboardRoles.Admin}, {DashboardRoles.Viewer}"));
|
|
}
|
|
else
|
|
{
|
|
authentication.AddCookie(DashboardAuthenticationDefaults.AuthenticationScheme, cookieOptions =>
|
|
{
|
|
// Hardened defaults (HttpOnly, SameSite=Strict, SecurePolicy, SlidingExpiration,
|
|
// ExpireTimeSpan) via the shared ZbCookieDefaults.Apply. requireHttps is set to
|
|
// its default (true / Always) here and overridden per-environment by the
|
|
// PostConfigure below; the 8-hour idle timeout is preserved (not the 30-min default).
|
|
ZbCookieDefaults.Apply(cookieOptions, requireHttps: true, idleTimeout: TimeSpan.FromHours(8));
|
|
// Cookie name, path, and redirect paths are MxGateway-specific — set after Apply
|
|
// so they are never overwritten by the shared helper (Apply intentionally skips name).
|
|
// This is the canonical default; it is overridden per-environment from
|
|
// DashboardOptions.CookieName by the PostConfigure below.
|
|
cookieOptions.Cookie.Name = DashboardAuthenticationDefaults.CookieName;
|
|
cookieOptions.Cookie.Path = "/";
|
|
cookieOptions.LoginPath = "/login";
|
|
cookieOptions.LogoutPath = "/logout";
|
|
cookieOptions.AccessDeniedPath = "/denied";
|
|
});
|
|
}
|
|
|
|
authentication.AddScheme<AuthenticationSchemeOptions, HubTokenAuthenticationHandler>(
|
|
DashboardAuthenticationDefaults.HubAuthenticationScheme,
|
|
_ => { });
|
|
|
|
// Honour DashboardOptions.RequireHttpsCookie (default true / Always; set false for dev
|
|
// HTTP deployments → SameAsRequest) and the optional per-environment cookie-name
|
|
// override. Both run after the inline AddCookie config above, so they win.
|
|
services.AddOptions<CookieAuthenticationOptions>(DashboardAuthenticationDefaults.AuthenticationScheme)
|
|
.Configure<IOptions<GatewayOptions>>((cookieOptions, gatewayOptions) =>
|
|
{
|
|
cookieOptions.Cookie.SecurePolicy = gatewayOptions.Value.Dashboard.RequireHttpsCookie
|
|
? CookieSecurePolicy.Always
|
|
: CookieSecurePolicy.SameAsRequest;
|
|
|
|
// Config-driven cookie name (MxGateway:Dashboard:CookieName). Null/blank keeps
|
|
// the canonical default set above, so a misconfiguration cannot unname the cookie.
|
|
var cookieName = gatewayOptions.Value.Dashboard.CookieName;
|
|
if (!string.IsNullOrWhiteSpace(cookieName))
|
|
{
|
|
cookieOptions.Cookie.Name = cookieName;
|
|
}
|
|
});
|
|
|
|
services.AddAuthorization(authorization =>
|
|
{
|
|
authorization.AddPolicy(
|
|
DashboardAuthenticationDefaults.ViewerPolicy,
|
|
policy => policy.AddRequirements(DashboardAuthorizationRequirement.AnyDashboardRole));
|
|
authorization.AddPolicy(
|
|
DashboardAuthenticationDefaults.AdminPolicy,
|
|
policy => policy.AddRequirements(DashboardAuthorizationRequirement.AdminOnly));
|
|
authorization.AddPolicy(
|
|
DashboardAuthenticationDefaults.HubClientsPolicy,
|
|
policy => policy
|
|
.AddAuthenticationSchemes(
|
|
DashboardAuthenticationDefaults.AuthenticationScheme,
|
|
DashboardAuthenticationDefaults.HubAuthenticationScheme)
|
|
.AddRequirements(DashboardAuthorizationRequirement.AnyDashboardRole));
|
|
});
|
|
services.AddSingleton<IAuthorizationHandler, DashboardAuthorizationHandler>();
|
|
|
|
return services;
|
|
}
|
|
}
|