Skip to content

feat(permissions): add useAccessibleOrgs hook and export getPermissionKeys - #1793

Merged
pontusringblom merged 3 commits into
layer5io:masterfrom
rishiraj38:feat/accessible-orgs-hook
Aug 12, 2026
Merged

feat(permissions): add useAccessibleOrgs hook and export getPermissionKeys#1793
pontusringblom merged 3 commits into
layer5io:masterfrom
rishiraj38:feat/accessible-orgs-hook

Conversation

@rishiraj38

@rishiraj38 rishiraj38 commented Aug 12, 2026

Copy link
Copy Markdown
Member

Description

Add a shared useAccessibleOrgs hook that filters organizations based on whether the user holds a given permission key. This enables both Meshery Cloud and Meshery to show only accessible organizations in the org switcher on 403/permission-denied pages.

Key Changes

  • New useAccessibleOrgs hook (src/custom/useAccessibleOrgs.ts): Accepts org data and a permissionKey as parameters, queries /api/identity/orgs/:orgId/users/keys per-org in parallel via useLazyGetUserKeysQuery, and returns only orgs where the user holds the required permission(s). Generic over the org element type so callers preserve their full typing (e.g. org.name, org.avatar).
  • Exported getPermissionKeys from PermissionProvider — previously internal-only, now available for consumers that need to extract keys from a PermissionKeySpec without going through the CASL evaluator.
  • Barrel exports: Added useAccessibleOrgs, UseAccessibleOrgsOptions, and getPermissionKeys to src/index.tsx.

Related Issues / PRs

Type of Change

  • New feature (non-breaking change which adds functionality)
Screen.Recording.2026-08-13.at.1.03.53.AM.mov

Signed commits

  • Yes, I signed my commits.

Summary by CodeRabbit

  • New Features
    • Added a hook to identify organizations where users have a specified permission.
    • Supports single permissions and flexible “any of” or “all” permission checks.
    • Provides loading, readiness, and empty-result states.
    • Added public exports for the organization-access hook, its options type, and permission key utilities.

…nKeys

Signed-off-by: Rishi Raj <rishiraj438gt@gmail.com>
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@rishiraj38, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 48 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9ee82037-4a21-4162-99f5-b1086cda47ad

📥 Commits

Reviewing files that changed from the base of the PR and between e570ca6 and 788b153.

📒 Files selected for processing (1)
  • src/custom/useAccessibleOrgs.ts
📝 Walkthrough

Walkthrough

Adds useAccessibleOrgs to evaluate organization permissions asynchronously and return accessible organizations with loading and readiness state. The package now publicly exports the hook, its options type, and getPermissionKeys.

Changes

Accessible organization permissions

Layer / File(s) Summary
Permission matching contract
src/custom/useAccessibleOrgs.ts
Defines permission matching for individual keys, anyOf sets, and all-key sets. Matching is case-insensitive. Adds the hook options and public return contract.
Organization permission checks
src/custom/useAccessibleOrgs.ts
Queries eligible non-current organizations in parallel, caches checked IDs, excludes failed requests, and derives accessible organizations and state flags.
Public API exports
src/custom/permissions.tsx, src/index.tsx
Re-exports getPermissionKeys, useAccessibleOrgs, and UseAccessibleOrgsOptions.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant useAccessibleOrgs
  participant useLazyGetUserKeysQuery
  participant PermissionMatcher
  Caller->>useAccessibleOrgs: provide organizations and permission specification
  useAccessibleOrgs->>useLazyGetUserKeysQuery: query eligible organization user keys in parallel
  useLazyGetUserKeysQuery-->>useAccessibleOrgs: return fulfilled or failed results
  useAccessibleOrgs->>PermissionMatcher: evaluate permission keys and function names
  PermissionMatcher-->>useAccessibleOrgs: return accessible organization IDs
  useAccessibleOrgs-->>Caller: return accessible organizations and readiness flags
Loading

Possibly related PRs

Suggested reviewers: leecalcote, codeahmedjamil

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the new useAccessibleOrgs hook and the getPermissionKeys export.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Signed-off-by: Rishi Raj <180109771+rishiraj38@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/custom/useAccessibleOrgs.ts`:
- Around line 95-99: Update the permission-checking flow in useAccessibleOrgs,
including the effect around checkedRef and the request logic around lines
101-137, to associate cached results and each request batch with the current
permissionKey. Clear checkedRef and checkedOrgs when permissionKey changes,
ignore responses belonging to older batches, and ensure isChecking is updated
only for the active batch.
- Around line 125-136: Update the results loop in the setCheckedOrgs callback to
record rejected checks in next using each request’s organization identifier and
a false permission value. Preserve the fulfilled-result handling and ensure
every organization marked as checked before the request reaches a checked state.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 22e283a7-4fd9-48d1-8e90-f6be0776e4b9

📥 Commits

Reviewing files that changed from the base of the PR and between 982b2ad and e570ca6.

📒 Files selected for processing (3)
  • src/custom/permissions.tsx
  • src/custom/useAccessibleOrgs.ts
  • src/index.tsx

Comment on lines +95 to +99
const [checkedOrgs, setCheckedOrgs] = useState<Map<string, boolean>>(new Map());
const [isChecking, setIsChecking] = useState(false);

// Stable ref to avoid re-triggering the effect on every state update
const checkedRef = useRef<Set<string>>(new Set());

@coderabbitai coderabbitai Bot Aug 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Invalidate permission results when the permission requirement changes.

checkedRef and checkedOrgs are keyed only by organization ID. When permissionKey changes, Line 107 suppresses new requests for already checked organizations. The hook can then return access results for the previous permission requirement.

Associate each request batch with its permission requirement. Clear the result cache when that requirement changes. Ignore results from an older batch after a newer requirement becomes active. Keep isChecking scoped to the active batch.

Also applies to: 101-137

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/custom/useAccessibleOrgs.ts` around lines 95 - 99, Update the
permission-checking flow in useAccessibleOrgs, including the effect around
checkedRef and the request logic around lines 101-137, to associate cached
results and each request batch with the current permissionKey. Clear checkedRef
and checkedOrgs when permissionKey changes, ignore responses belonging to older
batches, and ensure isChecking is updated only for the active batch.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch in theory, but in practice permissionKey is set once from the 403 page's permission shield and never changes during the component lifecycle. The useCallback dep array already includes permissionKey so if it did change, the effect re-fires. That said, I'll add a reset of checkedRef when permissionKey changes for correctness doesn't hurt.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rishiraj38, agreed. The 403-page lifecycle makes this low risk in the current caller.

If permissionKey can change, reset both checkedRef and checkedOrgs. Also guard in-flight responses so an older request cannot populate the cache after the new requirement becomes active. Resetting only checkedRef can still expose results from the previous requirement.

🐇


✏️ Learnings added
Learnt from: rishiraj38
Repo: layer5io/sistent PR: 1793
File: src/custom/useAccessibleOrgs.ts:95-99
Timestamp: 2026-08-12T19:55:05.061Z
Learning: In layer5io/sistent, `useAccessibleOrgs` is currently used from the 403-page permission shield, where `permissionKey` is normally fixed for the component lifecycle.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: layer5io/sistent PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-08-04T18:30:58.858Z
Learning: Applies to src/**/*.{ts,tsx} : Use the `Key` interface from `meshery/schemas/permissions`; do not define local permission keys or use/re-export generated `Keys` or `PermissionKeys` maps.

You are interacting with an AI system.

Comment thread src/custom/useAccessibleOrgs.ts
Signed-off-by: Rishi Raj <rishiraj438gt@gmail.com>
@leecalcote

Copy link
Copy Markdown
Member

@rishiraj38, are we importing any RTK client packages from the key construct in meshery/schemas?

@leecalcote

Copy link
Copy Markdown
Member

I wonder how we are dealing with the balance of performance via caching and stale data via cache invalidation. @coderabbitai, thoughts?

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Changed files ---'
git diff --name-only HEAD^ HEAD

printf '%s\n' '--- Hook outline ---'
ast-grep outline src/custom/useAccessibleOrgs.ts --view expanded || true

printf '%s\n' '--- Hook source ---'
cat -n src/custom/useAccessibleOrgs.ts

printf '%s\n' '--- Permission provider outline ---'
ast-grep outline src/custom/PermissionProvider.tsx --view expanded || true

printf '%s\n' '--- Permission query, cache, and hook usages ---'
rg -n -C 4 -i 'useAccessibleOrgs|cache|invalidate|permission.*key|getPermissionKeys|query' \
  src test tests __tests__ 2>/dev/null || true

printf '%s\n' '--- Relevant diff ---'
git diff HEAD^ HEAD -- src/custom/useAccessibleOrgs.ts src/custom/PermissionProvider.tsx src/custom/permissions.tsx src/index.tsx

Length of output: 191535


@leecalcote, the current balance favors request suppression over freshness.

useAccessibleOrgs keeps a component-local cache in checkedOrgs and checkedRef. It sends one request per eligible organization for each mounted hook instance. This prevents duplicate requests caused by re-renders. The parallel requests are reasonable for the stated 403-page use case.

The hook clears this cache only when the permissionKey reference changes. It does not invalidate when any of these change:

  • The user receives or loses a role or permission.
  • Organization membership changes.
  • The active session changes.
  • A previously failed request becomes available.
  • A time limit expires.

A failed request is also cached as false. This makes the UI reach isReady, but a transient network failure hides an accessible organization until unmount, remount, or a changed permissionKey.

I recommend an explicit freshness contract. Add a host-provided invalidationKey or refreshToken to UseAccessibleOrgsOptions, and clear checkedOrgs and checkedRef when it changes. The host can increment it after role, membership, or session updates. Optionally expose refetchAccessibleOrgs() for retry after a request failure.

Also, callers should memoize permissionKey. A new object with identical keys resets the current cache because the effect uses reference equality.

For this 403-page path, I would not add polling or a long-lived global cache. An explicit invalidation signal provides predictable correctness with the existing low request volume.

You are interacting with an AI system.

@rishiraj38

Copy link
Copy Markdown
Member Author

@rishiraj38, are we importing any RTK client packages from the key construct in meshery/schemas?

Yes. The hook imports useLazyGetUserKeysQuery from @meshery/schemas/cloudApi and the Key type from @meshery/schemas/permissions.

@pontusringblom

Copy link
Copy Markdown
Contributor

@rishiraj38, are we importing any RTK client packages from the key construct in meshery/schemas?

Yes. The hook imports useLazyGetUserKeysQuery from @meshery/schemas/cloudApi and the Key type from @meshery/schemas/permissions.

Good to hear!

@pontusringblom
pontusringblom merged commit fc2f497 into layer5io:master Aug 12, 2026
6 checks passed
@pontusringblom

Copy link
Copy Markdown
Contributor

This affects Meshery UI, too, right?

@rishiraj38

Copy link
Copy Markdown
Member Author

This affects Meshery UI, too, right?

I shouldn't have used cloudApi directly; Meshery can't directly talk to the cloud server. It routes all requests through the Meshery server via mesheryApi. Updated the hook to accept triggerGetKeys as a parameter so each app passes its own trigger. Fixed in this PR.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants