feat(management): any-of role gates; restrict ListSecuredWrites to Operator/Verifier/Admin (arch-review UA1)

Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
Joseph Doherty
2026-07-10 05:03:51 -04:00
parent 4fb754f9f6
commit 6c9de9f7ab
8 changed files with 133 additions and 18 deletions
@@ -88,6 +88,24 @@ public static class AuthorizationPolicies
/// </summary>
public const string RequireVerifier = "RequireVerifier";
/// <summary>
/// Read access to the Secured Writes surface (the Secured Writes page +
/// its Pending/history list). The history table exposes process-sensitive
/// tag values, so it is NOT open to any authenticated user — satisfied by
/// any of {<see cref="Roles.Operator"/>, <see cref="Roles.Verifier"/>,
/// <see cref="Roles.Administrator"/>} (arch-review UA1). Aligns with the
/// ManagementActor's <c>ListSecuredWritesCommand</c> any-of gate.
/// </summary>
public const string RequireSecuredWriteAccess = "RequireSecuredWriteAccess";
/// <summary>
/// Roles that satisfy <see cref="RequireSecuredWriteAccess"/>. Held in one
/// place so the policy, the docs, and the ManagementActor gate stay in
/// lockstep: {<c>Operator</c>, <c>Verifier</c>, <c>Administrator</c>}.
/// </summary>
public static readonly string[] SecuredWriteAccessRoles =
{ Roles.Operator, Roles.Verifier, Roles.Administrator };
/// <summary>
/// Read access to the Audit Log surface (Audit Log page,
/// Configuration Audit Log page, Audit nav group). Granted to the
@@ -155,6 +173,12 @@ public static class AuthorizationPolicies
options.AddPolicy(RequireVerifier, policy =>
policy.RequireClaim(JwtTokenService.RoleClaimType, Roles.Verifier));
// Any-of read gate for the Secured Writes surface (arch-review UA1):
// succeeds when the principal holds any of Operator/Verifier/
// Administrator, mirroring the ManagementActor ListSecuredWrites gate.
options.AddPolicy(RequireSecuredWriteAccess, policy =>
policy.RequireClaim(JwtTokenService.RoleClaimType, SecuredWriteAccessRoles));
// Multi-role permission policies — the policy succeeds when the
// principal holds ANY of the mapped roles. RequireClaim with
// multiple allowed values is the right primitive: it checks