mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-26 20:20:10 +00:00
fix(proxy): match Prisma index names + extend listing to team for user-keyed callers
Two follow-ups to the managed-resource isolation fix: 1. Rename the new composite indexes to match Prisma's auto-generated naming convention (`<Table>_created_by_team_id_created_at_idx`). The previous `*_team_owner_created_at_idx` names left `prisma migrate diff` reporting an outstanding `RENAME INDEX`, failing `test_aaaasschema_migration_check`. 2. Make `build_owner_filter` return an OR clause when the caller has both a `user_id` and a `team_id`, so listings include team-shared resources the same way `can_access_resource` already permits reading them. Without this a user could fetch a team-shared resource by id but never see it in their list view. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+6
-3
@@ -13,6 +13,9 @@ ALTER TABLE "LiteLLM_ManagedFileTable" ADD COLUMN IF NOT EXISTS "created_by_team
|
||||
ALTER TABLE "LiteLLM_ManagedObjectTable" ADD COLUMN IF NOT EXISTS "created_by_team_id" TEXT;
|
||||
ALTER TABLE "LiteLLM_ManagedVectorStoreTable" ADD COLUMN IF NOT EXISTS "created_by_team_id" TEXT;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "LiteLLM_ManagedFileTable_team_owner_created_at_idx" ON "LiteLLM_ManagedFileTable" ("created_by_team_id", "created_at" DESC);
|
||||
CREATE INDEX IF NOT EXISTS "LiteLLM_ManagedObjectTable_team_owner_created_at_idx" ON "LiteLLM_ManagedObjectTable" ("created_by_team_id", "created_at" DESC);
|
||||
CREATE INDEX IF NOT EXISTS "LiteLLM_ManagedVectorStoreTable_team_owner_created_at_idx" ON "LiteLLM_ManagedVectorStoreTable" ("created_by_team_id", "created_at" DESC);
|
||||
-- Index names follow Prisma's auto-generated convention so `prisma migrate diff`
|
||||
-- against the schema is clean. Postgres caps identifier length at 63 chars,
|
||||
-- which truncates the vector-store name to `_created__idx`.
|
||||
CREATE INDEX IF NOT EXISTS "LiteLLM_ManagedFileTable_created_by_team_id_created_at_idx" ON "LiteLLM_ManagedFileTable" ("created_by_team_id", "created_at" DESC);
|
||||
CREATE INDEX IF NOT EXISTS "LiteLLM_ManagedObjectTable_created_by_team_id_created_at_idx" ON "LiteLLM_ManagedObjectTable" ("created_by_team_id", "created_at" DESC);
|
||||
CREATE INDEX IF NOT EXISTS "LiteLLM_ManagedVectorStoreTable_created_by_team_id_created__idx" ON "LiteLLM_ManagedVectorStoreTable" ("created_by_team_id", "created_at" DESC);
|
||||
|
||||
@@ -38,17 +38,31 @@ def build_owner_filter(
|
||||
- ``{"created_by": <user_id>}`` for user-keyed callers.
|
||||
- ``{"created_by_team_id": <team_id>}`` for service-account callers
|
||||
that have a team but no user_id.
|
||||
- ``{"OR": [...]}`` when the caller has both — listing must include
|
||||
both their own resources and team-shared ones so it stays consistent
|
||||
with ``can_access_resource``.
|
||||
- ``None`` means deny: callers MUST skip the query rather than fall
|
||||
back to an unscoped fetch.
|
||||
"""
|
||||
if _user_has_admin_view(user_api_key_dict):
|
||||
return {}
|
||||
|
||||
if user_api_key_dict.user_id is not None:
|
||||
return {"created_by": user_api_key_dict.user_id}
|
||||
user_id = user_api_key_dict.user_id
|
||||
team_id = user_api_key_dict.team_id
|
||||
|
||||
if user_api_key_dict.team_id is not None:
|
||||
return {"created_by_team_id": user_api_key_dict.team_id}
|
||||
if user_id is not None and team_id is not None:
|
||||
return {
|
||||
"OR": [
|
||||
{"created_by": user_id},
|
||||
{"created_by_team_id": team_id},
|
||||
]
|
||||
}
|
||||
|
||||
if user_id is not None:
|
||||
return {"created_by": user_id}
|
||||
|
||||
if team_id is not None:
|
||||
return {"created_by_team_id": team_id}
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@@ -34,9 +34,17 @@ def test_owner_filter_service_account_scoped_to_team():
|
||||
assert build_owner_filter(service_account) == {"created_by_team_id": "team-eng"}
|
||||
|
||||
|
||||
def test_owner_filter_user_id_takes_precedence_over_team_id():
|
||||
def test_owner_filter_user_with_team_returns_or_filter():
|
||||
"""List view must mirror `can_access_resource`: a user-keyed caller in a
|
||||
team can also access team-shared resources, so the listing returns both
|
||||
their own records and team records via an OR filter."""
|
||||
user = UserAPIKeyAuth(user_id="alice", team_id="team-eng")
|
||||
assert build_owner_filter(user) == {"created_by": "alice"}
|
||||
assert build_owner_filter(user) == {
|
||||
"OR": [
|
||||
{"created_by": "alice"},
|
||||
{"created_by_team_id": "team-eng"},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def test_owner_filter_no_identity_returns_none():
|
||||
|
||||
Reference in New Issue
Block a user