Skip to content

feat(console): lock role-gated actions instead of hiding or disabling them - #194

Merged
Bugs5382 merged 1 commit into
mainfrom
feat/183-role-gate-locks
Oct 15, 2026
Merged

Bugs5382 merged 1 commit into
mainfrom
feat/183-role-gate-locks

Conversation

@Bugs5382

Copy link
Copy Markdown
Contributor

What and why

Every action the signed-in operator's level can't use now shows the kit's lock pattern (a
lock icon, aria-disabled, still focusable, with a reason a screen reader announces) instead of a
plain disabled button or a hidden control:

  • Enrollment: New enrollment (needs operator), Approve and Reject on a request (needs operator;
    a LINK request needs admin)
  • Certificate profiles: Delete profile (was hidden for a non-admin)
  • Protocol detail: the per-node ACME/EST enable/disable switch (was hidden for a non-admin,
    showing "admin only" text)
  • Node Config: Apply (was a "Read-only" note instead of the button)
  • Node profile Reconcile: Apply catalog version (was a "requires admin level" note instead of
    the button)
  • Node Issue and Re-key: these had no client-side gate at all, even though the manager requires
    operator level for both (RekeyNode, IssueLeaf) -- a viewer only found out after submitting.
    They now lock the same way as the rest.

No route, permission check or server-side behaviour changes; this is presentation only.

I rebased onto main after #192 landed rather than stacking, since #192 touched the first-run
wizard and the credential-request page. Neither of those screens turned out to have a role gate
to convert (their "level" field is the level being requested, not a gate on the signed-in
operator), so there was nothing left to do there for this issue.

Every test that asserted toBeDisabled() or a hidden control for one of these gates now asserts
the locked aria-disabled/lockedReason state instead -- an intentional behaviour change, named in
each test with a short comment on why it moved.

Refs #183

Verification

  • Lint clean
  • Tests pass
  • Build succeeds
  • Documentation updated (if behavior or API changed)

How this was verified

  • npm run lint (eslint + prettier --check): clean.
  • npm run test across all workspaces: 788 console + 247 ui tests pass, including the axe check
    on every page and component touched here.
  • npm run build across all workspaces: tsc + vite build succeed for console, tsc succeeds for ui.
  • node --test scripts/*.test.mjs and node scripts/check-release-bundle.mjs apps/console/dist:
    release bundle clean, no dev-only markers.
  • No behavior or API changed outside the presentation layer this issue covers, so there is no
    docs page to update beyond the kit's existing Button/RoleGate doc comments.

… them

Enrollment's New enrollment and LINK approve, the profile catalog's Delete
profile, the per-node ACME/EST switch, node Config apply, node profile
Reconcile, and node Issue/Re-key now use the kit's Button lockedReason prop
when the signed-in operator's level can't use them, instead of a native
disabled button or a hidden control. Each stays focusable and explains the
role it needs; the server-side checks are unchanged.

Issue and Re-key had no client-side gate at all even though the manager
requires operator level for both (RekeyNode, IssueLeaf); a viewer only found
out on submit. They now lock the same way as the rest.

Tests that asserted toBeDisabled()/a hidden control for these gates now
assert the locked aria-disabled/lockedReason state instead.

Refs #183

Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
@Bugs5382
Bugs5382 marked this pull request as ready for review October 15, 2026 20:15
@github-actions github-actions Bot added the enhancement New feature (feat). Minor version bump. label Oct 15, 2026
@Bugs5382
Bugs5382 merged commit 4bb05e5 into main Oct 15, 2026
18 checks passed
@Bugs5382
Bugs5382 deleted the feat/183-role-gate-locks branch October 15, 2026 23:33
Bugs5382 added a commit that referenced this pull request Oct 15, 2026
…g them (#197)

Extends #194's lock pattern to the actions it didn't cover: profile-form's
Save, node-chain-validate's Validate, node-image-lifecycle's Stage/Activate/
Roll back, operators' request/record/complete/cancel/deny, adopt's Preview
(the wizard's one entry point), root-detail's Rename, node-detail-panel's
Rename/Export key/Import key/Decommission/Reboot, and the SCEP challenge and
enrolment mint/revoke/approve/reject actions.

Every one of these was either hidden entirely for a role that lacks it, or
replaced by a disabled control or a "read-only"/"requires X level" note.
They now show the kit's Button lockedReason, or RoleGate for the few that
are plain table-row buttons rather than the kit's Button (operators' Deny,
Complete and Cancel) -- RoleGate needed a TooltipProvider the console had
never wired up for this purpose, so this adds one locally around the
Operators page, plus the ResizeObserver stub its Radix Tooltip content needs
under jsdom, mirroring the one the UI kit's own test setup already has.

Adopt's admin gate used to replace the whole page with a one-line notice;
it now renders the real wizard with its endpoint field disabled and its
Preview button locked, since nothing past Preview is reachable without it
anyway -- the same "no route or server behaviour changes" rule as the rest
of this pass.

Every test that asserted a hidden or disabled control for one of these now
asserts the locked aria-disabled/lockedReason state instead, named in each
with a short comment on why it moved, same as #194.

No route, permission check or server-side behaviour changes here either;
this is presentation only.

Refs #196

Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
Co-authored-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature (feat). Minor version bump.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant