fix: scoping virtual keys in the teams view to be applying the team filter globally instead of an or branch

This commit is contained in:
Ryan Crabbe
2026-03-07 16:23:12 -08:00
parent 28c33f53a3
commit daf7c0c3a8
2 changed files with 42 additions and 3 deletions
@@ -4345,8 +4345,6 @@ def _build_key_filter_conditions(
user_condition: Dict[str, Any] = {}
if user_id and isinstance(user_id, str):
user_condition["user_id"] = user_id
if team_id and isinstance(team_id, str):
user_condition["team_id"] = team_id
if key_alias and isinstance(key_alias, str):
user_condition["key_alias"] = key_alias
if exclude_team_id and isinstance(exclude_team_id, str):
@@ -4414,8 +4412,10 @@ def _build_key_filter_conditions(
elif len(or_conditions) == 1:
where.update(or_conditions[0])
# Apply project_id and access_group_id as global AND filters so they
# Apply team_id, project_id and access_group_id as global AND filters so they
# narrow results across all visibility conditions (own keys, team keys, etc.)
if team_id and isinstance(team_id, str):
where = {"AND": [where, {"team_id": team_id}]}
if project_id:
where = {"AND": [where, {"project_id": project_id}]}
if access_group_id:
@@ -6344,6 +6344,45 @@ async def test_build_key_filter_project_id_and_access_group_id():
assert {"project_id": project_id} in inner_and
@pytest.mark.asyncio
async def test_build_key_filter_team_id_scoped():
"""
When team_id is provided, it should act as a global AND filter so keys
from other teams are excluded even when the user is admin of multiple teams.
"""
from litellm.proxy.management_endpoints.key_management_endpoints import (
_build_key_filter_conditions,
)
where = _build_key_filter_conditions(
user_id="multi-team-user",
team_id="team-A",
organization_id=None,
key_alias=None,
key_hash=None,
exclude_team_id=None,
admin_team_ids=["team-A", "team-B"],
member_team_ids=["team-A", "team-B"],
include_created_by_keys=True,
)
def _collect_team_id_filters(d):
results = []
if isinstance(d, dict):
for k, v in d.items():
if k == "team_id":
results.append(v)
else:
results.extend(_collect_team_id_filters(v))
elif isinstance(d, list):
for item in d:
results.extend(_collect_team_id_filters(item))
return results
team_id_filters = _collect_team_id_filters(where)
assert "team-A" in team_id_filters, f"Expected global team_id='team-A' AND filter, got: {where}"
@pytest.mark.asyncio
async def test_get_member_team_ids():
"""