using System.Security.Claims; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.DependencyInjection; using ZB.MOM.WW.ScadaBridge.Security; using ZB.MOM.WW.Secrets.Ui; namespace ZB.MOM.WW.ScadaBridge.Host.Tests; /// /// G-6 (Task 7) offline behavioral proof: composing the Secrets.Ui authorization /// (Configure<AuthorizationOptions>(o => o.AddSecretsAuthorization())) on top of /// ScadaBridge's own yields the /// two named policies secrets:manage + secrets:reveal, and — because ScadaBridge /// builds identities with RoleClaimType = ZbClaimTypes.Role and the library's admin role is /// literally "Administrator" — an Administrator principal is authorized for BOTH, /// while a non-admin / anonymous principal is denied BOTH. This is the offline evidence for the /// Task-6 "no app-specific policy remap needed" decision, exercised against the real /// rather than mere registration. /// public class SecretsAuthorizationTests { private const string ManagePolicy = "secrets:manage"; private const string RevealPolicy = "secrets:reveal"; private static IServiceProvider BuildProvider() { var services = new ServiceCollection(); services.AddLogging(); // Mirror the Central composition: ScadaBridge's own authorization first // (registers the core authorization services + ScadaBridge policies via // AddAuthorization), then the additive Secrets.Ui policy configuration. services.AddScadaBridgeAuthorization(); services.Configure(o => o.AddSecretsAuthorization()); return services.BuildServiceProvider(); } private static ClaimsPrincipal PrincipalWithRole(string role) { // Build the identity EXACTLY as ScadaBridge does: role claims under // JwtTokenService.RoleClaimType (== ZbClaimTypes.Role == ClaimTypes.Role) and // roleType set to the same, so IsInRole (used by the library's RequireRole) resolves. var identity = new ClaimsIdentity( new[] { new Claim(JwtTokenService.RoleClaimType, role) }, authenticationType: "Test", nameType: JwtTokenService.UsernameClaimType, roleType: JwtTokenService.RoleClaimType); return new ClaimsPrincipal(identity); } [Fact] public async Task BothSecretsPolicies_AreRegistered() { var provider = BuildProvider(); var policyProvider = provider.GetRequiredService(); Assert.NotNull(await policyProvider.GetPolicyAsync(ManagePolicy)); Assert.NotNull(await policyProvider.GetPolicyAsync(RevealPolicy)); } [Fact] public async Task Administrator_IsAuthorized_ForBothManageAndReveal() { var provider = BuildProvider(); var authService = provider.GetRequiredService(); var admin = PrincipalWithRole(Roles.Administrator); Assert.True((await authService.AuthorizeAsync(admin, ManagePolicy)).Succeeded); Assert.True((await authService.AuthorizeAsync(admin, RevealPolicy)).Succeeded); } [Fact] public async Task NonAdministratorRole_IsDenied_ForBothManageAndReveal() { var provider = BuildProvider(); var authService = provider.GetRequiredService(); var viewer = PrincipalWithRole(Roles.Viewer); Assert.False((await authService.AuthorizeAsync(viewer, ManagePolicy)).Succeeded); Assert.False((await authService.AuthorizeAsync(viewer, RevealPolicy)).Succeeded); } [Fact] public async Task AnonymousPrincipal_IsDenied_ForBothManageAndReveal() { var provider = BuildProvider(); var authService = provider.GetRequiredService(); var anonymous = new ClaimsPrincipal(new ClaimsIdentity()); Assert.False((await authService.AuthorizeAsync(anonymous, ManagePolicy)).Succeeded); Assert.False((await authService.AuthorizeAsync(anonymous, RevealPolicy)).Succeeded); } }