docs(comments): strip internal task/milestone/bundle bookkeeping from code comments

Remove project bookkeeping citations from shipped code comments across the
solution: hyphenated task IDs (WP-14, StoreAndForward-025), milestone/task/
issue refs (M3, Task 4, Audit Log #23, #21), Bundle X task-bundle labels,
and C/D/K/S/T phase labels.

Comment text only — no code logic, string/log literals, or XML-doc structure
changed. Genuine descriptions are preserved (only the citation is stripped),
and technical lookalikes are retained (UTF-8, SHA-256, T00:00:00, M365,
UTC-5, pre-C4/pre-C5 schema versions). Flagged by the new CommentChecker
TaskReferenceInComment / TrackingReferenceInComment checks plus targeted
grep passes; full solution builds clean, append-only guard tests pass.
This commit is contained in:
Joseph Doherty
2026-07-07 11:03:26 -04:00
parent 67005ca4c0
commit 9cff87fe85
435 changed files with 2338 additions and 2547 deletions
@@ -18,8 +18,7 @@ namespace ZB.MOM.WW.ScadaBridge.Security;
/// </para>
///
/// <para>
/// Default role → permission mapping (#23 M7-T15 / Bundle G), post Task 1.7
/// canonicalization + SoD collapse:
/// Default role → permission mapping, post canonicalization + SoD collapse:
/// <list type="table">
/// <listheader>
/// <term>Role</term>
@@ -50,7 +49,7 @@ namespace ZB.MOM.WW.ScadaBridge.Security;
/// </item>
/// </list>
/// <para>
/// SoD collapse (Task 1.7): the legacy distinct audit roles were removed. The
/// SoD collapse: 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
@@ -75,14 +74,14 @@ public static class AuthorizationPolicies
public const string RequireDeployment = "RequireDeployment";
/// <summary>
/// Permission to initiate a two-person Secured Write (M7-A3 / T14a).
/// Permission to initiate a two-person Secured Write.
/// Satisfied by the <see cref="Roles.Operator"/> role claim. Single-role
/// policy mirroring <see cref="RequireAdmin"/>.
/// </summary>
public const string RequireOperator = "RequireOperator";
/// <summary>
/// Permission to approve a two-person Secured Write (M7-A3 / T14a).
/// Permission to approve a two-person Secured Write.
/// Satisfied by the <see cref="Roles.Verifier"/> role claim. Kept distinct
/// from <see cref="RequireOperator"/> so initiation and approval are
/// separable duties.
@@ -90,10 +89,10 @@ public static class AuthorizationPolicies
public const string RequireVerifier = "RequireVerifier";
/// <summary>
/// Read access to the Audit Log #23 surface (Audit Log page,
/// Read access to the Audit Log 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).
/// the home of the former <c>AuditReadOnly</c> role).
/// </summary>
public const string OperationalAudit = "OperationalAudit";
@@ -110,11 +109,11 @@ public static class AuthorizationPolicies
/// so the seed/docs and the policy stay in lockstep.
/// </summary>
/// <remarks>
/// Public so the ManagementService HTTP API (#23 M8) — which gates the
/// Public so the ManagementService HTTP API — 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,
/// {<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 };
@@ -127,7 +126,7 @@ public static class AuthorizationPolicies
/// <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
/// against this set directly. {<c>Administrator</c>} (was
/// {Admin, Audit}).
/// </remarks>
public static readonly string[] AuditExportRoles = { Roles.Administrator };
@@ -45,7 +45,7 @@ public enum SessionValidationAction
}
/// <summary>
/// M2.19 (#15): the unit-testable core of the cookie <c>OnValidatePrincipal</c> event.
/// The unit-testable core of the cookie <c>OnValidatePrincipal</c> event.
/// Enforces the idle timeout and refreshes the session's role/scope claims from the
/// STORED LDAP group claims via the DB-backed <see cref="RoleMapper"/> — <b>without any
/// LDAP call</b> — picking up central role-mapping (and scope-rule) changes mid-session.
@@ -13,7 +13,7 @@ public class JwtTokenService
private readonly SecurityOptions _options;
private readonly ILogger<JwtTokenService> _logger;
// Task 1.5 (full canonical claims): these constants are the single source of
// (Full canonical claims): these constants are the single source of
// truth that every mint site, every authorization policy, and the token
// validation parameters reference. Redefining them as aliases of the shared
// ZbClaimTypes vocabulary migrates the whole module centrally:
@@ -29,7 +29,7 @@ public class JwtTokenService
public const string SiteIdClaimType = ZbClaimTypes.ScopeId;
public const string LastActivityClaimType = "LastActivity";
// M2.19 (#15): the cookie session now stores the user's raw LDAP groups and a
// The cookie session now stores the user's raw LDAP groups and a
// role-mapping refresh anchor so an active interactive session can re-run the
// DB-backed RoleMapper (NOT LDAP) mid-session and pick up central role-mapping
// changes. These two have no canonical ZbClaimTypes equivalent (the shared
@@ -229,7 +229,7 @@ public class JwtTokenService
/// claims). Enforcing the idle check here — rather than relying on the caller to
/// invoke <see cref="IsIdleTimedOut"/> first — guarantees the documented 30-minute
/// idle policy holds regardless of caller discipline; otherwise an idle-expired
/// session could be kept alive indefinitely by background refreshes (Security-014).
/// session could be kept alive indefinitely by background refreshes.
/// </para>
/// </summary>
/// <param name="currentPrincipal">The current authenticated principal whose claims carry identity and the last-activity anchor.</param>
@@ -6,7 +6,7 @@ namespace ZB.MOM.WW.ScadaBridge.Security;
/// Translates the shared <see cref="LdapAuthFailure"/> enum returned by
/// <c>ZB.MOM.WW.Auth.Ldap</c>'s <see cref="ILdapAuthService"/> into the
/// user-facing error strings ScadaBridge surfaced from its bespoke
/// <c>LdapAuthService</c> before the Task 1.2 cutover.
/// <c>LdapAuthService</c> before the cutover.
/// </summary>
/// <remarks>
/// <para>
@@ -31,14 +31,14 @@ namespace ZB.MOM.WW.ScadaBridge.Security;
/// library rejects it outright. Surfaced as the misconfiguration message so
/// the operator — not the user — is pointed at the cause.</item>
/// <item><see cref="LdapAuthFailure.ServiceAccountBindFailed"/> keeps the
/// donor's distinct "service is misconfigured" wording (Security-019) so a
/// donor's distinct "service is misconfigured" wording so a
/// system-side fault is not blamed on user input. NOTE: the library also maps
/// connect/search infrastructure failures (directory unreachable) into this
/// bucket, so this message now covers "directory unavailable at connect/search
/// time" as well — see <see cref="LdapAuthFailure.GroupLookupFailed"/> for the
/// post-bind directory-outage case.</item>
/// <item><see cref="LdapAuthFailure.GroupLookupFailed"/> keeps the donor's
/// "directory is temporarily unavailable" wording (Security-012): a post-bind
/// "directory is temporarily unavailable" wording: a post-bind
/// group-lookup failure means the directory is partially unavailable and the
/// login is failed closed rather than admitting a roleless session. NOTE: the
/// library additionally treats a successful-but-empty group set as
@@ -13,7 +13,7 @@ public class RoleMapper
_securityRepository = securityRepository ?? throw new ArgumentNullException(nameof(securityRepository));
}
// virtual: a test seam so HTTP-pipeline tests (e.g. the #23 M8 audit
// virtual: a test seam so HTTP-pipeline tests (e.g. the audit
// endpoints) can substitute the LDAP-group→role resolution.
/// <summary>Maps a list of LDAP group names to ScadaBridge roles and computes site-scope permissions.</summary>
/// <param name="ldapGroups">LDAP group names from the authenticated user's directory entry.</param>
@@ -59,7 +59,7 @@ public class RoleMapper
}
}
// Union semantics (Security-016): a Deployment user is system-wide iff
// Union semantics: a Deployment user is system-wide iff
// *any* matched Deployment mapping has no scope rules. A user in both
// SCADA-Deploy-All (unscoped) and SCADA-Deploy-SiteA (scoped to Site A)
// gets the broader grant, not the narrower one — matching the design's
+6 -6
View File
@@ -11,10 +11,10 @@ namespace ZB.MOM.WW.ScadaBridge.Security;
/// (policy <c>RequireClaim</c> values + the audit role arrays), and at LDAP
/// mapping rows configured by an operator. Holding the literals here means a
/// rename either succeeds everywhere or fails to compile, eliminating the
/// "string drift" class that Security-018 documented.
/// "string drift" class.
/// </para>
/// <para>
/// Task 1.7 canonicalization (auth normalization): role VALUES were
/// Canonicalization (auth normalization): role VALUES were
/// standardized onto the canonical six (<c>Viewer/Operator/Engineer/Designer/
/// Deployer/Administrator</c>; only four are used by ScadaBridge). The legacy
/// ScadaBridge role names were renamed/collapsed as follows:
@@ -30,10 +30,10 @@ namespace ZB.MOM.WW.ScadaBridge.Security;
/// </list>
/// <c>Engineer</c> exists in the canonical vocabulary but is unused by
/// ScadaBridge, so it is intentionally not declared here. <c>Operator</c> is
/// now declared (M7-A3 / T14a) for the two-person Secured Writes feature.
/// now declared for the two-person Secured Writes feature.
/// </para>
/// <para>
/// Secured Writes (M7-A3 / T14a): <c>Operator</c> initiates a secured write and
/// Secured Writes: <c>Operator</c> initiates a secured write and
/// <c>Verifier</c> approves it — two distinct global roles so a single principal
/// cannot both initiate and approve (separation of duties). Both are coarse
/// global roles, matching the existing role model; site scoping (if any) is
@@ -47,11 +47,11 @@ public static class Roles
public const string Deployer = "Deployer";
public const string Viewer = "Viewer";
/// <summary>Initiates a two-person Secured Write (M7-A3 / T14a). Canonical
/// <summary>Initiates a two-person Secured Write. Canonical
/// vocabulary role; pairs with <see cref="Verifier"/> who approves.</summary>
public const string Operator = "Operator";
/// <summary>Approves a two-person Secured Write (M7-A3 / T14a). Held by a
/// <summary>Approves a two-person Secured Write. Held by a
/// principal distinct from the initiating <see cref="Operator"/>.</summary>
public const string Verifier = "Verifier";
@@ -7,7 +7,7 @@ namespace ZB.MOM.WW.ScadaBridge.Security;
/// <see cref="IGroupRoleMapper{TRole}"/> seam from <c>ZB.MOM.WW.Auth.Abstractions</c>.
/// </summary>
/// <remarks>
/// Task 1.1 of the Auth-library adoption: this is an <b>additive</b> wrapper. It does not
/// This is an <b>additive</b> wrapper for the Auth-library adoption. It does not
/// re-implement the LDAP-group → role resolution or the site-scope union semantics — it
/// delegates wholesale to <see cref="RoleMapper.MapGroupsToRolesAsync"/> and re-shapes the
/// result onto the shared contract. <typeparamref name="TRole"/> is <see cref="string"/>
@@ -16,7 +16,7 @@ namespace ZB.MOM.WW.ScadaBridge.Security;
/// HttpOnly/Secure and managed entirely by ASP.NET Core cookie authentication.
/// </para>
/// <para>
/// Task 1.2/1.4 cutover: the LDAP connection settings that used to live here as
/// The LDAP connection settings that used to live here as
/// flat <c>Ldap*</c> keys (server, port, transport, search base, service account,
/// attributes, timeout) moved into a nested <c>ScadaBridge:Security:Ldap</c>
/// sub-section bound to the shared <c>ZB.MOM.WW.Auth.Abstractions.Ldap.LdapOptions</c>
@@ -60,7 +60,7 @@ public class SecurityOptions
public int JwtRefreshThresholdMinutes { get; set; } = 5;
/// <summary>
/// M2.19 (#15): how long a cookie session's role-mapping projection may be stale
/// How long a cookie session's role-mapping projection may be stale
/// before <c>OnValidatePrincipal</c> re-runs the DB-backed <c>RoleMapper</c> on the
/// session's stored LDAP group claims and rebuilds the role/scope claims. Default:
/// <b>15 minutes</b>, matching the documented sliding-refresh cadence.
@@ -107,7 +107,7 @@ public class SecurityOptions
}
/// <summary>
/// M2.19 (#15): startup validator for <see cref="SecurityOptions"/>. Fails fast at boot
/// Startup validator for <see cref="SecurityOptions"/>. Fails fast at boot
/// on any configuration that would defeat idle-timeout enforcement.
/// </summary>
/// <remarks>
@@ -16,7 +16,7 @@ public static class ServiceCollectionExtensions
/// <summary>
/// The configuration section bound to the shared LDAP <c>LdapOptions</c>. Nested
/// under the existing <c>ScadaBridge:Security</c> section as a <c>Ldap</c> sub-section
/// (Task 1.4 config rename) so the non-LDAP <see cref="SecurityOptions"/> fields stay
/// so the non-LDAP <see cref="SecurityOptions"/> fields stay
/// where they are while the LDAP connection settings bind to the shared library.
/// The Host composition root references this constant to register the shared LDAP
/// auth (<c>AddZbLdapAuth</c>) before calling <see cref="AddSecurity"/>.
@@ -46,7 +46,7 @@ public static class ServiceCollectionExtensions
/// <returns>The same <paramref name="services"/> instance for chaining.</returns>
public static IServiceCollection AddSecurity(this IServiceCollection services, bool disableLogin = false)
{
// Task 1.2 cutover: ScadaBridge's bespoke LdapAuthService was replaced by the
// ScadaBridge's bespoke LdapAuthService was replaced by the
// shared ZB.MOM.WW.Auth.Ldap implementation (ScadaBridge was the donor for its
// hardened bind-then-search / escaping / fail-closed semantics, so this was a
// behaviour-equivalent re-point). That registration — AddZbLdapAuth, which binds
@@ -61,7 +61,7 @@ public static class ServiceCollectionExtensions
services.AddScoped<JwtTokenService>();
services.AddScoped<RoleMapper>();
// M2.19 (#15): the cookie OnValidatePrincipal core. Scoped to match the
// The cookie OnValidatePrincipal core. Scoped to match the
// IGroupRoleMapper<string> it depends on (which depends on the Scoped
// ISecurityRepository). The clock is injected (TimeProvider) so the idle/refresh
// thresholds can be exercised deterministically in tests; the production default
@@ -81,7 +81,7 @@ public static class ServiceCollectionExtensions
services.AddHttpContextAccessor();
services.AddSingleton<IAuditActorAccessor, HttpAuditActorAccessor>();
// Auth-adoption Task 1.1: register the shared IGroupRoleMapper<string>
// Auth-adoption: register the shared IGroupRoleMapper<string>
// seam additively, wrapping RoleMapper to reuse its DB-backed mapping +
// site-scope union semantics. Scoped to match RoleMapper's lifetime (it
// depends on the Scoped ISecurityRepository). The existing RoleMapper
@@ -89,7 +89,7 @@ public static class ServiceCollectionExtensions
// to consume this seam in a later task.
services.AddScoped<IGroupRoleMapper<string>, ScadaBridgeGroupRoleMapper>();
// M2.19 (#15): fail-fast config guard — RoleRefreshThresholdMinutes must be strictly
// Fail-fast config guard — RoleRefreshThresholdMinutes must be strictly
// less than IdleTimeoutMinutes. If they are equal or inverted, a single un-refreshed
// cycle can exhaust the entire idle window and idle enforcement is silently defeated.
// SecurityOptionsValidator is registered with ValidateOnStart so a misconfigured
@@ -142,7 +142,7 @@ public static class ServiceCollectionExtensions
// SameSite / SecurePolicy / SlidingExpiration / ExpireTimeSpan are likewise
// applied there via ZbCookieDefaults.Apply.
// M2.19 (#15): OnValidatePrincipal enforces the idle timeout and refreshes
// OnValidatePrincipal enforces the idle timeout and refreshes
// the role/scope claims from the session's STORED LDAP groups (DB-backed
// RoleMapper, NO LDAP) so central role-mapping changes take effect
// mid-session. The lambda is a THIN adapter: it resolves the request-scoped
@@ -154,7 +154,7 @@ public static class ServiceCollectionExtensions
});
}
// CentralUI-005: configure the cookie session as a sliding window so the
// Configure the cookie session as a sliding window so the
// code matches the documented policy ("sliding refresh, 30-minute idle
// timeout"). ASP.NET cookie auth exposes a single ExpireTimeSpan plus a
// SlidingExpiration flag — it cannot natively model a 15-minute sliding
@@ -169,7 +169,7 @@ public static class ServiceCollectionExtensions
// session window. Bound here via PostConfigure so SecurityOptions
// (configured by the Host after AddSecurity) is honoured.
//
// Task 1.5: the cookie hardening (HttpOnly=true, SameSite=Strict,
// The cookie hardening (HttpOnly=true, SameSite=Strict,
// SecurePolicy, SlidingExpiration=true, ExpireTimeSpan=idle) now comes from
// the shared ZbCookieDefaults.Apply, with requireHttps + idleTimeout driven
// by SecurityOptions so behaviour (30-min sliding idle window, HTTPS-only
@@ -193,7 +193,7 @@ public static class ServiceCollectionExtensions
? SecurityOptions.DefaultCookieName
: cookieName;
// Security-021: when the operator opts out of HTTPS-only cookies,
// When the operator opts out of HTTPS-only cookies,
// log a Warning so an HTTP-only deployment is at least audible in
// the startup log. The cookie carries the embedded JWT bearer
// credential — over plain HTTP that travels in cleartext on every
@@ -213,7 +213,7 @@ public static class ServiceCollectionExtensions
}
/// <summary>
/// M2.19 (#15): the thin <see cref="CookieAuthenticationEvents.OnValidatePrincipal"/>
/// The thin <see cref="CookieAuthenticationEvents.OnValidatePrincipal"/>
/// adapter. It resolves the request-scoped <see cref="CookieSessionValidator"/>,
/// asks it for a decision, and applies it to the cookie context:
/// <list type="bullet">
@@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Authentication.Cookies;
namespace ZB.MOM.WW.ScadaBridge.Security;
/// <summary>
/// M2.19 (#15): the single, shared source of truth for the FULL set of claims that
/// The single, shared source of truth for the FULL set of claims that
/// back an interactive cookie session. BOTH the <c>/auth/login</c> endpoint and the
/// <c>OnValidatePrincipal</c> mid-session role-refresh path build their principal
/// through <see cref="Build"/>, so the two can never drift — the spec requires the
@@ -12,7 +12,7 @@ namespace ZB.MOM.WW.ScadaBridge.Security;
/// </summary>
/// <remarks>
/// The claim shape is exactly what the login endpoint historically minted, plus the
/// two M2.19 additions:
/// two additions:
/// <list type="bullet">
/// <item><see cref="ClaimTypes.Name"/> — resolves <c>Identity.Name</c>.</item>
/// <item><see cref="JwtTokenService.DisplayNameClaimType"/> — human display name.</item>
@@ -21,9 +21,9 @@ namespace ZB.MOM.WW.ScadaBridge.Security;
/// <item><see cref="JwtTokenService.SiteIdClaimType"/> — one per permitted site,
/// ONLY when the mapping is not system-wide (deny-by-omission preserved).</item>
/// <item><see cref="JwtTokenService.GroupClaimType"/> — one per raw LDAP group
/// (M2.19): the durable input the mid-session RoleMapper re-run consumes.</item>
/// the durable input the mid-session RoleMapper re-run consumes.</item>
/// <item><see cref="JwtTokenService.LastRoleRefreshClaimType"/> — the role-mapping
/// refresh anchor (M2.19), ISO-8601 round-trippable.</item>
/// refresh anchor, ISO-8601 round-trippable.</item>
/// <item><see cref="JwtTokenService.LastActivityClaimType"/> — the idle-timeout
/// anchor; seeded to the refresh timestamp at login so idle-timeout can be
/// enforced consistently from the very first request.</item>
@@ -21,7 +21,7 @@
<PackageReference Include="Novell.Directory.Ldap.NETStandard" />
<PackageReference Include="ZB.MOM.WW.Auth.Abstractions" />
<PackageReference Include="ZB.MOM.WW.Auth.Ldap" />
<!-- Inbound-API key re-arch (C1): LibraryInboundApiKeyAdmin implements the
<!-- Inbound-API key re-arch: LibraryInboundApiKeyAdmin implements the
Commons IInboundApiKeyAdmin management seam over the shared admin facade
(ApiKeyAdminCommands). Security is the one project referenced by BOTH the
Host (ManagementActor, via ManagementService) and CentralUI, and it already