mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
fix(proxy): reduce team-list where-builder complexity and update org-admin scope tests
This commit is contained in:
parent
b4b6f585c5
commit
919923d2bd
2 changed files with 41 additions and 12 deletions
|
|
@ -3839,6 +3839,28 @@ async def _get_org_admin_org_ids(
|
|||
return org_ids if org_ids else None
|
||||
|
||||
|
||||
def _apply_org_admin_membership_union(
|
||||
where_conditions: dict[str, Any],
|
||||
org_admin_org_ids: Optional[list[str]],
|
||||
user_team_ids: list[str],
|
||||
) -> None:
|
||||
"""Union (teams in the admin's orgs) with (teams the caller is a member of).
|
||||
|
||||
Mutates ``where_conditions`` in place for a self/bare org-admin query so
|
||||
teams in orgs where the caller is only a member stay visible (LIT-3723).
|
||||
An org admin with no memberships still sees their org teams, so this never
|
||||
collapses to an empty result.
|
||||
"""
|
||||
union_or: list[dict[str, Any]] = [{"organization_id": {"in": org_admin_org_ids}}]
|
||||
if user_team_ids:
|
||||
union_or.append({"team_id": {"in": user_team_ids}})
|
||||
existing_or = where_conditions.pop("OR", None) # set above from `search`
|
||||
if existing_or is not None:
|
||||
where_conditions["AND"] = [{"OR": existing_or}, {"OR": union_or}]
|
||||
else:
|
||||
where_conditions["OR"] = union_or
|
||||
|
||||
|
||||
async def _build_team_list_where_conditions(
|
||||
prisma_client: PrismaClient,
|
||||
team_id: Optional[str],
|
||||
|
|
@ -3917,14 +3939,7 @@ async def _build_team_list_where_conditions(
|
|||
# of), so teams in orgs where I'm only a member stay visible
|
||||
# (LIT-3723). An org admin with no memberships must still see org
|
||||
# teams, so do NOT early-return None here.
|
||||
union_or: List[Dict[str, Any]] = [{"organization_id": {"in": org_admin_org_ids}}]
|
||||
if user_team_ids:
|
||||
union_or.append({"team_id": {"in": user_team_ids}})
|
||||
existing_or = where_conditions.pop("OR", None) # set above from `search`
|
||||
if existing_or is not None:
|
||||
where_conditions["AND"] = [{"OR": existing_or}, {"OR": union_or}]
|
||||
else:
|
||||
where_conditions["OR"] = union_or
|
||||
_apply_org_admin_membership_union(where_conditions, org_admin_org_ids, user_team_ids)
|
||||
else:
|
||||
# When user_id is provided, filter by that user's direct team
|
||||
# memberships. For org admins the access control gate in
|
||||
|
|
|
|||
|
|
@ -3137,6 +3137,7 @@ async def test_list_team_v2_org_admin_sees_org_teams():
|
|||
organization_id=None,
|
||||
team_id=None,
|
||||
team_alias=None,
|
||||
search=None,
|
||||
user_api_key_dict=mock_user_api_key_dict,
|
||||
page=1,
|
||||
page_size=10,
|
||||
|
|
@ -3149,9 +3150,12 @@ async def test_list_team_v2_org_admin_sees_org_teams():
|
|||
assert len(result["teams"]) == 1
|
||||
assert result["teams"][0].members_count == 1
|
||||
|
||||
# Verify org-scoped where clause
|
||||
# Bare org-admin query is scoped to the admin's org via the union
|
||||
# branch (LIT-3723). With no direct memberships the union collapses to
|
||||
# the org-scope branch only.
|
||||
where = mock_db.litellm_teamtable.find_many.call_args.kwargs["where"]
|
||||
assert where["organization_id"] == {"in": ["org_A"]}
|
||||
assert where["OR"] == [{"organization_id": {"in": ["org_A"]}}]
|
||||
assert "organization_id" not in where
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -3237,6 +3241,7 @@ async def test_list_team_v2_org_admin_own_user_id_sees_all_org_teams():
|
|||
organization_id=None,
|
||||
team_id=None,
|
||||
team_alias=None,
|
||||
search=None,
|
||||
user_api_key_dict=mock_user_api_key_dict,
|
||||
page=1,
|
||||
page_size=10,
|
||||
|
|
@ -3248,9 +3253,18 @@ async def test_list_team_v2_org_admin_own_user_id_sees_all_org_teams():
|
|||
assert result["total"] == 2
|
||||
assert len(result["teams"]) == 2
|
||||
|
||||
# Verify the where clause scopes by org only — no team_id filter
|
||||
# Self/bare org-admin query unions org-scoped teams with the caller's
|
||||
# own memberships (LIT-3723). The org-scope branch still guarantees the
|
||||
# admin sees *all* teams in their org (regression #30215); the team_id
|
||||
# branch only widens the result and never clamps it. No top-level
|
||||
# organization_id/team_id key that could hide an org team the admin
|
||||
# isn't a direct member of.
|
||||
where = mock_db.litellm_teamtable.find_many.call_args.kwargs["where"]
|
||||
assert where["organization_id"] == {"in": ["org_A"]}
|
||||
assert where["OR"] == [
|
||||
{"organization_id": {"in": ["org_A"]}},
|
||||
{"team_id": {"in": ["team_1"]}},
|
||||
]
|
||||
assert "organization_id" not in where
|
||||
assert "team_id" not in where
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue