refactor: rename ScadaLink → ZB.MOM.WW.ScadaBridge (code + projects + namespaces)
Solution + 23 src projects + 26 test projects renamed; folders, csproj, namespaces, and ScadaLinkDbContext/ScadaBridgeDbContext class updated. ActorSystem "scadalink" → "scadabridge", Akka seed-node URLs migrated. SQL roles/logins, LDAP domains, CLI command name, and CLI config dir (~/.scadalink → ~/.scadabridge) also renamed. Build green; 5 Host.Tests fail awaiting SQL login rename in next commit. Pre-existing StaleTagMonitor timing flakes unchanged. Rename script committed at tools/rename-to-scadabridge.sh.
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
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):
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Role</term>
|
||||
/// <description>Policies granted</description>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <term><c>Admin</c></term>
|
||||
/// <description><see cref="RequireAdmin"/>,
|
||||
/// <see cref="OperationalAudit"/>, <see cref="AuditExport"/> — admins hold
|
||||
/// every permission by convention so an Admin-only user never loses
|
||||
/// access to a new surface.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><c>Design</c></term>
|
||||
/// <description><see cref="RequireDesign"/></description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><c>Deployment</c></term>
|
||||
/// <description><see cref="RequireDeployment"/></description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><c>Audit</c></term>
|
||||
/// <description><see cref="OperationalAudit"/>,
|
||||
/// <see cref="AuditExport"/> — the full audit surface (read + bulk
|
||||
/// export) per <c>Component-AuditLog.md</c> §"Authorization".</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><c>AuditReadOnly</c></term>
|
||||
/// <description><see cref="OperationalAudit"/> only — operators who
|
||||
/// should see the Audit Log + drill in to incidents but not pull bulk
|
||||
/// CSV exports. Use this when delegating triage without granting
|
||||
/// forensic-export capability.</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// 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 four 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>Audit</c> role, the <c>AuditReadOnly</c> role, and the
|
||||
/// <c>Admin</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 triage operator can read the
|
||||
/// table without being able to exfiltrate it in bulk. Granted to the
|
||||
/// <c>Audit</c> role and the <c>Admin</c> role.
|
||||
/// </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.
|
||||
/// </remarks>
|
||||
public static readonly string[] OperationalAuditRoles = { Roles.Admin, Roles.Audit, Roles.AuditReadOnly };
|
||||
|
||||
/// <summary>
|
||||
/// Roles that satisfy <see cref="AuditExport"/>. A strict subset of
|
||||
/// <see cref="OperationalAuditRoles"/> — read access does NOT imply
|
||||
/// export permission.
|
||||
/// </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.
|
||||
/// </remarks>
|
||||
public static readonly string[] AuditExportRoles = { Roles.Admin, Roles.Audit };
|
||||
|
||||
/// <summary>
|
||||
/// Registers the ScadaBridge authorization policies (Admin, Design, Deployment, OperationalAudit, AuditExport).
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection to register into.</param>
|
||||
public static IServiceCollection AddScadaBridgeAuthorization(this IServiceCollection services)
|
||||
{
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy(RequireAdmin, policy =>
|
||||
policy.RequireClaim(JwtTokenService.RoleClaimType, Roles.Admin));
|
||||
|
||||
options.AddPolicy(RequireDesign, policy =>
|
||||
policy.RequireClaim(JwtTokenService.RoleClaimType, Roles.Design));
|
||||
|
||||
options.AddPolicy(RequireDeployment, policy =>
|
||||
policy.RequireClaim(JwtTokenService.RoleClaimType, Roles.Deployment));
|
||||
|
||||
// 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=Admin (and nothing else) satisfies the
|
||||
// OperationalAudit policy without needing a separate Audit
|
||||
// role claim.
|
||||
options.AddPolicy(OperationalAudit, policy =>
|
||||
policy.RequireClaim(JwtTokenService.RoleClaimType, OperationalAuditRoles));
|
||||
|
||||
options.AddPolicy(AuditExport, policy =>
|
||||
policy.RequireClaim(JwtTokenService.RoleClaimType, AuditExportRoles));
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user