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