feat(dashboard): add auto-login auth handler for DisableLogin mode
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ZB.MOM.WW.Auth.AspNetCore;
|
||||
using ZB.MOM.WW.MxGateway.Server.Configuration;
|
||||
|
||||
namespace ZB.MOM.WW.MxGateway.Server.Dashboard;
|
||||
|
||||
/// <summary>
|
||||
/// Authentication handler used ONLY when <c>MxGateway:Dashboard:DisableLogin</c> is true.
|
||||
/// Registered under the dashboard cookie scheme name
|
||||
/// (<see cref="DashboardAuthenticationDefaults.AuthenticationScheme"/>), it authenticates
|
||||
/// EVERY request as the configured dev user with both dashboard roles — no credential check,
|
||||
/// no cookie, no LDAP bind. The minted principal mirrors the shape the real login
|
||||
/// (<see cref="DashboardAuthenticator"/>) produces, so policies and the UI cannot tell it
|
||||
/// apart. DEV/TEST ONLY; never enable in production.
|
||||
/// </summary>
|
||||
public sealed class DashboardAutoLoginAuthenticationHandler
|
||||
: AuthenticationHandler<AuthenticationSchemeOptions>, IAuthenticationSignInHandler
|
||||
{
|
||||
/// <summary>Username used when <c>AutoLoginUser</c> is null or blank.</summary>
|
||||
public const string DefaultUser = "multi-role";
|
||||
|
||||
private readonly string _user;
|
||||
|
||||
/// <summary>Initializes the handler with scheme plumbing and the dashboard options.</summary>
|
||||
/// <param name="options">The per-scheme authentication options monitor.</param>
|
||||
/// <param name="logger">The logger factory the base handler uses.</param>
|
||||
/// <param name="encoder">The URL encoder the base handler uses.</param>
|
||||
/// <param name="gatewayOptions">Gateway options carrying the dashboard auto-login user.</param>
|
||||
public DashboardAutoLoginAuthenticationHandler(
|
||||
IOptionsMonitor<AuthenticationSchemeOptions> options,
|
||||
ILoggerFactory logger,
|
||||
UrlEncoder encoder,
|
||||
IOptions<GatewayOptions> gatewayOptions)
|
||||
: base(options, logger, encoder)
|
||||
=> _user = gatewayOptions.Value.Dashboard.AutoLoginUser ?? DefaultUser;
|
||||
|
||||
/// <summary>No-op: auto-login writes no cookie, so a sign-in has nothing to persist.</summary>
|
||||
/// <param name="user">Ignored.</param>
|
||||
/// <param name="properties">Ignored.</param>
|
||||
/// <returns>A completed task.</returns>
|
||||
public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties? properties) => Task.CompletedTask;
|
||||
|
||||
/// <summary>No-op: there is no auth cookie to clear; the next request re-authenticates.</summary>
|
||||
/// <param name="properties">Ignored.</param>
|
||||
/// <returns>A completed task.</returns>
|
||||
public Task SignOutAsync(AuthenticationProperties? properties) => Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
ClaimsPrincipal principal = CreatePrincipal(_user);
|
||||
AuthenticationTicket ticket = new(principal, Scheme.Name);
|
||||
|
||||
return Task.FromResult(AuthenticateResult.Success(ticket));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the multi-role dev principal. Null/blank <paramref name="user"/> falls back to
|
||||
/// <see cref="DefaultUser"/>. Claim shape mirrors <see cref="DashboardAuthenticator"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The configured auto-login username (may be null/blank).</param>
|
||||
/// <returns>An authenticated principal holding both dashboard roles.</returns>
|
||||
internal static ClaimsPrincipal CreatePrincipal(string? user)
|
||||
{
|
||||
string name = string.IsNullOrWhiteSpace(user) ? DefaultUser : user.Trim();
|
||||
|
||||
Claim[] claims =
|
||||
[
|
||||
new Claim(ClaimTypes.NameIdentifier, name),
|
||||
new Claim(ZbClaimTypes.Username, name),
|
||||
new Claim(ZbClaimTypes.Name, name),
|
||||
new Claim(ZbClaimTypes.DisplayName, name),
|
||||
new Claim(ZbClaimTypes.Role, DashboardRoles.Admin),
|
||||
new Claim(ZbClaimTypes.Role, DashboardRoles.Viewer),
|
||||
];
|
||||
|
||||
ClaimsIdentity identity = new(
|
||||
claims,
|
||||
DashboardAuthenticationDefaults.AuthenticationScheme,
|
||||
ZbClaimTypes.Name,
|
||||
ZbClaimTypes.Role);
|
||||
|
||||
return new ClaimsPrincipal(identity);
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
using System.Security.Claims;
|
||||
using ZB.MOM.WW.MxGateway.Server.Dashboard;
|
||||
|
||||
namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard;
|
||||
|
||||
public sealed class DashboardAutoLoginAuthenticationHandlerTests
|
||||
{
|
||||
[Fact]
|
||||
public void CreatePrincipal_MintsAuthenticatedMultiRoleUser()
|
||||
{
|
||||
ClaimsPrincipal principal = DashboardAutoLoginAuthenticationHandler.CreatePrincipal("multi-role");
|
||||
|
||||
Assert.True(principal.Identity!.IsAuthenticated);
|
||||
Assert.Equal("multi-role", principal.Identity!.Name);
|
||||
Assert.True(principal.IsInRole(DashboardRoles.Admin));
|
||||
Assert.True(principal.IsInRole(DashboardRoles.Viewer));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
public void CreatePrincipal_BlankUser_FallsBackToDefault(string? user)
|
||||
{
|
||||
ClaimsPrincipal principal = DashboardAutoLoginAuthenticationHandler.CreatePrincipal(user);
|
||||
|
||||
Assert.Equal(DashboardAutoLoginAuthenticationHandler.DefaultUser, principal.Identity!.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreatePrincipal_TrimsUser()
|
||||
{
|
||||
ClaimsPrincipal principal = DashboardAutoLoginAuthenticationHandler.CreatePrincipal(" multi-role ");
|
||||
|
||||
Assert.Equal("multi-role", principal.Identity!.Name);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user