Skip to content

fix: honor key access_group_ids when team restricts models - #26275

Merged
ryan-crabbe-berri merged 4 commits into
litellm_internal_stagingfrom
litellm_fix-ag-not-resolved
May 2, 2026
Merged

fix: honor key access_group_ids when team restricts models#26275
ryan-crabbe-berri merged 4 commits into
litellm_internal_stagingfrom
litellm_fix-ag-not-resolved

Conversation

@ryan-crabbe-berri

@ryan-crabbe-berri ryan-crabbe-berri commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Behavior

common_checks now wraps can_team_access_model in try/except. On team_model_access_denied, it consults a new _key_access_group_grants_model helper.

The helper resolves each of the key's access_group_ids via get_access_object (cache-first, same path team-side fallback already uses) and accepts a group only if either:

  • the group's assigned_team_ids includes the key's team_id, or
  • the group's assigned_key_ids includes the key's token.

Accepted groups' access_model_names are unioned and the requested model is checked against that set. If found, the team gate's denial is overridden. If no group authorizes the caller, or none of the authorized groups grants the model, the original team_model_access_denied is re-raised.

This means the access group itself is the source of truth for which teams and keys can use it — keeping the team-as-owner boundary while not requiring the team's access_group_ids field to be populated for the override to fire.

Before / after

Screenshot 2026-05-01 at 4 56 43 PM

Test plan

  • tests/proxy_unit_tests/test_auth_checks.py — 6 new unit tests for _key_access_group_grants_model:
    • team-authorized happy path (group's assigned_team_ids includes the team) → True
    • key-authorized happy path (assigned_key_ids includes the key's token) → True
    • key has no access_group_ids → False (early return)
    • group authorizes caller but doesn't grant the model → False
    • group grants the model but authorizes neither team nor key → False (the foreign-group bypass test)
    • get_access_object raises → False (treated as no authorization)
  • All 62 tests in test_auth_checks.py pass; black-formatted.
  • End-to-end QA against running proxy following the table above. main = 401/401, branch = 200/401.

Out of scope (follow-up)

  • Creation-time validation on /key/generate and /key/update so the API rejects unauthorized access_group_ids upfront instead of silently dropping them at request time.
  • UI scoping in the key edit form's <AccessGroupSelector /> so it only shows groups whose assigned_team_ids / assigned_key_ids authorize the key.
  • Display of access_group_ids on the key detail view.
  • assigned_team_ids / assigned_key_ids editors on the access group form.

Two model-access gates run per request in `common_checks` and they're
asymmetric: `can_key_call_model` falls back to the key's
`access_group_ids`, but `can_team_access_model` only looks at
`team.models` + `team.access_group_ids`. A key granted a model via its
own access group on a model-restricted team is silently denied at the
team gate.

Wrap `can_team_access_model` in try/except in `common_checks`: on
`team_model_access_denied`, consult a new `_key_access_group_grants_model`
helper that expands `valid_token.access_group_ids` via the existing
`_get_models_from_access_groups` and checks via `_can_object_call_model`.
Re-raise if the key's access groups don't grant the model. Any other
exception propagates unchanged.

Effect: request allowed if `team allows X` OR `key's access group
grants X`, making the two gates symmetric.

Test: add three unit tests for `_key_access_group_grants_model`
covering: group covers model, key has no groups, group resolves but
does not cover model.
@greptile-apps

greptile-apps Bot commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a legitimate gap in the access-group model: when a team's allow-list blocks a model, the key's own access_group_ids were never consulted, causing a spurious 401 even when an admin had explicitly granted the model to that key. The fix adds _key_access_group_grants_model, called as a catch-block fallback on team_model_access_denied, which re-checks model access using only access groups that explicitly authorize the key's team or token — preserving the team-ownership boundary. The implementation is well-tested with six targeted unit tests covering the happy paths, early-return, boundary conditions, and the foreign-group bypass regression.

Confidence Score: 5/5

Safe to merge; the two findings are P2 observability and naming suggestions that do not affect correctness or security.

No P0 or P1 issues found. The authorization logic is sound: the access-group override is gated on the group itself listing the key's team or token, preventing escalation. The null-guard on _prisma_client and _user_api_key_cache is present. All six new tests are mock-only and pass. The only gaps are missing debug log messages and a slightly stale tracer span name.

No files require special attention.

Important Files Changed

Filename Overview
litellm/proxy/auth/auth_checks.py Adds _key_access_group_grants_model helper and wraps can_team_access_model in try/except; logic is sound but the helper produces no observability output when it silently returns False, making auth failures hard to diagnose.
tests/proxy_unit_tests/test_auth_checks.py Adds 6 well-structured unit tests covering happy paths, early-return, model-not-covered, foreign-group bypass, and exception tolerance; all mock-only (no real network calls).

Reviews (2): Last reviewed commit: "fix: gate key access_group override on g..." | Re-trigger Greptile

Comment thread litellm/proxy/auth/auth_checks.py Outdated
veria-ai[bot]
veria-ai Bot previously requested changes Apr 22, 2026

@veria-ai veria-ai 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.

High: Team model restriction bypass via key access_group_ids

This PR allows a key's access_group_ids to override team-level model restrictions in common_checks. Previously, the team model check was a hard boundary — if the team couldn't access a model, the request was denied regardless of key-level permissions. Now, if can_team_access_model denies access, the code falls through to check whether the key's access_group_ids grant the model, effectively letting key-level access groups override team restrictions.

A team member who can generate or update keys (via /key/generate or /key/update) can set access_group_ids to reference any access group, including groups created by admins for other teams with broader model access. There is no server-side validation that a key's access_group_ids must be a subset of its team's allowed access groups or models.

Comment thread litellm/proxy/auth/auth_checks.py
@ryan-crabbe-berri ryan-crabbe-berri changed the title fix: honor key access_group_ids when team restricts models wip: honor key access_group_ids when team restricts models Apr 25, 2026
@codecov

codecov Bot commented Apr 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.66667% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
litellm/proxy/auth/auth_checks.py 66.66% 7 Missing ⚠️

📢 Thoughts on this report? Let us know!

A team member could set any access_group_ids on their key (e.g. a group
assigned only to a different team) and override the team's model
restriction. Intersect the key's access_group_ids with team_object.access_group_ids
in _key_access_group_grants_model so foreign groups are dropped before
model expansion. Adds a regression test that asserts expansion is never
called for foreign groups.
@ryan-crabbe-berri
ryan-crabbe-berri temporarily deployed to integration-postgres May 1, 2026 22:54 — with GitHub Actions Inactive
@ryan-crabbe-berri
ryan-crabbe-berri temporarily deployed to integration-postgres May 1, 2026 22:54 — with GitHub Actions Inactive
@ryan-crabbe-berri
ryan-crabbe-berri temporarily deployed to integration-postgres May 1, 2026 22:54 — with GitHub Actions Inactive
@ryan-crabbe-berri
ryan-crabbe-berri temporarily deployed to integration-postgres May 1, 2026 22:54 — with GitHub Actions Inactive
@ryan-crabbe-berri
ryan-crabbe-berri temporarily deployed to integration-postgres May 1, 2026 22:54 — with GitHub Actions Inactive
Replaces the previous intersect-with-team.access_group_ids check, which
made the override unreachable in practice (the team-gate fallback already
covered every case the intersection allowed). The override now resolves
each of the key's access_group_ids via get_access_object and accepts the
group only if its assigned_team_ids includes the key's team_id, or its
assigned_key_ids includes the key's token. This fulfills the original ask
(a key can extend a team's allow-list via a group the admin granted to
that team or that specific key) while still rejecting foreign groups
referenced by team members of other teams.
@ryan-crabbe-berri
ryan-crabbe-berri temporarily deployed to integration-postgres May 1, 2026 23:29 — with GitHub Actions Inactive
@ryan-crabbe-berri
ryan-crabbe-berri temporarily deployed to integration-postgres May 1, 2026 23:29 — with GitHub Actions Inactive
@ryan-crabbe-berri
ryan-crabbe-berri temporarily deployed to integration-postgres May 1, 2026 23:29 — with GitHub Actions Inactive
@ryan-crabbe-berri
ryan-crabbe-berri temporarily deployed to integration-postgres May 1, 2026 23:29 — with GitHub Actions Inactive
@ryan-crabbe-berri
ryan-crabbe-berri temporarily deployed to integration-postgres May 1, 2026 23:29 — with GitHub Actions Inactive
@ryan-crabbe-berri ryan-crabbe-berri changed the title wip: honor key access_group_ids when team restricts models fix: honor key access_group_ids when team restricts models May 1, 2026
@ryan-crabbe-berri

Copy link
Copy Markdown
Contributor Author

@greptileai review

@ryan-crabbe-berri
ryan-crabbe-berri merged commit 85d426c into litellm_internal_staging May 2, 2026
112 of 113 checks passed
@ryan-crabbe-berri
ryan-crabbe-berri deleted the litellm_fix-ag-not-resolved branch May 2, 2026 01:37
fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
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