using System.Security.Claims; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Security.Auth; namespace ZB.MOM.WW.OtOpcUa.Security.Tests; /// /// Pins the semantics of the four AdminUI authorization policies registered by /// AddOtOpcUaAuth. Mirrors provider /// construction (in-memory config, real registration, resolve ). /// These are the CI-side negative-authz proof: the docker-dev rig auto-authenticates with /// all roles (DisableLogin=true) so a role-denied path can never be observed live. /// public class AdminUiPoliciesTests { private static IAuthorizationService BuildAuthorizationService() { var config = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary { ["Security:Auth:DisableLogin"] = "false", }).Build(); var services = new ServiceCollection(); services.AddLogging(); services.AddOtOpcUaAuth(config); var sp = services.BuildServiceProvider(); return sp.GetRequiredService(); } /// An authenticated principal carrying the given role claims. private static ClaimsPrincipal AuthenticatedWith(params string[] roles) => new(new ClaimsIdentity( roles.Select(r => new Claim(ClaimTypes.Role, r)), authenticationType: "Test")); /// An unauthenticated principal (no authenticationType ⇒ IsAuthenticated=false). private static ClaimsPrincipal Unauthenticated() => new(new ClaimsIdentity()); private static async Task AllowedAsync(ClaimsPrincipal user, string policy) => (await BuildAuthorizationService().AuthorizeAsync(user, resource: null, policy)).Succeeded; // ── ConfigEditor: Administrator or Designer ──────────────────────────────────── [Fact] public async Task ConfigEditor_denies_Viewer() => (await AllowedAsync(AuthenticatedWith("Viewer"), AdminUiPolicies.ConfigEditor)).ShouldBeFalse(); [Fact] public async Task ConfigEditor_denies_Operator() => (await AllowedAsync(AuthenticatedWith(DevAuthRoles.Operator), AdminUiPolicies.ConfigEditor)).ShouldBeFalse(); [Fact] public async Task ConfigEditor_allows_Designer() => (await AllowedAsync(AuthenticatedWith("Designer"), AdminUiPolicies.ConfigEditor)).ShouldBeTrue(); [Fact] public async Task ConfigEditor_allows_Administrator() => (await AllowedAsync(AuthenticatedWith("Administrator"), AdminUiPolicies.ConfigEditor)).ShouldBeTrue(); [Fact] public async Task ConfigEditor_denies_unauthenticated() => (await AllowedAsync(Unauthenticated(), AdminUiPolicies.ConfigEditor)).ShouldBeFalse(); // ── AuthenticatedRead: any authenticated user ────────────────────────────────── [Fact] public async Task AuthenticatedRead_allows_roleless_authenticated() => (await AllowedAsync(AuthenticatedWith(), AdminUiPolicies.AuthenticatedRead)).ShouldBeTrue(); [Fact] public async Task AuthenticatedRead_allows_Viewer() => (await AllowedAsync(AuthenticatedWith("Viewer"), AdminUiPolicies.AuthenticatedRead)).ShouldBeTrue(); [Fact] public async Task AuthenticatedRead_denies_unauthenticated() => (await AllowedAsync(Unauthenticated(), AdminUiPolicies.AuthenticatedRead)).ShouldBeFalse(); // ── FleetAdmin: Administrator only (pre-existing policy, pinned here) ─────────── [Fact] public async Task FleetAdmin_allows_Administrator() => (await AllowedAsync(AuthenticatedWith("Administrator"), AdminUiPolicies.FleetAdmin)).ShouldBeTrue(); [Fact] public async Task FleetAdmin_denies_Designer() => (await AllowedAsync(AuthenticatedWith("Designer"), AdminUiPolicies.FleetAdmin)).ShouldBeFalse(); [Fact] public async Task FleetAdmin_denies_Operator() => (await AllowedAsync(AuthenticatedWith(DevAuthRoles.Operator), AdminUiPolicies.FleetAdmin)).ShouldBeFalse(); // ── DriverOperator: Operator + Administrator only (pre-existing policy, pinned) ─ [Fact] public async Task DriverOperator_allows_Operator() => (await AllowedAsync(AuthenticatedWith(DevAuthRoles.Operator), AdminUiPolicies.DriverOperator)).ShouldBeTrue(); [Fact] public async Task DriverOperator_allows_Administrator() => (await AllowedAsync(AuthenticatedWith("Administrator"), AdminUiPolicies.DriverOperator)).ShouldBeTrue(); [Fact] public async Task DriverOperator_denies_Designer() => (await AllowedAsync(AuthenticatedWith("Designer"), AdminUiPolicies.DriverOperator)).ShouldBeFalse(); [Fact] public async Task DriverOperator_denies_Viewer() => (await AllowedAsync(AuthenticatedWith("Viewer"), AdminUiPolicies.DriverOperator)).ShouldBeFalse(); // ── Dev-rig protection: DevAuthRoles.All satisfies every policy ──────────────── [Theory] [InlineData(AdminUiPolicies.FleetAdmin)] [InlineData(AdminUiPolicies.DriverOperator)] [InlineData(AdminUiPolicies.ConfigEditor)] [InlineData(AdminUiPolicies.AuthenticatedRead)] public async Task DevAuthRoles_All_satisfies_every_policy(string policy) => (await AllowedAsync(AuthenticatedWith(DevAuthRoles.All), policy)).ShouldBeTrue(); }