e57d864ab2
The dashboard auth cookie name was hardcoded to the constant DashboardAuthenticationDefaults.CookieName (MxGatewayDashboard). Browser cookies are scoped by host+path but NOT by port, so two gateway instances sharing a hostname would clobber each other's dashboard session under the shared name. Add DashboardOptions.CookieName (MxGateway:Dashboard:CookieName); null/blank keeps the canonical default. Applied in the existing dashboard cookie PostConfigure (runs after the inline AddCookie default, so it wins). Behaviour is unchanged when unset. Adds a Tests case for the override.
121 lines
6.5 KiB
C#
121 lines
6.5 KiB
C#
using Microsoft.AspNetCore.Authentication;
|
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.Extensions.Configuration;
|
|
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.
|
|
/// </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();
|
|
|
|
services
|
|
.AddAuthentication(DashboardAuthenticationDefaults.AuthenticationScheme)
|
|
.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";
|
|
})
|
|
.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;
|
|
}
|
|
}
|