fix(secrets): bridge the shared audit seam so /admin/secrets renders (#22)

Secrets.Ui components inject the SHARED ZB.MOM.WW.Audit.IAuditWriter seam,
which is deliberately distinct from the repo's own Commons IAuditWriter —
the G-4 adoption mounted the UI without registering it, so component
activation threw and the page 500'd on every render (hidden behind the
login wall; it plausibly never worked).

Fix: CentralSharedSeamAuditWriter forwards the shared seam into the central
direct-write path (ICentralAuditWriter -> dbo.AuditLog) — NOT the site
SQLite chain, which is never resolved on central and has no forwarder
there. Registered in the Central composition root next to the Secrets UI
authorization wiring.

Regression pin: CentralCompositionRootTests resolves the full Secrets.Ui
component injection set (ISecretStore / ISecretCipher / shared
IAuditWriter) out of the REAL Program.cs via WebApplicationFactory, plus a
type check that the seam is the central bridge. Red on the previous
commit; the defect class is invisible until the DI graph is actually
built.

Live-proven on the local docker cluster: /admin/secrets 200 + interactive
(Blazor circuit + working @onclick), and secret.add / secret.delete events
(both outcomes) landed in central dbo.AuditLog via the bridge.

Closes #22

Claude-Session: https://claude.ai/code/session_01BL2Vu1ESDQ9SCN4gVKkdts
This commit is contained in:
Joseph Doherty
2026-07-19 00:57:50 -04:00
parent 0083ce3560
commit fc86e8bfe1
3 changed files with 76 additions and 0 deletions
@@ -192,6 +192,14 @@ try
// builds identities with RoleClaimType = ZbClaimTypes.Role, so an Administrator is
// authorized for both manage + reveal. Done Host-side to keep Secrets.Ui out of Security.
builder.Services.Configure<AuthorizationOptions>(o => o.AddSecretsAuthorization());
// Secrets.Ui components audit through the SHARED ZB.MOM.WW.Audit.IAuditWriter seam,
// which is deliberately distinct from the repo's own Commons IAuditWriter — bridge it
// to the central direct-write path (ICentralAuditWriter → dbo.AuditLog), NOT the site
// SQLite chain, which is never resolved on central and has no forwarder here
// (ScadaBridge#22; component activation 500'd without this registration).
builder.Services.AddSingleton<ZB.MOM.WW.Audit.IAuditWriter>(
sp => new ZB.MOM.WW.ScadaBridge.Host.Services.CentralSharedSeamAuditWriter(
sp.GetRequiredService<ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services.ICentralAuditWriter>()));
builder.Services.AddInboundAPI();
// Inbound-API auth re-arch: the shared ZB.MOM.WW.Auth.ApiKeys verifier +