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:
Joseph Doherty
2026-05-28 09:37:45 -04:00
parent 6d87ee3c3b
commit 7b0b9c7365
1531 changed files with 11180 additions and 11054 deletions
@@ -0,0 +1,49 @@
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Configuration;
/// <summary>
/// Configuration for Audit Log (#23). Bound from the <c>AuditLog</c> section of
/// <c>appsettings.json</c>. Defaults reflect the design (alog.md §6, §10): an
/// 8 KiB payload-summary cap, a 64 KiB cap on error rows, and a 365-day central
/// retention window with monthly partition-switch purge. The default
/// header-redact list covers HTTP auth headers; per-target overrides extend
/// (never replace) the global redactor set.
/// </summary>
public sealed class AuditLogOptions
{
/// <summary>Default payload-summary cap in bytes (default 8 KiB).</summary>
public int DefaultCapBytes { get; set; } = 8192;
/// <summary>Payload-summary cap on error rows in bytes (default 64 KiB).</summary>
public int ErrorCapBytes { get; set; } = 65536;
/// <summary>HTTP headers redacted by default before persistence.</summary>
public List<string> HeaderRedactList { get; set; } = new()
{
"Authorization",
"X-Api-Key",
"Cookie",
"Set-Cookie",
};
/// <summary>Body-content redactors applied globally (regex patterns).</summary>
public List<string> GlobalBodyRedactors { get; set; } = new();
/// <summary>Per-target redaction overrides keyed by target identifier.</summary>
public Dictionary<string, PerTargetRedactionOverride> PerTargetOverrides { get; set; } = new();
/// <summary>Central retention window in days (default 365, range [30, 3650]).</summary>
public int RetentionDays { get; set; } = 365;
/// <summary>
/// Per-body byte ceiling applied to <see cref="AuditEvent.RequestSummary"/> and
/// <see cref="AuditEvent.ResponseSummary"/> for <see cref="AuditChannel.ApiInbound"/> rows
/// (default 1 MiB). The 8 KiB / 64 KiB default/error caps that apply to other channels
/// do not apply here — inbound traffic captures verbatim up to this ceiling and only
/// then sets <see cref="AuditEvent.PayloadTruncated"/>. See
/// <c>docs/plans/2026-05-23-inbound-api-full-response-audit-design.md</c>.
/// </summary>
public int InboundMaxBytes { get; set; } = 1_048_576;
}
@@ -0,0 +1,70 @@
using Microsoft.Extensions.Options;
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Configuration;
/// <summary>
/// Validates <see cref="AuditLogOptions"/> on startup. The caps drive payload
/// truncation in the M2+ writers, so an unset/zero cap would let arbitrarily
/// large blobs into the central <c>AuditLog</c> table. <see cref="AuditLogOptions.ErrorCapBytes"/>
/// must be at least as large as <see cref="AuditLogOptions.DefaultCapBytes"/>
/// because the error cap is meant to capture <em>more</em> detail than the
/// happy-path summary, not less. <see cref="AuditLogOptions.RetentionDays"/> is
/// bounded to <c>[30, 3650]</c> to keep purge windows sane: too short would
/// drop in-flight investigations, too long would defeat the partition-switch
/// purge's purpose.
/// </summary>
public sealed class AuditLogOptionsValidator : IValidateOptions<AuditLogOptions>
{
/// <summary>Inclusive lower bound for <see cref="AuditLogOptions.RetentionDays"/>.</summary>
public const int MinRetentionDays = 30;
/// <summary>Inclusive upper bound for <see cref="AuditLogOptions.RetentionDays"/>.</summary>
public const int MaxRetentionDays = 3650;
/// <summary>Inclusive lower bound for <see cref="AuditLogOptions.InboundMaxBytes"/> (8 KiB).</summary>
public const int MinInboundMaxBytes = 8_192;
/// <summary>Inclusive upper bound for <see cref="AuditLogOptions.InboundMaxBytes"/> (16 MiB).</summary>
public const int MaxInboundMaxBytes = 16_777_216;
/// <inheritdoc />
public ValidateOptionsResult Validate(string? name, AuditLogOptions options)
{
ArgumentNullException.ThrowIfNull(options);
var failures = new List<string>();
if (options.DefaultCapBytes <= 0)
{
failures.Add(
$"AuditLog:{nameof(AuditLogOptions.DefaultCapBytes)} ({options.DefaultCapBytes}) " +
"must be > 0; it drives payload-summary truncation in audit writers.");
}
if (options.ErrorCapBytes < options.DefaultCapBytes)
{
failures.Add(
$"AuditLog:{nameof(AuditLogOptions.ErrorCapBytes)} ({options.ErrorCapBytes}) " +
$"must be >= {nameof(AuditLogOptions.DefaultCapBytes)} ({options.DefaultCapBytes}); " +
"the error-row cap is intended to capture more detail than the happy-path summary.");
}
if (options.RetentionDays < MinRetentionDays || options.RetentionDays > MaxRetentionDays)
{
failures.Add(
$"AuditLog:{nameof(AuditLogOptions.RetentionDays)} ({options.RetentionDays}) " +
$"must be in [{MinRetentionDays}, {MaxRetentionDays}] days.");
}
if (options.InboundMaxBytes < MinInboundMaxBytes || options.InboundMaxBytes > MaxInboundMaxBytes)
{
failures.Add(
$"AuditLog:{nameof(AuditLogOptions.InboundMaxBytes)} ({options.InboundMaxBytes}) " +
$"must be in [{MinInboundMaxBytes}, {MaxInboundMaxBytes}] bytes.");
}
return failures.Count == 0
? ValidateOptionsResult.Success
: ValidateOptionsResult.Fail(failures);
}
}
@@ -0,0 +1,28 @@
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Configuration;
/// <summary>
/// Per-target redaction override applied additively on top of
/// <see cref="AuditLogOptions.GlobalBodyRedactors"/> and the
/// <see cref="AuditLogOptions.DefaultCapBytes"/> / <see cref="AuditLogOptions.ErrorCapBytes"/>
/// caps. Targets are identified by the script-facing external-system /
/// database / notification-list / inbound-API-key name.
/// </summary>
public sealed class PerTargetRedactionOverride
{
/// <summary>Optional payload cap override (bytes); null inherits the global cap.</summary>
public int? CapBytes { get; set; }
/// <summary>Additional body redactor regex patterns (appended to the global list).</summary>
public List<string>? AdditionalBodyRedactors { get; set; }
/// <summary>
/// Opt-in SQL parameter redaction: case-insensitive regex matched against
/// each SQL parameter NAME in the M4 <c>AuditingDbCommand</c> RequestSummary
/// JSON (<c>{"sql":"...","parameters":{"@name":"value", ...}}</c>); values
/// whose name matches are replaced with <c>&lt;redacted&gt;</c>. Null (the
/// default) means parameter values are captured verbatim. Only applied to
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Types.Enums.AuditChannel.DbOutbound"/>
/// rows.
/// </summary>
public string? RedactSqlParamsMatching { get; set; }
}