Skip to content

feat(webui): add missing permission scopes (models:register, logs:list, monitor:view) to UI - #5254

Merged
qinxuye merged 7 commits into
xorbitsai:mainfrom
m199369309:feat/permission-scope-align
Jul 29, 2026
Merged

feat(webui): add missing permission scopes (models:register, logs:list, monitor:view) to UI#5254
qinxuye merged 7 commits into
xorbitsai:mainfrom
m199369309:feat/permission-scope-align

Conversation

@m199369309

@m199369309 m199369309 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add three permission scopes that exist in the backend but were never exposed in the frontend UI:

  • models:register — Register/unregister models
  • logs:list — View logs
  • monitor:view — View monitoring

These scopes are already defined in xinference/api/oauth2/advanced/auth_service.py (INITIAL_ADMIN_PERMISSIONS) but were missing from ALL_PERMISSIONS in the frontend, making it impossible to grant them to non-admin users via the UI.

Changes

  • permissions.tsx: Add 3 missing scopes to ALL_PERMISSIONS and add logs/monitor groups to PERMISSION_GROUPS
  • use-menu-auth.ts: Add hasLogsList, hasMonitorView, and canRegisterModel computed properties (with legacy scope alias support for models:add and models:unregister)
  • sidebar.tsx: Gate log center on hasLogsList scope; gate monitor center on hasMonitorView scope; gate register model on canRegisterModel scope (all gated behind auth_advanced flag)
  • en/zh/ja/ko.ts: Add i18n labels and group names for new scopes

Backward Compatibility

  • Admin users have all scopes and are unaffected
  • Non-admin users without the new scopes will simply not see the log/monitor menu items (when auth_advanced is enabled)
  • Legacy models:add and models:unregister scopes recognized via frontend alias check (matching backend scope_aliases.py normalization)
  • No backend changes required

…t, monitor:view) to UI

- Add models:register, logs:list, monitor:view to ALL_PERMISSIONS array
- Add logs and monitor permission groups to PERMISSION_GROUPS
- Add hasLogsList and hasMonitorView to useMenuAuth hook
- Gate log center sidebar visibility on hasLogsList scope
- Gate monitor center sidebar visibility on hasMonitorView scope
- Add i18n labels for new scopes and groups (en, zh, ja, ko)

These three scopes are already defined in the backend
(INITIAL_ADMIN_PERMISSIONS) but were never exposed in the UI,
making it impossible to grant them to non-admin users.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@XprobeBot XprobeBot added this to the v3.x milestone Jul 28, 2026

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces new permissions (models:register, logs:list, and monitor:view) and updates the sidebar navigation to conditionally display the log and monitor centers based on these permissions. The review feedback correctly identifies a critical bug where the new permission flags are omitted from the useMemo dependency array in the sidebar, which prevents the UI from updating when permissions load asynchronously. Additionally, the feedback recommends implementing and applying the canRegisterModel permission check to properly gate the model registration menu item.

Comment thread frontend/src/components/layout/sidebar.tsx Outdated
Comment thread frontend/src/hooks/use-menu-auth.ts
- Add hasLogsList, hasMonitorView, canRegisterModel to navGroups
  useMemo dependency array to fix stale UI after async token load
- Add canRegisterModel to useMenuAuth hook
- Gate /register-model sidebar item on canRegisterModel scope

Without the dependency array fix, non-admin users with logs:list or
monitor:view scopes would never see the log/monitor center menu items
because navGroups is not recomputed after the token loads.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread frontend/src/hooks/use-menu-auth.ts Outdated
Backend scope_aliases.py normalizes models:add and models:unregister to
models:register. Users with legacy scopes in their JWT token pass API
authorization but should also see the Register Model sidebar item. Add
both legacy scope names to the hasScope check.
Comment thread frontend/src/components/layout/sidebar.tsx
… pages

Sidebar show gating alone does not prevent direct URL navigation to
/monitor-center or /log-center. Add a reusable PermissionGuard client
component that checks the required scope against useMenuAuth and
renders an access-denied page when auth_advanced is enabled and the
user lacks the scoped permission.

- Create PermissionGuard component (monitor:view, logs:list,
  models:register scopes)
- Wrap /monitor-center page with monitor:view guard
- Wrap /log-center page with logs:list guard
- Add accessDenied i18n keys (en, zh, ja, ko)

The /register-model page uses server-side redirect() so its guard is
handled at the sidebar level for now.

@qinxuye qinxuye 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.

One route-level permission gap remains.

Comment thread frontend/src/components/auth/permission-guard.tsx
The server-side redirect at /register-model does not protect direct URLs
such as /register-model/LLM or /register-model/LLM/<name>. Both client
pages rendered the registration form for users without the
models:register scope, relying only on backend 403 errors.

Wrap both dynamic page clients with PermissionGuard scope="models:register"
so the UI permission is enforced consistently with monitor-center and
log-center.

@qinxuye qinxuye 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.

One initialization gap remains in the route guard.

Comment thread frontend/src/components/auth/permission-guard.tsx
clusterUIConfig starts as {} so auth_advanced is temporarily falsy
before the global APIs resolve. This creates a brief window where
unauthorized users see protected pages on direct-route refresh.
Read globalReady from useGlobal() and render a Loading spinner
until the configuration is loaded.
qinxuye
qinxuye previously approved these changes Jul 28, 2026

@qinxuye qinxuye 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.

LGTM

@qinxuye qinxuye 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.

The two Windows failures are deterministic: Python 3.10 and 3.13 both fail test_build_subpool_envs_for_virtual_env_enabled because os.path.join() produces backslash-separated paths on Windows while the expected value hard-codes forward slashes. This is a baseline failure introduced by 7c772e1d1a, not by this frontend-only PR; the latest main run fails the same two Windows jobs. Please rebase/rerun after the baseline test is fixed so this PR can return to green.

@qinxuye
qinxuye dismissed their stale review July 28, 2026 18:52

Withdrawing approval until the two reproducible Windows CI failures are cleared. They are baseline failures rather than changes introduced by this PR.

@qinxuye qinxuye 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.

LGTM

@qinxuye
qinxuye merged commit 4a9602f into xorbitsai:main Jul 29, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants