Skip to content

fix: mirror backend capability checks in scope save guards - #147

Closed
isaacraja wants to merge 1 commit into
LibreChat-AI:mainfrom
isaacraja:fix/scope-save-guard-scoped-capabilities
Closed

isaacraja wants to merge 1 commit into
LibreChat-AI:mainfrom
isaacraja:fix/scope-save-guard-scoped-capabilities

Conversation

@isaacraja

Copy link
Copy Markdown

Problem

Users holding only scoped config capabilities (e.g. the group-manager role with assign:configs:group + manage:configs:balance) are blocked in the panel when saving a group's config with:

Insufficient permissions: requires one of assign:configs, manage:configs

The LibreChat backend would accept their requests — the panel's server-function guards are stricter than the API they front.

Root cause

Every scope-profile mutation in src/server/scopes.ts gated on the broad caps only:

await requireAnyCapability([SystemCapabilities.ASSIGN_CONFIGS, SystemCapabilities.MANAGE_CONFIGS]);

The backend (packages/api/src/admin/config.ts in LibreChat) is more permissive:

  • Whole-doc lifecycle (PUT upsert, toggle, delete): broad manage:configs OR broad assign:configs OR target-scoped assign:configs:
  • Field ops (PATCH /fields, DELETE /fields?fieldPath=, POST /fields/tombstone): broad manage:configs OR per-section manage:configs:
    — assign caps do not gate field ops

Scoped capabilities never imply broad ones (the implication graph only holds manage→read pairs), so the guard always threw.

Fix

Mirror the backend exactly:

  • createScopeFn / toggleScopeActiveFn / deleteScopeFn (whole-doc ops): requireAnyCapability([MANAGE_CONFIGS, ASSIGN_CONFIGS, "assign:configs:"])
  • saveFieldProfileValueFn / bulkSaveProfileValuesFn / removeFieldProfileValueFn / tombstoneFieldProfileValueFn (field ops): requireAllSectionCapabilities(sections) — the existing helper that short-circuits on broad manage:configs and otherwise requires manage:configs:
    per section

Also moved the isInterfacePermissionPath early-return before the guard in saveFieldProfileValueFn / bulkSaveProfileValuesFn so interface paths never trip a section check (matches saveBaseConfigFn's existing filter-then-guard order).

Testing

  • CI (vitest)
  • Manually verified against the backend checks: a principal with assign:configs:group + manage:configs:balance can now create/toggle/delete group scopes and save balance-section fields; non-balance field patches still fail at the backend with "Insufficient permissions for config section:
    " (unchanged backend behavior).

The scope-profile server functions gated every mutation on the broad assign:configs / manage:configs capabilities. The LibreChat backend is more permissive: whole-doc lifecycle (upsert/toggle/delete) accepts assign:configs:<principalType>, and field ops (PATCH/DELETE/tombstone) accept manage:configs:<section> per section. Users holding only scoped grants (e.g. assign:configs:group + manage:configs:balance) were blocked in the panel with "Insufficient permissions: requires one of assign:configs, manage:configs" even though the backend would accept their requests.

- whole-doc ops (create/toggle/delete): accept target-scoped assign:configs:<principalType> in addition to the broad caps
- field ops (save/bulk save/remove/tombstone): switch to requireAllSectionCapabilities, which short-circuits on broad manage:configs and otherwise requires manage:configs:<section>
@isaacraja isaacraja closed this Sep 21, 2026
@isaacraja
isaacraja deleted the fix/scope-save-guard-scoped-capabilities branch September 21, 2026 11:03
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.

1 participant