eabf270d71
Resolve all 622 issues flagged by the enhanced CommentChecker: add missing <returns> tags (incl. the standard phrasing on non-generic Task methods), add missing <summary> tags, and replace misused/redundant <inheritdoc/> on members that override or implement nothing with real documentation. Documentation-only — no behavior change; solution builds clean.
155 lines
7.1 KiB
C#
155 lines
7.1 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.Security;
|
|
|
|
/// <summary>
|
|
/// Centralised authorization policy names + the role→permission mapping
|
|
/// that defines them.
|
|
///
|
|
/// <para>
|
|
/// The codebase uses a thin role-claim model: each policy expresses a
|
|
/// permission, satisfied when the principal carries any role claim
|
|
/// (<see cref="JwtTokenService.RoleClaimType"/>) that maps to that
|
|
/// permission. Role names are free strings configured via
|
|
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Entities.Security.LdapGroupMapping"/> rows
|
|
/// (see <see cref="RoleMapper"/>) — there is no permission claim, just a
|
|
/// fan-out from role to allowed policies.
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// Default role → permission mapping (#23 M7-T15 / Bundle G), post Task 1.7
|
|
/// canonicalization + SoD collapse:
|
|
/// <list type="table">
|
|
/// <listheader>
|
|
/// <term>Role</term>
|
|
/// <description>Policies granted</description>
|
|
/// </listheader>
|
|
/// <item>
|
|
/// <term><c>Administrator</c></term>
|
|
/// <description><see cref="RequireAdmin"/>,
|
|
/// <see cref="OperationalAudit"/>, <see cref="AuditExport"/> — admins hold
|
|
/// every permission by convention so an Administrator-only user never loses
|
|
/// access to a new surface.</description>
|
|
/// </item>
|
|
/// <item>
|
|
/// <term><c>Designer</c></term>
|
|
/// <description><see cref="RequireDesign"/></description>
|
|
/// </item>
|
|
/// <item>
|
|
/// <term><c>Deployer</c></term>
|
|
/// <description><see cref="RequireDeployment"/></description>
|
|
/// </item>
|
|
/// <item>
|
|
/// <term><c>Viewer</c></term>
|
|
/// <description><see cref="OperationalAudit"/> only — read access to the
|
|
/// Audit Log + nav, but NOT <see cref="AuditExport"/>. This preserves the
|
|
/// half-SoD that the legacy <c>AuditReadOnly</c> role provided (read-not-
|
|
/// export) after <c>AuditReadOnly</c> was collapsed into
|
|
/// <c>Viewer</c>.</description>
|
|
/// </item>
|
|
/// </list>
|
|
/// <para>
|
|
/// SoD collapse (Task 1.7): the legacy distinct audit roles were removed. The
|
|
/// former <c>Audit</c> role (full audit surface = read + bulk export) was
|
|
/// collapsed into <c>Administrator</c> — a deliberate, accepted privilege
|
|
/// escalation (former audit-only users gain the entire admin surface: create
|
|
/// sites, manage LDAP mappings/API keys, import bundles). The former
|
|
/// <c>AuditReadOnly</c> role (read-only audit) was collapsed into
|
|
/// <c>Viewer</c>, which keeps audit-read but correctly LACKS export. The net
|
|
/// effect on the audit policies: <see cref="OperationalAudit"/> is granted to
|
|
/// {<c>Administrator</c>, <c>Viewer</c>} and <see cref="AuditExport"/> only to
|
|
/// {<c>Administrator</c>}.
|
|
/// </para>
|
|
/// LDAP group → role mapping is configured via the central UI Admin → LDAP
|
|
/// Mappings page (rows in <c>LdapGroupMappings</c>); the same code path
|
|
/// reads them whether the role is one of the built-ins above or any
|
|
/// future addition. Adding a role here means adding the LDAP mapping row in
|
|
/// the deployment; no schema migration is needed.
|
|
/// </para>
|
|
/// </summary>
|
|
public static class AuthorizationPolicies
|
|
{
|
|
public const string RequireAdmin = "RequireAdmin";
|
|
public const string RequireDesign = "RequireDesign";
|
|
public const string RequireDeployment = "RequireDeployment";
|
|
|
|
/// <summary>
|
|
/// Read access to the Audit Log #23 surface (Audit Log page,
|
|
/// Configuration Audit Log page, Audit nav group). Granted to the
|
|
/// <c>Administrator</c> role and the <c>Viewer</c> role (the latter being
|
|
/// the post-Task-1.7 home of the former <c>AuditReadOnly</c> role).
|
|
/// </summary>
|
|
public const string OperationalAudit = "OperationalAudit";
|
|
|
|
/// <summary>
|
|
/// Permission to pull a bulk CSV export of the Audit Log. Separate from
|
|
/// <see cref="OperationalAudit"/> so a <c>Viewer</c> can read the
|
|
/// table without being able to exfiltrate it in bulk. Granted to the
|
|
/// <c>Administrator</c> role only.
|
|
/// </summary>
|
|
public const string AuditExport = "AuditExport";
|
|
|
|
/// <summary>
|
|
/// Roles that satisfy <see cref="OperationalAudit"/>. Held in one place
|
|
/// so the seed/docs and the policy stay in lockstep.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Public so the ManagementService HTTP API (#23 M8) — which gates the
|
|
/// <c>/api/audit/*</c> routes with a manual Basic-Auth + LDAP role check
|
|
/// rather than the ASP.NET authorization-policy pipeline — can reuse the
|
|
/// exact same role set the <see cref="OperationalAudit"/> policy enforces.
|
|
/// Task 1.7: {<c>Administrator</c>, <c>Viewer</c>} (was {Admin, Audit,
|
|
/// AuditReadOnly} — the audit roles collapsed into Administrator/Viewer).
|
|
/// </remarks>
|
|
public static readonly string[] OperationalAuditRoles = { Roles.Administrator, Roles.Viewer };
|
|
|
|
/// <summary>
|
|
/// Roles that satisfy <see cref="AuditExport"/>. A strict subset of
|
|
/// <see cref="OperationalAuditRoles"/> — read access does NOT imply
|
|
/// export permission, so <c>Viewer</c> can read but not export.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Public for the same reason as <see cref="OperationalAuditRoles"/> —
|
|
/// the ManagementService <c>/api/audit/export</c> route checks roles
|
|
/// against this set directly. Task 1.7: {<c>Administrator</c>} (was
|
|
/// {Admin, Audit}).
|
|
/// </remarks>
|
|
public static readonly string[] AuditExportRoles = { Roles.Administrator };
|
|
|
|
/// <summary>
|
|
/// Registers the ScadaBridge authorization policies (Admin, Design, Deployment, OperationalAudit, AuditExport).
|
|
/// </summary>
|
|
/// <param name="services">The service collection to register into.</param>
|
|
/// <returns>The same <paramref name="services"/> instance, for call chaining.</returns>
|
|
public static IServiceCollection AddScadaBridgeAuthorization(this IServiceCollection services)
|
|
{
|
|
services.AddAuthorization(options =>
|
|
{
|
|
options.AddPolicy(RequireAdmin, policy =>
|
|
policy.RequireClaim(JwtTokenService.RoleClaimType, Roles.Administrator));
|
|
|
|
options.AddPolicy(RequireDesign, policy =>
|
|
policy.RequireClaim(JwtTokenService.RoleClaimType, Roles.Designer));
|
|
|
|
options.AddPolicy(RequireDeployment, policy =>
|
|
policy.RequireClaim(JwtTokenService.RoleClaimType, Roles.Deployer));
|
|
|
|
// 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
|
|
// whether *any* role claim's value is in the allowed set, so a
|
|
// user with role=Administrator (and nothing else) satisfies the
|
|
// OperationalAudit policy, and a user with role=Viewer satisfies
|
|
// OperationalAudit but not AuditExport.
|
|
options.AddPolicy(OperationalAudit, policy =>
|
|
policy.RequireClaim(JwtTokenService.RoleClaimType, OperationalAuditRoles));
|
|
|
|
options.AddPolicy(AuditExport, policy =>
|
|
policy.RequireClaim(JwtTokenService.RoleClaimType, AuditExportRoles));
|
|
});
|
|
|
|
return services;
|
|
}
|
|
}
|