mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-12 14:22:52 +00:00
Merge pull request #25541 from BerriAI/litellm_field_level_checks
[Fix] Align field-level checks in user and key update endpoints
This commit is contained in:
@@ -1131,6 +1131,16 @@ async def _update_single_user_helper(
|
||||
if prisma_client is None:
|
||||
raise Exception("Not connected to DB!")
|
||||
|
||||
# Only proxy admins can modify user_role
|
||||
if (
|
||||
user_request.user_role is not None
|
||||
and user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN.value
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="Only proxy admins can modify user roles.",
|
||||
)
|
||||
|
||||
# Validate user identifier
|
||||
if not user_request.user_id and not user_request.user_email:
|
||||
raise ValueError("Either user_id or user_email must be provided")
|
||||
@@ -1508,12 +1518,35 @@ async def bulk_user_update(
|
||||
detail={"error": "Database not connected"},
|
||||
)
|
||||
|
||||
# Only proxy admins can modify user_role in bulk updates
|
||||
_bulk_role = (
|
||||
getattr(data.user_updates, "user_role", None) if data.user_updates else None
|
||||
)
|
||||
if _bulk_role is None and data.users:
|
||||
_bulk_role = next(
|
||||
(u.user_role for u in data.users if u.user_role is not None), None
|
||||
)
|
||||
if (
|
||||
_bulk_role is not None
|
||||
and user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN.value
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="Only proxy admins can modify user roles.",
|
||||
)
|
||||
|
||||
# Determine the list of users to update
|
||||
users_to_update: Union[
|
||||
List[UpdateUserRequest], List[UpdateUserRequestNoUserIDorEmail]
|
||||
] = []
|
||||
|
||||
if data.all_users and data.user_updates:
|
||||
# Only proxy admins can update all users at once
|
||||
if user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN.value:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="Only proxy admins can update all users at once.",
|
||||
)
|
||||
# Optimized path for updating all users directly in database
|
||||
all_users_in_db = await prisma_client.db.litellm_usertable.find_many(
|
||||
order={"created_at": "desc"}
|
||||
|
||||
@@ -768,9 +768,9 @@ async def _common_key_generation_helper( # noqa: PLR0915
|
||||
request_type="key", **data_json, table_name="key"
|
||||
)
|
||||
|
||||
response["soft_budget"] = (
|
||||
data.soft_budget
|
||||
) # include the user-input soft budget in the response
|
||||
response[
|
||||
"soft_budget"
|
||||
] = data.soft_budget # include the user-input soft budget in the response
|
||||
|
||||
response = GenerateKeyResponse(**response)
|
||||
|
||||
@@ -1961,8 +1961,13 @@ async def _validate_update_key_data(
|
||||
user_api_key_cache=user_api_key_cache,
|
||||
)
|
||||
|
||||
# Admin-only: only proxy admins, team admins, or org admins can modify max_budget
|
||||
if data.max_budget is not None and data.max_budget != existing_key_row.max_budget:
|
||||
# Admin-only: only proxy admins, team admins, or org admins can modify max_budget or spend
|
||||
if (
|
||||
data.max_budget is not None and data.max_budget != existing_key_row.max_budget
|
||||
) or (
|
||||
data.spend is not None
|
||||
and data.spend != getattr(existing_key_row, "spend", None)
|
||||
):
|
||||
if prisma_client is not None:
|
||||
hashed_key = existing_key_row.token
|
||||
await _check_key_admin_access(
|
||||
@@ -1970,7 +1975,7 @@ async def _validate_update_key_data(
|
||||
hashed_token=hashed_key,
|
||||
prisma_client=prisma_client,
|
||||
user_api_key_cache=user_api_key_cache,
|
||||
route="/key/update (max_budget)",
|
||||
route="/key/update (max_budget/spend)",
|
||||
)
|
||||
|
||||
# Check team limits if key has a team_id (from request or existing key)
|
||||
@@ -3272,10 +3277,10 @@ async def delete_verification_tokens(
|
||||
try:
|
||||
if prisma_client:
|
||||
tokens = [_hash_token_if_needed(token=key) for key in tokens]
|
||||
_keys_being_deleted: List[LiteLLM_VerificationToken] = (
|
||||
await prisma_client.db.litellm_verificationtoken.find_many(
|
||||
where={"token": {"in": tokens}}
|
||||
)
|
||||
_keys_being_deleted: List[
|
||||
LiteLLM_VerificationToken
|
||||
] = await prisma_client.db.litellm_verificationtoken.find_many(
|
||||
where={"token": {"in": tokens}}
|
||||
)
|
||||
|
||||
if len(_keys_being_deleted) == 0:
|
||||
@@ -3475,9 +3480,9 @@ async def _rotate_master_key( # noqa: PLR0915
|
||||
from litellm.proxy.proxy_server import proxy_config
|
||||
|
||||
try:
|
||||
models: Optional[List] = (
|
||||
await prisma_client.db.litellm_proxymodeltable.find_many()
|
||||
)
|
||||
models: Optional[
|
||||
List
|
||||
] = await prisma_client.db.litellm_proxymodeltable.find_many()
|
||||
except Exception:
|
||||
models = None
|
||||
# 2. process model table
|
||||
@@ -4117,11 +4122,11 @@ async def validate_key_list_check(
|
||||
param="user_id",
|
||||
code=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
complete_user_info_db_obj: Optional[BaseModel] = (
|
||||
await prisma_client.db.litellm_usertable.find_unique(
|
||||
where={"user_id": user_api_key_dict.user_id},
|
||||
include={"organization_memberships": True},
|
||||
)
|
||||
complete_user_info_db_obj: Optional[
|
||||
BaseModel
|
||||
] = await prisma_client.db.litellm_usertable.find_unique(
|
||||
where={"user_id": user_api_key_dict.user_id},
|
||||
include={"organization_memberships": True},
|
||||
)
|
||||
|
||||
if complete_user_info_db_obj is None:
|
||||
@@ -4204,10 +4209,10 @@ async def _fetch_user_team_objects(
|
||||
if complete_user_info is None or not complete_user_info.teams:
|
||||
return []
|
||||
|
||||
teams: Optional[List[BaseModel]] = (
|
||||
await prisma_client.db.litellm_teamtable.find_many(
|
||||
where={"team_id": {"in": complete_user_info.teams}}
|
||||
)
|
||||
teams: Optional[
|
||||
List[BaseModel]
|
||||
] = await prisma_client.db.litellm_teamtable.find_many(
|
||||
where={"team_id": {"in": complete_user_info.teams}}
|
||||
)
|
||||
if teams is None:
|
||||
return []
|
||||
|
||||
Reference in New Issue
Block a user