feat(auth): MxGateway dashboard adopt ZbClaimTypes + ZbCookieDefaults, keep cookie name (Task 1.5)
- DashboardAuthenticator.CreatePrincipal: emit ZbClaimTypes.Username ("zb:username") with
the login username, ZbClaimTypes.DisplayName ("zb:displayname") with the display name,
ZbClaimTypes.Name (== ClaimTypes.Name) for Identity.Name resolution, ZbClaimTypes.Role
(== ClaimTypes.Role) for IsInRole/[Authorize]. Keep ClaimTypes.NameIdentifier for back-compat
read-sites; keep mxgateway:ldap_group unchanged (MxGateway-specific, no ZbClaimType for groups).
ClaimsIdentity built with nameType=ZbClaimTypes.Name, roleType=ZbClaimTypes.Role.
- DashboardServiceCollectionExtensions.AddGatewayDashboard: route cookie hardening through
ZbCookieDefaults.Apply(requireHttps:true, idleTimeout:8h); set cookie name/path/redirects
after Apply; PostConfigure still overrides SecurePolicy per RequireHttpsCookie setting.
- DashboardAuthenticatorTests: add AuthenticateAsync_Success_EmitsCanonicalZbClaims asserting
zb:username, zb:displayname, ZbClaimTypes.Role per role, Identity.Name, and ldap_group preserved.
This commit is contained in:
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ZB.MOM.WW.Auth.Abstractions.Ldap;
|
||||
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.Dashboard;
|
||||
|
||||
@@ -125,6 +126,59 @@ public sealed class DashboardAuthenticatorTests
|
||||
Assert.Equal(["GwAdmin", "GwReader"], groupClaims);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Task 1.5: the principal emits canonical ZbClaimTypes.Username ("zb:username") with
|
||||
/// the login username, ZbClaimTypes.Role (= ClaimTypes.Role) for each resolved role, and
|
||||
/// ZbClaimTypes.DisplayName ("zb:displayname") with the display name — while keeping
|
||||
/// ClaimTypes.NameIdentifier, ClaimTypes.Name, and mxgateway:ldap_group claims intact.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task AuthenticateAsync_Success_EmitsCanonicalZbClaims()
|
||||
{
|
||||
FakeLdapAuthService ldap = new(LdapAuthResult.Success(
|
||||
username: "jsmith",
|
||||
displayName: "John Smith",
|
||||
groups: ["GwAdmin", "GwReader"]));
|
||||
DashboardAuthenticator authenticator = CreateAuthenticator(ldap, StandardMapping());
|
||||
|
||||
DashboardAuthenticationResult result = await authenticator.AuthenticateAsync(
|
||||
"jsmith",
|
||||
"pw",
|
||||
CancellationToken.None);
|
||||
|
||||
Assert.True(result.Succeeded);
|
||||
ClaimsPrincipal principal = Assert.IsType<ClaimsPrincipal>(result.Principal);
|
||||
|
||||
// ZbClaimTypes.Username ("zb:username") carries the login username.
|
||||
Assert.Equal("jsmith", principal.FindFirstValue(ZbClaimTypes.Username));
|
||||
|
||||
// ZbClaimTypes.DisplayName ("zb:displayname") carries the display name.
|
||||
Assert.Equal("John Smith", principal.FindFirstValue(ZbClaimTypes.DisplayName));
|
||||
|
||||
// ZbClaimTypes.Role (= ClaimTypes.Role) is used for each role claim.
|
||||
IReadOnlyList<string> roleClaims = principal
|
||||
.FindAll(ZbClaimTypes.Role)
|
||||
.Select(c => c.Value)
|
||||
.OrderBy(r => r)
|
||||
.ToList();
|
||||
Assert.Contains(DashboardRoles.Admin, roleClaims);
|
||||
Assert.Contains(DashboardRoles.Viewer, roleClaims);
|
||||
|
||||
// IsInRole still works (identity built with roleType = ZbClaimTypes.Role).
|
||||
Assert.True(principal.IsInRole(DashboardRoles.Admin));
|
||||
Assert.True(principal.IsInRole(DashboardRoles.Viewer));
|
||||
|
||||
// ClaimTypes.Name still resolves as Identity.Name (nameType = ZbClaimTypes.Name = ClaimTypes.Name).
|
||||
Assert.Equal("John Smith", principal.Identity?.Name);
|
||||
|
||||
// mxgateway:ldap_group claims are preserved.
|
||||
IReadOnlyList<string> groups = principal
|
||||
.FindAll(DashboardAuthenticationDefaults.LdapGroupClaimType)
|
||||
.Select(c => c.Value)
|
||||
.ToList();
|
||||
Assert.Equal(["GwAdmin", "GwReader"], groups);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the user authenticates but none of their groups map to a dashboard role,
|
||||
/// the login is denied (the long-standing "no roles matched → denied" rule).
|
||||
|
||||
Reference in New Issue
Block a user