Skip to content

fix(dashboard/simulator): invalidate effective-permissions on policy/budget mutations (#3228 follow-up)#3237

Merged
houko merged 1 commit into
mainfrom
followup/3228-simulator-invalidation
Apr 26, 2026
Merged

fix(dashboard/simulator): invalidate effective-permissions on policy/budget mutations (#3228 follow-up)#3237
houko merged 1 commit into
mainfrom
followup/3228-simulator-invalidation

Conversation

@houko

@houko houko commented Apr 26, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #3228 (RBAC effective-permissions snapshot).

Bug

useEffectivePermissions (crates/librefang-api/dashboard/src/lib/queries/authz.ts) caches with staleTime: 30_000 and no dashboard mutation invalidated authzKeys.effective(name). After an admin saved a policy / role / channel-binding / budget edit through the dashboard, the permission simulator pane kept showing the pre-edit snapshot for up to 30 s.

The staleTime itself is fine — staleness is the right default for a read-only diagnostic page. The bug was specifically the missing invalidation on user-driven writes.

Invalidation chain added

Every mutation whose write feeds into EffectivePermissions (in librefang-kernel/src/auth.rs) now invalidates the simulator cache:

Mutation What it touches in EffectivePermissions Invalidation
useCreateUser new identity (clears stale 404) authzKeys.effective(name)
useUpdateUser role, channel_bindings authzKeys.effective(originalName) + new name on rename
useDeleteUser identity removed removeQueries(authzKeys.effective(name)) (avoids refetch racing the 404)
useImportUsers arbitrary rows authzKeys.all (bulk sweep)
useUpdateUserPolicy tool_policy, tool_categories, memory_access, channel_tool_rules authzKeys.effective(name)
useUpdateUserBudget / useDeleteUserBudget budget authzKeys.effective(name)

channel_role_mapping decision — NOT added

The reviewer's claim that EffectivePermissions is missing M4's channel_role_mapping was re-verified and is incorrect. The two M4 outputs that ARE per-user — channel_tool_rules and channel_bindings — are already present in the struct (auth.rs:194,198) and rendered in the simulator (PermissionSimulatorPage.tsx:203-204).

channel_role_mapping itself is global kernel config, not per-user data. It translates platform-native admin roles (Telegram supergroup admin, Discord guild admin, Slack workspace admin) into LibreFang roles for inbound senders without an explicit [users.x] row. By the time we're simulating a registered user, the explicit channel_bindings path applies and the global mapping is irrelevant for that user — adding it to a per-user snapshot would be misleading because the value doesn't depend on the selected user.

If a future need arises to surface global RBAC config, it belongs on a separate "RBAC overview" page, not on the per-user simulator.

Verification

  • pnpm typecheck produces only pre-existing errors on client.ts / UserBudgetPage.tsx that also reproduce on a clean origin/main checkout (unrelated to this fix).
  • No Rust changes were made (decision above), so cargo checks were skipped.
  • staleTime: 30_000 is unchanged — only invalidation handlers were added.

Test plan

  • Open the permission simulator, select user A.
  • In another tab, edit A's policy / role / channel binding / budget.
  • Save and switch back to the simulator — the snapshot should refresh on next focus / mount instead of waiting up to 30 s.
  • Repeat for useImportUsers (bulk sweep) and useDeleteUser (snapshot disappears immediately).

…budget mutations (#3228 follow-up)

The permission simulator's `useEffectivePermissions` hook caches with
`staleTime: 30_000` and no mutation in the dashboard invalidated
`authzKeys.effective(name)`. After an admin saved a policy, role,
channel-binding, or budget edit, the simulator pane kept showing the
pre-edit snapshot for up to 30s.

Add invalidation of `authzKeys.effective(variables.name)` (or
`authzKeys.all` for bulk import) to every mutation whose write feeds
into `EffectivePermissions`:
  - useCreateUser (clears any cached 404 for the new name)
  - useUpdateUser (role + channel_bindings; both old & new name on rename)
  - useDeleteUser (removes the snapshot to avoid racing a 404)
  - useImportUsers (bulk; sweep authzKeys.all)
  - useUpdateUserPolicy (tool_policy / tool_categories / memory_access /
    channel_tool_rules)
  - useUpdateUserBudget / useDeleteUserBudget (budget)

The 30s staleTime is intentional and unchanged — staleness is fine for
background refetches; the bug was specifically that user-driven
mutations weren't pushing the cache.

`channel_role_mapping` was NOT added to `EffectivePermissions`. The
struct is per-user; `channel_role_mapping` is a global kernel-config
section that translates platform-native admin roles (Telegram
supergroup admin, Discord guild admin, Slack workspace admin) to
LibreFang roles for users WITHOUT an explicit `[users.x]` row. By the
time we're simulating a registered user, the explicit channel_bindings
path applies and the global mapping is irrelevant for that user. The
two M4 outputs that ARE per-user (`channel_tool_rules` and
`channel_bindings`) are already surfaced.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions github-actions Bot added ready-for-review PR is ready for maintainer review size/S 10-49 lines changed labels Apr 26, 2026
@github-actions github-actions Bot added the no-rust-required This task does not require Rust knowledge label Apr 26, 2026
@houko
houko merged commit 6807dc2 into main Apr 26, 2026
22 checks passed
@houko
houko deleted the followup/3228-simulator-invalidation branch April 26, 2026 14:27
@github-actions github-actions Bot removed the ready-for-review PR is ready for maintainer review label Apr 26, 2026
@houko houko mentioned this pull request Apr 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-rust-required This task does not require Rust knowledge size/S 10-49 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant