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;
///
/// Authentication handler used ONLY when MxGateway:Dashboard:DisableLogin is true.
/// Registered under the dashboard cookie scheme name
/// (), 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
/// () produces, so policies and the UI cannot tell it
/// apart. DEV/TEST ONLY; never enable in production.
///
public sealed class DashboardAutoLoginAuthenticationHandler
: AuthenticationHandler, IAuthenticationSignInHandler
{
/// Username used when AutoLoginUser is null or blank.
public const string DefaultUser = "multi-role";
private readonly string _user;
/// Initializes the handler with scheme plumbing and the dashboard options.
/// The per-scheme authentication options monitor.
/// The logger factory the base handler uses.
/// The URL encoder the base handler uses.
/// Gateway options carrying the dashboard auto-login user.
public DashboardAutoLoginAuthenticationHandler(
IOptionsMonitor options,
ILoggerFactory logger,
UrlEncoder encoder,
IOptions gatewayOptions)
: base(options, logger, encoder)
{
string? configured = gatewayOptions.Value.Dashboard.AutoLoginUser;
_user = string.IsNullOrWhiteSpace(configured) ? DefaultUser : configured.Trim();
}
/// No-op: auto-login writes no cookie, so a sign-in has nothing to persist.
/// Ignored.
/// Ignored.
/// A completed task.
public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties? properties) => Task.CompletedTask;
/// No-op: there is no auth cookie to clear; the next request re-authenticates.
/// Ignored.
/// A completed task.
public Task SignOutAsync(AuthenticationProperties? properties) => Task.CompletedTask;
///
protected override Task HandleAuthenticateAsync()
{
ClaimsPrincipal principal = CreatePrincipal(_user);
AuthenticationTicket ticket = new(principal, Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
///
/// Builds the multi-role dev principal. Null/blank falls back to
/// . The authorization-relevant claim shape mirrors
/// ; LDAP group claims (LdapGroupClaimType) are
/// intentionally omitted because auto-login has no real LDAP context.
///
/// The configured auto-login username (may be null/blank).
/// An authenticated principal holding both dashboard roles.
internal static ClaimsPrincipal CreatePrincipal(string? user)
{
string name = string.IsNullOrWhiteSpace(user) ? DefaultUser : user.Trim();
// LdapGroupClaimType claims are omitted — no LDAP groups exist in the auto-login context.
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);
}
}