fix(adminui): make /admin/secrets interactive - wrap the Secrets.Ui page with a render mode (#483)
v2-ci / build (push) Successful in 4m0s
v2-ci / unit-tests (push) Failing after 10m19s

The Secrets.Ui RCL's routable page was routed directly, but this host's
router is deliberately static (cookie SignInAsync needs SSR) with per-page
@rendermode opt-in - a directive the RCL page cannot carry, because the
other three family hosts render it under a globally interactive router
where a nested render mode throws. Routing straight to the RCL page
therefore rendered /admin/secrets with no circuit: it displayed, but every
@onclick was silently dead.

Fix: a host-side wrapper page (Pages/SecretsAdmin.razor) now owns
/admin/secrets, carries the standard per-page InteractiveServer render
mode, re-states the RCL page's own authorization policy (the router only
enforces [Authorize] on the routed component, which is now the wrapper),
and renders the RCL page as an ordinary child. The RCL assembly is
de-registered from both AdditionalAssemblies sites (router + endpoint) so
the route is unambiguous.

Guards: SecretsPageWiringTests pins the render mode (the #483 regression),
route parity with the RCL page, and policy parity; the page census in
PageAuthorizationGuardTests classifies the new page and admits the
secrets:manage policy. AdminUI.Tests 665/665.

Live-verified on the rebuilt docker-dev rig with Playwright: /_blazor
circuit negotiated on /admin/secrets and Add secret opens the editor
(before the fix: zero interactive markers, no circuit, dead click).

Closes #483.

Claude-Session: https://claude.ai/code/session_01BL2Vu1ESDQ9SCN4gVKkdts
This commit is contained in:
Joseph Doherty
2026-07-19 00:22:23 -04:00
parent c878fbbd03
commit 5f72ff851d
5 changed files with 107 additions and 7 deletions
@@ -47,6 +47,11 @@ public class PageAuthorizationGuardTests
// ── FleetAdmin (1) ──
[typeof(RoleGrants)] = AdminUiPolicies.FleetAdmin,
// ── Secrets (1): host wrapper for the ZB.MOM.WW.Secrets.Ui RCL page (lmxopcua#483) —
// carries the RCL page's own policy, registered additively by AddAdminUI via
// AddSecretsAuthorization(). ──
[typeof(SecretsAdmin)] = global::ZB.MOM.WW.Secrets.Ui.SecretsAuthorization.ManagePolicy,
// ── AuthenticatedRead (16): dashboards, lists, live tails (read-only) + the dev ContextMenu demo ──
[typeof(Home)] = AdminUiPolicies.AuthenticatedRead,
[typeof(global::ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages.Dev.ContextMenuDemo)] = AdminUiPolicies.AuthenticatedRead,
@@ -75,6 +80,9 @@ public class PageAuthorizationGuardTests
AdminUiPolicies.DriverOperator,
AdminUiPolicies.ConfigEditor,
AdminUiPolicies.AuthenticatedRead,
// Not an AdminUiPolicies constant: the /admin/secrets wrapper re-states the Secrets.Ui
// RCL page's own policy, which AddAdminUI registers via AddSecretsAuthorization().
global::ZB.MOM.WW.Secrets.Ui.SecretsAuthorization.ManagePolicy,
];
private static IReadOnlyList<Type> RoutablePages() =>