mirror of
https://github.com/BerriAI/litellm.git
synced 2025-04-27 03:34:10 +00:00
Litellm UI stable version 02 12 2025 (#8497)
* fix(key_management_endpoints.py): fix `/key/list` to include `return_full_object` as a top-level query param Allows user to specify they want the keys as a list of objects * refactor(key_list.tsx): initial refactor of key table in user dashboard offloads key filtering logic to backend api prevents common error of user not being able to see their keys * fix(key_management_endpoints.py): allow internal user to query `/key/list` to see their keys * fix(key_management_endpoints.py): add validation checks and filtering to `/key/list` endpoint allow internal user to see their keys. not anybody else's * fix(view_key_table.tsx): fix issue where internal user could not see default team keys * fix: fix linting error * fix: fix linting error * fix: fix linting error * fix: fix linting error * fix: fix linting error * fix: fix linting error * fix: fix linting error
This commit is contained in:
parent
b58348746a
commit
a6a30ce997
9 changed files with 351 additions and 108 deletions
|
@ -1676,6 +1676,52 @@ async def regenerate_key_fn(
|
|||
raise handle_exception_on_proxy(e)
|
||||
|
||||
|
||||
def validate_key_list_check(
|
||||
complete_user_info: LiteLLM_UserTable,
|
||||
user_id: Optional[str],
|
||||
team_id: Optional[str],
|
||||
organization_id: Optional[str],
|
||||
key_alias: Optional[str],
|
||||
):
|
||||
if complete_user_info.user_role == LitellmUserRoles.PROXY_ADMIN.value:
|
||||
return # proxy admin can see all keys
|
||||
|
||||
# internal user can only see their own keys
|
||||
if user_id:
|
||||
if complete_user_info.user_id != user_id:
|
||||
raise ProxyException(
|
||||
message="You are not authorized to check another user's keys",
|
||||
type=ProxyErrorTypes.bad_request_error,
|
||||
param="user_id",
|
||||
code=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
|
||||
if team_id:
|
||||
if team_id not in complete_user_info.teams:
|
||||
raise ProxyException(
|
||||
message="You are not authorized to check this team's keys",
|
||||
type=ProxyErrorTypes.bad_request_error,
|
||||
param="team_id",
|
||||
code=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
|
||||
if organization_id:
|
||||
if (
|
||||
complete_user_info.organization_memberships is None
|
||||
or organization_id
|
||||
not in [
|
||||
membership.organization_id
|
||||
for membership in complete_user_info.organization_memberships
|
||||
]
|
||||
):
|
||||
raise ProxyException(
|
||||
message="You are not authorized to check this organization's keys",
|
||||
type=ProxyErrorTypes.bad_request_error,
|
||||
param="organization_id",
|
||||
code=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/key/list",
|
||||
tags=["key management"],
|
||||
|
@ -1689,14 +1735,18 @@ async def list_keys(
|
|||
size: int = Query(10, description="Page size", ge=1, le=100),
|
||||
user_id: Optional[str] = Query(None, description="Filter keys by user ID"),
|
||||
team_id: Optional[str] = Query(None, description="Filter keys by team ID"),
|
||||
organization_id: Optional[str] = Query(
|
||||
None, description="Filter keys by organization ID"
|
||||
),
|
||||
key_alias: Optional[str] = Query(None, description="Filter keys by key alias"),
|
||||
return_full_object: bool = Query(False, description="Return full key object"),
|
||||
) -> KeyListResponseObject:
|
||||
"""
|
||||
List all keys for a given user or team.
|
||||
List all keys for a given user / team / organization.
|
||||
|
||||
Returns:
|
||||
{
|
||||
"keys": List[str],
|
||||
"keys": List[str] or List[UserAPIKeyAuth],
|
||||
"total_count": int,
|
||||
"current_page": int,
|
||||
"total_pages": int,
|
||||
|
@ -1706,7 +1756,14 @@ async def list_keys(
|
|||
from litellm.proxy.proxy_server import prisma_client
|
||||
|
||||
# Check for unsupported parameters
|
||||
supported_params = {"page", "size", "user_id", "team_id", "key_alias"}
|
||||
supported_params = {
|
||||
"page",
|
||||
"size",
|
||||
"user_id",
|
||||
"team_id",
|
||||
"key_alias",
|
||||
"return_full_object",
|
||||
}
|
||||
unsupported_params = set(request.query_params.keys()) - supported_params
|
||||
if unsupported_params:
|
||||
raise ProxyException(
|
||||
|
@ -1722,6 +1779,47 @@ async def list_keys(
|
|||
verbose_proxy_logger.error("Database not connected")
|
||||
raise Exception("Database not connected")
|
||||
|
||||
if not user_api_key_dict.user_id:
|
||||
raise ProxyException(
|
||||
message="You are not authorized to access this endpoint. No 'user_id' is associated with your API key.",
|
||||
type=ProxyErrorTypes.bad_request_error,
|
||||
param="user_id",
|
||||
code=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
|
||||
complete_user_info: 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 is None:
|
||||
raise ProxyException(
|
||||
message="You are not authorized to access this endpoint. No 'user_id' is associated with your API key.",
|
||||
type=ProxyErrorTypes.bad_request_error,
|
||||
param="user_id",
|
||||
code=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
|
||||
complete_user_info_pydantic_obj = LiteLLM_UserTable(
|
||||
**complete_user_info.model_dump()
|
||||
)
|
||||
|
||||
validate_key_list_check(
|
||||
complete_user_info=complete_user_info_pydantic_obj,
|
||||
user_id=user_id,
|
||||
team_id=team_id,
|
||||
organization_id=organization_id,
|
||||
key_alias=key_alias,
|
||||
)
|
||||
|
||||
if user_id is None and complete_user_info_pydantic_obj.user_role != [
|
||||
LitellmUserRoles.PROXY_ADMIN.value,
|
||||
LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY.value,
|
||||
]:
|
||||
user_id = user_api_key_dict.user_id
|
||||
|
||||
response = await _list_key_helper(
|
||||
prisma_client=prisma_client,
|
||||
page=page,
|
||||
|
@ -1729,6 +1827,7 @@ async def list_keys(
|
|||
user_id=user_id,
|
||||
team_id=team_id,
|
||||
key_alias=key_alias,
|
||||
return_full_object=return_full_object,
|
||||
)
|
||||
|
||||
verbose_proxy_logger.debug("Successfully prepared response")
|
||||
|
@ -1736,6 +1835,7 @@ async def list_keys(
|
|||
return response
|
||||
|
||||
except Exception as e:
|
||||
verbose_proxy_logger.exception(f"Error in list_keys: {e}")
|
||||
if isinstance(e, HTTPException):
|
||||
raise ProxyException(
|
||||
message=getattr(e, "detail", f"error({str(e)})"),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue