Skip to content

feat(frontend): wire router meta.minRole into a real guard, ranked against the backend's role order (#16244) - #16489

Merged
mrveiss merged 1 commit into
mainfrom
issue-16244-minrole-guard
Sep 13, 2026
Merged

mrveiss merged 1 commit into
mainfrom
issue-16244-minrole-guard

Conversation

@mrveiss

@mrveiss mrveiss commented Sep 12, 2026 •

Copy link
Copy Markdown
Owner

Single-issue rationale: #16244 covers wiring the router's meta.minRole into a real navigation guard, ranked by the backend's role order. No other open issue shares that scope. The nearest neighbour, #16243, is about usePermissions' permission-string vocabulary, which is a different file and a different mechanism.

Thinking Path

types/router.d.ts declared meta.minRole ("Minimum role required"), but nothing used it: no route set it, no guard read it. The only real role gate was meta.admin + userStore.isAdmin, so minRole looked like working access control but wasn't (found while fixing #14937, which retyped this field to the canonical Role).

Wired it in rather than removing it, per the issue's own ask: a route guard comparing the user's role to minRole by rank, sourced from the backend's actual ranking (autobot_shared/auth/permissions.py::_ROLE_META) rather than a second hand-written order.

This is UI routing only. Same as the existing meta.admin check it sits beside: it never grants access the backend refuses, and the backend's own permission gates remain the actual authority. It changes no route today — nothing sets minRole yet — it just makes the mechanism real so a route safely can.

What Changed

  • constants/roles.ts: ROLE_RANK (mirrors _ROLE_META's priority order exactly: superadmin 110, admin 100, operator 80, analyst 60, editor 55, user 50, readonly 10) and meetsMinRole(role, minRole).
  • router/index.ts: new exported pure function resolveMinRoleRedirect(to, userRole) — the strictest (highest-ranked) minRole among a route's matched records, checked against the caller's role, returning a redirect or null. Wired into the existing navigation guard right after the admin check, same style (redirect to /home, same debug-log pattern). Kept as a standalone function specifically so it's unit-testable without mocking the whole router/user-store stack (there was no existing router test file to extend).

Verification

  • New tests: constants/__tests__/roles.test.ts (+6: rank ordering including the superadmin-above-admin case, meetsMinRole above/at/below the floor, fails safe on missing/unknown role) and a new router/__tests__/resolveMinRoleRedirect.test.ts (6 cases: below minRole redirects, at/above minRole allowed, no minRole set allows any authenticated user, no role at all redirects, strictest minRole among nested matched records wins).
  • Pre-push hook: vue-tsc/vitest skipped locally (no node_modules) — CI is authoritative for frontend type-checking and tests.
  • Pre-commit hooks (hardcoded-values, secret detection, frontend canonical checks) all passed.

Closes #16244

Model Used

Claude Sonnet 5

…ainst the backend's role order (#16244)

types/router.d.ts declared meta.minRole but no route set it and no guard
read it -- it looked like working access control but wasn't. The only
real role gate was meta.admin + userStore.isAdmin.

Adds ROLE_RANK + meetsMinRole to constants/roles.ts, mirroring
autobot_shared/auth/permissions.py's _ROLE_META priority order exactly
(superadmin 110, admin 100, operator 80, analyst 60, editor 55, user 50,
readonly 10) rather than a second hand-written ordering. Wires a new
resolveMinRoleRedirect() into router.ts's navigation guard, alongside the
existing admin check -- same pattern (redirect to /home), same caveat:
this is UI routing only, and the backend's own permission gates remain
the actual authority. It changes no route today (nothing sets minRole
yet); it establishes the enforcement mechanism so a route safely can.

resolveMinRoleRedirect is a small pure function (to, userRole) -> redirect
| null, kept separate from the inline guard callback specifically so it's
unit-testable without mocking the whole router/user-store stack. Tests
cover: below minRole redirects, at/above minRole is allowed, no minRole
set allows any authenticated user, no role at all is redirected, and the
strictest minRole among nested matched records wins.

Closes #16244
@mrveiss mrveiss added this to the v0.9.0 milestone Sep 12, 2026
@mrveiss mrveiss added tech-debt area: frontend-unification Wave 4 · cluster B — Frontend unification & design system labels Sep 12, 2026
@mrveiss mrveiss self-assigned this Sep 12, 2026
@coderabbitai

coderabbitai Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 6612bdb9-64e9-4931-b789-206cd0fbd14c


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

✅ SSOT Configuration Compliance: Passing

🎉 No new hardcoded values of either class — ssot and other both block.

Known backlog in pipeline-scripts/hardcoded_values_baseline.txt is suppressed and tracked in #14371.

@github-actions

Copy link
Copy Markdown
Contributor

Notice: 29 open PRs — past the runaway threshold (25)

There is no PR queue limit, and this is not a request to defer this PR. Work proceeds one issue at a time without a cap on open PRs; review capacity is the constraint.

This notice only means the count is high enough to be worth a glance for a runaway — something opening PRs in a loop, or a merge pipeline that has stalled so nothing is draining.

Currently open:

If the queue is draining normally, ignore this. Otherwise:

  1. Check whether CI is dispatching at all — see the ci-dispatch-watchdog status on these PRs
  2. Merge the ones whose CI has finished and review has passed: gh pr merge <number> --squash --delete-branch
  3. Look for a loop opening near-identical PRs

Warn-only runaway detector — .github/workflows/pr-queue-gate.yml. It never blocks a merge.

This was referenced Sep 12, 2026
@mrveiss

mrveiss commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

Independent review: approved. resolveMinRoleRedirect() is wired into beforeEach after the admin check. ROLE_RANK in constants/roles.ts matches autobot_shared/auth/permissions.py's _ROLE_META byte for byte (superadmin 110, admin 100, operator 80, analyst 60, editor 55, user 50, readonly 10). It's a client-side redirect only, so no backend gate is touched, and no route sets meta.minRole yet, so live routing doesn't change. There are no new UI strings. Closing refs are [16244], fully delivered, and required CI passes. It doesn't overlap #16493, so either can land first. Non-blocking: ROLE_RANK is mirrored by hand, and codegen is tracked in #16491.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: frontend-unification Wave 4 · cluster B — Frontend unification & design system frontend priority: medium tech-debt

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tech-debt(frontend): router meta.minRole is declared and read by no guard

1 participant