feat(dashboard): swap to auto-login handler when DisableLogin is set
This commit is contained in:
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Authentication;
|
|||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using ZB.MOM.WW.Auth.Abstractions.Roles;
|
using ZB.MOM.WW.Auth.Abstractions.Roles;
|
||||||
using ZB.MOM.WW.Auth.AspNetCore;
|
using ZB.MOM.WW.Auth.AspNetCore;
|
||||||
@@ -55,9 +56,36 @@ public static class DashboardServiceCollectionExtensions
|
|||||||
.AddInteractiveServerComponents();
|
.AddInteractiveServerComponents();
|
||||||
services.AddSignalR();
|
services.AddSignalR();
|
||||||
|
|
||||||
services
|
// DEV/TEST ONLY. Read directly from configuration here because authentication scheme
|
||||||
.AddAuthentication(DashboardAuthenticationDefaults.AuthenticationScheme)
|
// registration runs before options binding. Key mirrors DashboardOptions.DisableLogin.
|
||||||
.AddCookie(DashboardAuthenticationDefaults.AuthenticationScheme, cookieOptions =>
|
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, once-at-startup warning (emitted when GatewayOptions is first resolved).
|
||||||
|
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.",
|
||||||
|
gatewayOptions.Dashboard.AutoLoginUser ?? DashboardAutoLoginAuthenticationHandler.DefaultUser,
|
||||||
|
$"{DashboardRoles.Admin}, {DashboardRoles.Viewer}"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
authentication.AddCookie(DashboardAuthenticationDefaults.AuthenticationScheme, cookieOptions =>
|
||||||
{
|
{
|
||||||
// Hardened defaults (HttpOnly, SameSite=Strict, SecurePolicy, SlidingExpiration,
|
// Hardened defaults (HttpOnly, SameSite=Strict, SecurePolicy, SlidingExpiration,
|
||||||
// ExpireTimeSpan) via the shared ZbCookieDefaults.Apply. requireHttps is set to
|
// ExpireTimeSpan) via the shared ZbCookieDefaults.Apply. requireHttps is set to
|
||||||
@@ -73,10 +101,12 @@ public static class DashboardServiceCollectionExtensions
|
|||||||
cookieOptions.LoginPath = "/login";
|
cookieOptions.LoginPath = "/login";
|
||||||
cookieOptions.LogoutPath = "/logout";
|
cookieOptions.LogoutPath = "/logout";
|
||||||
cookieOptions.AccessDeniedPath = "/denied";
|
cookieOptions.AccessDeniedPath = "/denied";
|
||||||
})
|
});
|
||||||
.AddScheme<AuthenticationSchemeOptions, HubTokenAuthenticationHandler>(
|
}
|
||||||
DashboardAuthenticationDefaults.HubAuthenticationScheme,
|
|
||||||
_ => { });
|
authentication.AddScheme<AuthenticationSchemeOptions, HubTokenAuthenticationHandler>(
|
||||||
|
DashboardAuthenticationDefaults.HubAuthenticationScheme,
|
||||||
|
_ => { });
|
||||||
|
|
||||||
// Honour DashboardOptions.RequireHttpsCookie (default true / Always; set false for dev
|
// Honour DashboardOptions.RequireHttpsCookie (default true / Always; set false for dev
|
||||||
// HTTP deployments → SameAsRequest) and the optional per-environment cookie-name
|
// HTTP deployments → SameAsRequest) and the optional per-environment cookie-name
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
using System.Security.Claims;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using ZB.MOM.WW.MxGateway.Server;
|
||||||
|
using ZB.MOM.WW.MxGateway.Server.Dashboard;
|
||||||
|
|
||||||
|
namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard;
|
||||||
|
|
||||||
|
public sealed class DashboardDisableLoginTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task DisableLoginOff_CookieSchemeUsesCookieHandler()
|
||||||
|
{
|
||||||
|
await using WebApplication app = GatewayApplication.Build([]);
|
||||||
|
IAuthenticationSchemeProvider provider =
|
||||||
|
app.Services.GetRequiredService<IAuthenticationSchemeProvider>();
|
||||||
|
|
||||||
|
AuthenticationScheme? scheme = await provider.GetSchemeAsync(
|
||||||
|
DashboardAuthenticationDefaults.AuthenticationScheme);
|
||||||
|
|
||||||
|
Assert.NotNull(scheme);
|
||||||
|
Assert.Equal(typeof(CookieAuthenticationHandler), scheme!.HandlerType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DisableLoginOn_CookieSchemeUsesAutoLoginHandler()
|
||||||
|
{
|
||||||
|
await using WebApplication app = GatewayApplication.Build(
|
||||||
|
["--MxGateway:Dashboard:DisableLogin=true"]);
|
||||||
|
IAuthenticationSchemeProvider provider =
|
||||||
|
app.Services.GetRequiredService<IAuthenticationSchemeProvider>();
|
||||||
|
|
||||||
|
AuthenticationScheme? scheme = await provider.GetSchemeAsync(
|
||||||
|
DashboardAuthenticationDefaults.AuthenticationScheme);
|
||||||
|
|
||||||
|
Assert.NotNull(scheme);
|
||||||
|
Assert.Equal(typeof(DashboardAutoLoginAuthenticationHandler), scheme!.HandlerType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DisableLoginOn_AutoLoginPrincipalSatisfiesAdminAndViewerPolicies()
|
||||||
|
{
|
||||||
|
await using WebApplication app = GatewayApplication.Build(
|
||||||
|
["--MxGateway:Dashboard:DisableLogin=true"]);
|
||||||
|
IAuthorizationService authorization =
|
||||||
|
app.Services.GetRequiredService<IAuthorizationService>();
|
||||||
|
ClaimsPrincipal user = DashboardAutoLoginAuthenticationHandler.CreatePrincipal("multi-role");
|
||||||
|
|
||||||
|
Assert.True((await authorization.AuthorizeAsync(
|
||||||
|
user, resource: null, DashboardAuthenticationDefaults.AdminPolicy)).Succeeded);
|
||||||
|
Assert.True((await authorization.AuthorizeAsync(
|
||||||
|
user, resource: null, DashboardAuthenticationDefaults.ViewerPolicy)).Succeeded);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user