Central UI /admin/secrets throws 500 — Secrets.Ui needs ZB.MOM.WW.Audit.IAuditWriter, which the host never registers #22

Closed
opened 2026-07-19 00:34:48 -04:00 by dohertj2 · 0 comments
Owner

Found 2026-07-19 during the family-wide sweep for the OtOpcUa dead-secrets-page defect (lmxopcua#483). ScadaBridge does not have that render-mode defect (its router is globally interactive) — it has a different one that was hiding behind the login wall.

Symptom

On a freshly deployed local cluster (current main @ 0083ce35, Secrets 0.2.1), navigating to /admin/secrets on a Central node returns HTTP 500:

System.InvalidOperationException: Cannot provide a value for property 'Audit' on type
'ZB.MOM.WW.Secrets.Ui.Components.ConfirmDeleteModal'.
There is no registered service of type 'ZB.MOM.WW.Audit.IAuditWriter'.

Reproduced with Security:Auth:DisableLogin (the auto-login principal), so it is not an authorization problem. It was never seen before because unauthenticated probes stop at the login redirect — the page has plausibly never rendered successfully on ScadaBridge.

Cause

ScadaBridge has two IAuditWriter interfaces:

  • ZB.MOM.WW.Audit.IAuditWriter — the shared-lib seam, which ZB.MOM.WW.Secrets.Ui components (SecretEditor, ConfirmDeleteModal) @inject;
  • ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services.IAuditWriter — the repo's own seam (deliberately distinct per its doc comment), which is what AddAuditLog registers (FallbackAuditWriter).

The secrets adoption (G-4) mounted the UI but never bridged the shared seam, so the container has no ZB.MOM.WW.Audit.IAuditWriter and component construction fails.

Suggested fix

Both interfaces already use the canonical ZB.MOM.WW.Audit.AuditEvent and the same WriteAsync(AuditEvent, CancellationToken) shape, so a tiny adapter registered in the Central composition root suffices, e.g. services.AddSingleton<ZB.MOM.WW.Audit.IAuditWriter>(sp => new SharedSeamAuditWriterAdapter(sp.GetRequiredService<Commons.Interfaces.Services.IAuditWriter>())) — forwarding secrets-UI audit events into the existing site/central audit pipeline. Add a DI test that builds the Central container and resolves the Secrets.Ui component dependencies (ISecretStore, ISecretCipher, ZB.MOM.WW.Audit.IAuditWriter) — this is the same defect class as the four caught in the secrets library: wiring visible only when the graph is actually built.

Verification recipe

Local cluster (bash docker/deploy.sh), DisableLogin override, then /admin/secrets on :9001 must render and "Add secret" must open the editor (Playwright check exists from the #483 sweep; OtOpcUa/HistorianGateway/MxGateway all pass it).

Found 2026-07-19 during the family-wide sweep for the OtOpcUa dead-secrets-page defect (`lmxopcua#483`). ScadaBridge does **not** have that render-mode defect (its router is globally interactive) — it has a different one that was hiding behind the login wall. ## Symptom On a freshly deployed local cluster (current `main` @ `0083ce35`, Secrets 0.2.1), navigating to `/admin/secrets` on a Central node returns **HTTP 500**: ``` System.InvalidOperationException: Cannot provide a value for property 'Audit' on type 'ZB.MOM.WW.Secrets.Ui.Components.ConfirmDeleteModal'. There is no registered service of type 'ZB.MOM.WW.Audit.IAuditWriter'. ``` Reproduced with `Security:Auth:DisableLogin` (the auto-login principal), so it is not an authorization problem. It was never seen before because unauthenticated probes stop at the login redirect — the page has plausibly never rendered successfully on ScadaBridge. ## Cause ScadaBridge has **two** `IAuditWriter` interfaces: - `ZB.MOM.WW.Audit.IAuditWriter` — the shared-lib seam, which `ZB.MOM.WW.Secrets.Ui` components (`SecretEditor`, `ConfirmDeleteModal`) `@inject`; - `ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services.IAuditWriter` — the repo's own seam (deliberately distinct per its doc comment), which is what `AddAuditLog` registers (`FallbackAuditWriter`). The secrets adoption (G-4) mounted the UI but never bridged the shared seam, so the container has no `ZB.MOM.WW.Audit.IAuditWriter` and component construction fails. ## Suggested fix Both interfaces already use the **canonical `ZB.MOM.WW.Audit.AuditEvent`** and the same `WriteAsync(AuditEvent, CancellationToken)` shape, so a tiny adapter registered in the Central composition root suffices, e.g. `services.AddSingleton<ZB.MOM.WW.Audit.IAuditWriter>(sp => new SharedSeamAuditWriterAdapter(sp.GetRequiredService<Commons.Interfaces.Services.IAuditWriter>()))` — forwarding secrets-UI audit events into the existing site/central audit pipeline. Add a DI test that builds the Central container and resolves the Secrets.Ui component dependencies (`ISecretStore`, `ISecretCipher`, `ZB.MOM.WW.Audit.IAuditWriter`) — this is the same defect class as the four caught in the secrets library: wiring visible only when the graph is actually built. ## Verification recipe Local cluster (`bash docker/deploy.sh`), `DisableLogin` override, then `/admin/secrets` on `:9001` must render and "Add secret" must open the editor (Playwright check exists from the #483 sweep; OtOpcUa/HistorianGateway/MxGateway all pass it).
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/ScadaBridge#22