Skip to content

fix: gate LLM features for users without an assigned model#568

Merged
pancacake merged 3 commits into
HKUDS:devfrom
IliaAvdeev:fix/capability-access-gating
Jun 14, 2026
Merged

fix: gate LLM features for users without an assigned model#568
pancacake merged 3 commits into
HKUDS:devfrom
IliaAvdeev:fix/capability-access-gating

Conversation

@IliaAvdeev

Copy link
Copy Markdown

Description

A non-admin user with no LLM model assigned could open Chat / Partners / etc.
but silently failed: the turn died into an empty "Delete"-only bubble and the
conversation stayed stuck on "New chat", with no hint that an admin must assign
a model first.

This PR replaces that dead-end with a single capability-access gate applied
across every LLM-consuming surface, so an unprovisioned user sees a clear,
locked UI instead of a broken one.

Frontend

  • CapabilityAccessProvider probes /api/v1/settings (catalog presence = admin
    signal) plus the grant-filtered /api/v1/settings/llm-options, and exposes
    has(capability). It re-checks on tab focus/visibility, so a mid-session grant
    change is reflected without polling.
  • CapabilityGate + RequireCapability lock LLM feature pages (Chat, Partners,
    Co-Writer, Book, Mastery Path, Playground) with a "contact your administrator"
    notice. Sidebar entries for locked features render greyed with a lock badge and
    do not navigate.
  • One route→capability source of truth in web/lib/capability-routes.ts, so a
    new gated feature is declared in a single place.

Backend (defense in depth — the UI gate can be bypassed)

  • has_capability_access() helper, admin-aware, matching the redacted model view
    the frontend reads.
  • Reusable require_llm_access dependency returning a typed 403 with
    code: "NO_MODEL_ACCESS".

Scope: per-user grants cover only the LLM capability (embedding/search are
shared admin infrastructure), so gating is LLM-only.

Related Issues

Module(s) Affected

  • agents
  • api
  • config
  • core
  • knowledge
  • logging
  • services
  • tools
  • utils
  • web (Frontend)
  • docs (Documentation)
  • scripts
  • tests
  • Other: multi_user

Checklist

  • I have read and followed the contribution guidelines.
  • My code follows the project's coding standards.
  • I have run pre-commit and fixed any issues (ruff, ruff-format, prettier, mypy, bandit, detect-secrets all pass on the changed files).
  • I have added relevant tests for my changes.
  • I have updated the documentation (no user-facing docs affected).
  • My changes do not introduce any new security vulnerabilities (this PR adds access control; Bandit passes).

Additional Notes

Screenshots

Before — an unprovisioned user hit a dead chat: the turn failed into a
"Delete"-only bubble and the sidebar stayed stuck on "New chat":

image

After — feature page is locked with a clear "contact admin" notice:

image

After — sidebar shows LLM features greyed with a lock; Knowledge / Space /
Memory stay reachable:

image
  • Tests: backend tests/multi_user/test_capability_access.py (7/7) covers
    has_capability_access and the require_llm_access guard; frontend
    web/tests/capability-access.test.ts (3/3) covers capabilityForPath.
  • i18n: new strings use English keys (project convention); zh translations
    can follow via i18n:audit.

Open to feedback: these are my proposed UX/architecture choices (which
features to gate, the route→capability mapping, the typed 403 contract). If
you'd prefer a different approach, I'm happy to discuss and adjust.

@IliaAvdeev
IliaAvdeev force-pushed the fix/capability-access-gating branch from 755e073 to a40a525 Compare June 13, 2026 19:26
@IliaAvdeev IliaAvdeev changed the title Fix/capability access gating fix: gate LLM features for users without an assigned model Jun 13, 2026
The turn path (chat/partners/mastery via turn_runtime) already enforces
LLM-grant access at its single enforcement point and surfaces a clear
terminal "no model assigned" rejection — so require_llm_access was never
wired to any endpoint and only duplicated that logic. Drop it (and its
guard-only tests) and have turn_runtime reuse has_capability_access, giving
the gate one source of truth shared with the frontend lock.

Frontend fixes kept from the original PR plus two corrections:
- capabilityForPath now matches on a path-segment boundary, so a sibling
  route like /booket can't be swallowed by the /book prefix (+ regression).
- fix the misleading Knowledge sidebar comment — Knowledge needs no
  per-user grant and is never gated.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@pancacake

Copy link
Copy Markdown
Collaborator

Thanks for your contribution. I think this is great, i'll do the rest and merge it~

@pancacake
pancacake merged commit 26f7912 into HKUDS:dev Jun 14, 2026
vaskoyudha added a commit to vaskoyudha/deeptutor-for-programmer-fork that referenced this pull request Jul 25, 2026
…ting

fix: gate LLM features for users without an assigned model
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.

2 participants