using System.Security.Claims; 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; /// /// Verifies the shared Secrets UI is wired into the dashboard: the /// AddSecretsAuthorization policies are registered so the mounted /// /admin/secrets Blazor page ([Authorize(Policy = "secrets:manage")]) and its /// reveal action resolve, AND that the dashboard's real Administrator principal shape actually /// satisfies them (the claim-type-compatibility risk class that bit HistorianGateway). The /// browser reveal/redirect behaviour is verified separately on the box. /// public sealed class DashboardSecretsMountTests { private const string ManagePolicy = "secrets:manage"; private const string RevealPolicy = "secrets:reveal"; /// /// Verifies that Build registers the shared Secrets authorization policies /// (secrets:manage + secrets:reveal) added additively via /// Configure<AuthorizationOptions>(o => o.AddSecretsAuthorization()). /// /// A task that represents the asynchronous operation. [Fact] public async Task Build_RegistersSecretsAuthorizationPolicies() { await using WebApplication app = GatewayApplication.Build([]); IAuthorizationPolicyProvider policyProvider = app.Services.GetRequiredService(); AuthorizationPolicy? managePolicy = await policyProvider.GetPolicyAsync(ManagePolicy); AuthorizationPolicy? revealPolicy = await policyProvider.GetPolicyAsync(RevealPolicy); Assert.NotNull(managePolicy); Assert.NotNull(revealPolicy); } /// /// Verifies that the dashboard's Administrator principal — constructed exactly the way /// mints it /// (role claim on ZbClaimTypes.Role, carrying Administrator, authenticated) — /// satisfies BOTH secrets policies. This locks claim-type compatibility: if roles were ever /// issued on a divergent claim type, RequireRole("Administrator") would no longer match /// and this test would fail. /// /// A task that represents the asynchronous operation. [Fact] public async Task AdministratorPrincipal_SatisfiesSecretsPolicies() { await using WebApplication app = GatewayApplication.Build([]); IAuthorizationService authorization = app.Services.GetRequiredService(); ClaimsPrincipal administrator = DashboardAutoLoginAuthenticationHandler.CreatePrincipal("multi-role"); AuthorizationResult manage = await authorization.AuthorizeAsync(administrator, resource: null, ManagePolicy); AuthorizationResult reveal = await authorization.AuthorizeAsync(administrator, resource: null, RevealPolicy); Assert.True(manage.Succeeded); Assert.True(reveal.Succeeded); } /// /// Verifies that an anonymous, role-less principal is DENIED both secrets policies, proving the /// policies actually gate on the Administrator role rather than admitting everyone. /// /// A task that represents the asynchronous operation. [Fact] public async Task AnonymousPrincipal_IsDeniedSecretsPolicies() { await using WebApplication app = GatewayApplication.Build([]); IAuthorizationService authorization = app.Services.GetRequiredService(); ClaimsPrincipal anonymous = new(new ClaimsIdentity()); AuthorizationResult manage = await authorization.AuthorizeAsync(anonymous, resource: null, ManagePolicy); AuthorizationResult reveal = await authorization.AuthorizeAsync(anonymous, resource: null, RevealPolicy); Assert.False(manage.Succeeded); Assert.False(reveal.Succeeded); } }