mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-05 08:07:05 +00:00
fix(proxy): only a provably missing user row counts as unrestricted in the websocket passthrough gate
This commit is contained in:
parent
f846388bb1
commit
a0b2e7fca6
2 changed files with 36 additions and 3 deletions
|
|
@ -2352,6 +2352,13 @@ async def _backfill_null_user_email(
|
|||
return updated_row
|
||||
|
||||
|
||||
class UserNotFoundError(ValueError):
|
||||
"""The user row is provably absent, as opposed to merely unreadable, so a caller that reads a missing row as no user-level limits can key on it without also swallowing a database that would not answer."""
|
||||
|
||||
def __init__(self, user_id: str) -> None:
|
||||
super().__init__(f"User doesn't exist in db. 'user_id'={user_id}. Create user via `/user/new` call.")
|
||||
|
||||
|
||||
@log_db_metrics
|
||||
async def get_user_object(
|
||||
user_id: str | None,
|
||||
|
|
@ -2457,7 +2464,7 @@ async def get_user_object(
|
|||
value=None,
|
||||
last_db_access_time=last_db_access_time,
|
||||
)
|
||||
raise Exception
|
||||
raise UserNotFoundError(user_id=user_id)
|
||||
|
||||
if response.organization_memberships is not None and len(response.organization_memberships) > 0:
|
||||
# dump each organization membership to type LiteLLM_OrganizationMembershipTable
|
||||
|
|
@ -2493,7 +2500,9 @@ async def get_user_object(
|
|||
)
|
||||
|
||||
return _response
|
||||
except Exception as e: # if user not in db
|
||||
except UserNotFoundError:
|
||||
raise
|
||||
except Exception as e:
|
||||
_log_budget_lookup_failure("user", e)
|
||||
raise ValueError(
|
||||
f"User doesn't exist in db. 'user_id'={user_id}. Create user via `/user/new` call. Got error - {e}"
|
||||
|
|
@ -4169,7 +4178,7 @@ async def _user_object_or_none(
|
|||
user_id_upsert=False,
|
||||
proxy_logging_obj=proxy_logging_obj,
|
||||
)
|
||||
except ValueError:
|
||||
except UserNotFoundError:
|
||||
return None
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7361,6 +7361,30 @@ async def test_enforced_model_allowlists_treats_a_missing_user_row_as_unrestrict
|
|||
assert [list(scope) for scope in scopes] == [[], [], [], [], []]
|
||||
|
||||
|
||||
class _UnreachableUserPrisma:
|
||||
class db:
|
||||
class litellm_usertable:
|
||||
@staticmethod
|
||||
async def find_unique(where: dict[str, str], include: dict[str, bool]) -> None:
|
||||
raise RuntimeError("database gone")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_enforced_model_allowlists_surfaces_a_failed_user_lookup():
|
||||
from litellm.proxy.auth.auth_checks import enforced_model_allowlists
|
||||
from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache
|
||||
from litellm.proxy.utils import ProxyLogging
|
||||
|
||||
cache = UserApiKeyCache()
|
||||
with pytest.raises(ValueError, match="database gone"):
|
||||
await enforced_model_allowlists(
|
||||
valid_token=UserAPIKeyAuth(token="hashed-fake", user_id="user-fake"),
|
||||
prisma_client=_UnreachableUserPrisma(),
|
||||
user_api_key_cache=cache,
|
||||
proxy_logging_obj=ProxyLogging(user_api_key_cache=cache),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_enforced_model_allowlists_reads_every_level_from_cache():
|
||||
from litellm.proxy._types import (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue