test(proxy): separate the member_add permission gate from the provisioning gate - #35507
Merged
yuneng-berri merged 1 commit intoAug 1, 2026
Conversation
…oning gate Adding a team member by a user_id with no user row is now proxy-admin-only, so the /team/member_add authz matrix, which targeted a never-seeded user_id, started 403ing every non-proxy-admin caller. Seed the member as a real user row so the matrix reads _validate_team_member_add_permissions alone; leaving it unseeded and relaxing the expectations to 403 would have left all 18 rows green with that gate deleted outright. Cover the new gate at the HTTP boundary, where only the helper was pinned before: a team admin and an org admin both clear the permission check on the same team and are still refused an unprovisioned user_id, with no user row left behind. Pin the escape hatch that refusal names too, so closing the email-invite path for non-proxy-admins cannot pass silently. Promote the user seeder the member-info pins had kept private to conftest, and reclaim invited users by their scratch-prefixed email, since an invite allocates the user_id server-side.
Contributor
Greptile SummaryThis test-only PR separates coverage of team-member permission checks from coverage of the user-provisioning restriction.
Confidence Score: 5/5The PR appears safe to merge; the test refactor preserves authorization coverage while adding focused provisioning coverage and complete fixture cleanup. Each parameterized case uses isolated scratch identifiers, the extracted helper preserves the previous seeding behavior, and the new tests verify both HTTP responses and persisted database state.
|
| Filename | Overview |
|---|---|
| tests/proxy_behavior/management/conftest.py | Adds a reusable scratch-user seeder and safely expands teardown to reclaim email-invited users with server-generated IDs. |
| tests/proxy_behavior/management/test_team_member_add.py | Isolates the existing authorization matrix from provisioning behavior and adds focused boundary tests for both provisioning denial and email invitations. |
| tests/proxy_behavior/management/test_team_member_info_validation.py | Replaces the private user-seeding helper with the equivalent shared fixture helper without changing test behavior. |
Reviews (1): Last reviewed commit: "test(proxy): separate the member_add per..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
tin-berri
approved these changes
Aug 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TLDR
Problem this solves:
How it solves it:
Relevant issues
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Captured at
ff4a50c768against a local proxy on port 4001, no mocks. Setup is a team admin (aninternal_userholding theadminrole on their own team) plus the team itself:A. The team admin cannot conjure a user_id that has no user row, and nothing is created behind the refusal. This is the leg the tests had no boundary coverage for.
B. The escape hatch that refusal names actually works for the same caller. Inviting by
user_emailallocates the user_id server-side, so a team admin keeps it:C. A proxy admin adding that same unprovisioned user_id is still allowed, and does provision the row. The gate is a role split, not a blanket block:
D. The team admin can still add a member that already exists. This is the behavior the matrix rows in question were written to pin, and the reason relaxing them to 403 would have been wrong:
Type
✅ Test
Changes
Adding a team member by a
user_idwith no user row is now proxy-admin-only. The/team/member_addauthz matrix targeted a never-seededuser_id, so it started refusing every non-proxy-admin caller and three rows went red. That matrix exists to pin_validate_team_member_add_permissions, which the provisioning rule did not touch, so the member is now seeded as a real user row and the matrix goes back to its original expectations.Relaxing those three rows to 403 instead would have been the wrong repair. Every non-proxy-admin row in the matrix would then be explainable by the provisioning gate alone; I built that variant and ran it against a tree with
_validate_team_member_add_permissionsdeleted outright, and all 18 rows stayed green. With the member seeded, the same deletion fails 13 of them.The provisioning rule itself was pinned only by unit tests calling the helper directly, so nothing proved it was wired into the route. It gets its own matrix, over three actors who all clear the permission check on the same team, so the provisioning gate is the only variable between them; the denial legs also assert no user row is left behind, which is the actual claim. One more test covers the
user_emailinvite the 403 message points callers at, so closing that path for non-proxy-admins cannot pass silently.The user seeder that
test_team_member_info_validation.pykept private moves toconftest.pyalongside the othercreate_scratch_*seeders. The scratch teardown now also reclaims users by scratch-prefixed email, since an invite allocates the user_id server-side and the id-prefix sweep cannot see it.Verified by mutation rather than by the suite passing. Deleting the provisioning gate fails the two new denial rows; deleting the permission gate fails 13 matrix rows; widening the provisioning gate to also reject email-only members fails the escape-hatch test.
tests/proxy_behavioris 733 passed, up from 729.Final Attestation