fix(proxy): only widen an id-only bulk member delete to its email when the user lists the team

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
ryan 2026-09-15 00:09:57 +00:00
parent 5ace6fa731
commit 98ea14758f
2 changed files with 23 additions and 1 deletions

View file

@ -218,7 +218,9 @@ async def _remove_members_from_team(
requested_rows: Final = await _user_tx_db(tx).find_many(
where=_any_filter(_in_filter("user_id", requested_ids), _in_filter("user_email", requested_emails))
)
email_of: Final = MappingProxyType({u.user_id: u.user_email for u in requested_rows if u.user_email is not None})
email_of: Final = MappingProxyType(
{u.user_id: u.user_email for u in requested_rows if u.user_email is not None and team_id in u.teams}
)
requests: Final = tuple(_with_row_email(r, email_of) for r in members)
removed_members: Final = tuple(m for m in roster if any(_addresses_member(m, r) for r in requests))
kept_members: Final = tuple(m for m in roster if not any(_addresses_member(m, r) for r in requests))

View file

@ -610,6 +610,26 @@ async def test_bulk_member_delete_by_id_removes_the_members_email_only_roster_en
assert users["u1"].teams == [] and users["twin"].teams == ["t1"]
@pytest.mark.asyncio
async def test_bulk_member_delete_by_id_of_a_non_member_leaves_a_same_email_users_roster_entry():
team = LiteLLM_TeamTable(
team_id="t1",
members_with_roles=[
Member(user_id=None, user_email="shared@example.com", role="user"),
Member(user_id="keep", user_email="keep@example.com", role="user"),
],
)
outsider = _UserRow(user_id="outsider", user_email="shared@example.com", teams=[])
member = _UserRow(user_id="member", user_email="shared@example.com", teams=["t1"])
prisma = _FakePrisma(users=[outsider, member, _user("keep", "t1")], teams=[team])
results = await _remove(prisma, "t1", [{"user_id": "outsider"}])
assert [(r.success, r.error) for r in results] == [(False, "User not found in team")]
assert _roster(prisma, "t1") == [None, "keep"]
assert prisma.db.litellm_usertable.rows["member"].teams == ["t1"]
@pytest.mark.asyncio
async def test_bulk_member_delete_rejects_unknown_team_and_unauthorized_callers():
prisma = _FakePrisma(users=[_user("u1", "t1")], teams=[_team("t1", "u1")])