(fix) Fix - don't allow viewer roles to create virtual keys (#6764)

* fix ui route permissions

* fix test_is_ui_route_allowed

* fix test_is_ui_route_allowed

* test_user_role_permissions
This commit is contained in:
Ishaan Jaff 2024-11-15 18:02:13 -08:00 committed by GitHub
parent 9ba8f40bd1
commit 6d2ee70a9a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 148 additions and 42 deletions

View file

@ -278,6 +278,7 @@ class LiteLLMRoutes(enum.Enum):
management_routes = [ # key
"/key/generate",
"/key/{token_id}/regenerate",
"/key/update",
"/key/delete",
"/key/info",
@ -339,11 +340,7 @@ class LiteLLMRoutes(enum.Enum):
"/sso",
"/sso/get/ui_settings",
"/login",
"/key/generate",
"/key/{token_id}/regenerate",
"/key/update",
"/key/info",
"/key/delete",
"/config",
"/spend",
"/user",
@ -364,6 +361,7 @@ class LiteLLMRoutes(enum.Enum):
internal_user_routes = (
[
"/key/generate",
"/key/{token_id}/regenerate",
"/key/update",
"/key/delete",
"/key/health",

View file

@ -1,5 +1,5 @@
import re
from typing import Optional
from typing import List, Optional
from fastapi import HTTPException, Request, status
@ -80,7 +80,9 @@ class RouteChecks:
status_code=status.HTTP_403_FORBIDDEN,
detail=f"user not allowed to access this OpenAI routes, role= {_user_role}",
)
if route in LiteLLMRoutes.management_routes.value:
if RouteChecks.check_route_access(
route=route, allowed_routes=LiteLLMRoutes.management_routes.value
):
# the Admin Viewer is only allowed to call /user/update for their own user_id and can only update
if route == "/user/update":
@ -101,21 +103,27 @@ class RouteChecks:
elif (
_user_role == LitellmUserRoles.INTERNAL_USER.value
and route in LiteLLMRoutes.internal_user_routes.value
and RouteChecks.check_route_access(
route=route, allowed_routes=LiteLLMRoutes.internal_user_routes.value
)
):
pass
elif (
_user_is_org_admin(request_data=request_data, user_object=user_obj)
and route in LiteLLMRoutes.org_admin_allowed_routes.value
elif _user_is_org_admin(
request_data=request_data, user_object=user_obj
) and RouteChecks.check_route_access(
route=route, allowed_routes=LiteLLMRoutes.org_admin_allowed_routes.value
):
pass
elif (
_user_role == LitellmUserRoles.INTERNAL_USER_VIEW_ONLY.value
and route in LiteLLMRoutes.internal_user_view_only_routes.value
and RouteChecks.check_route_access(
route=route,
allowed_routes=LiteLLMRoutes.internal_user_view_only_routes.value,
)
):
pass
elif (
route in LiteLLMRoutes.self_managed_routes.value
elif RouteChecks.check_route_access(
route=route, allowed_routes=LiteLLMRoutes.self_managed_routes.value
): # routes that manage their own allowed/disallowed logic
pass
else:
@ -207,3 +215,20 @@ class RouteChecks:
if re.match(pattern, route):
return True
return False
@staticmethod
def check_route_access(route: str, allowed_routes: List[str]) -> bool:
"""
Check if a route has access by checking both exact matches and patterns
Args:
route (str): The route to check
allowed_routes (list): List of allowed routes/patterns
Returns:
bool: True if route is allowed, False otherwise
"""
return route in allowed_routes or any( # Check exact match
RouteChecks._route_matches_pattern(route=route, pattern=allowed_route)
for allowed_route in allowed_routes
) # Check pattern match

View file

@ -111,12 +111,12 @@ def _get_bearer_token(
return api_key
def _is_ui_route_allowed(
def _is_ui_route(
route: str,
user_obj: Optional[LiteLLM_UserTable] = None,
) -> bool:
"""
- Route b/w ui token check and normal token check
- Check if the route is a UI used route
"""
# this token is only used for managing the ui
allowed_routes = LiteLLMRoutes.ui_routes.value
@ -133,15 +133,7 @@ def _is_ui_route_allowed(
for allowed_route in allowed_routes
):
return True
else:
if user_obj is not None and _is_user_proxy_admin(user_obj=user_obj):
return True
elif _has_user_setup_sso() and route in LiteLLMRoutes.sso_only_routes.value:
return True
else:
raise Exception(
f"This key is made for LiteLLM UI, Tried to access route: {route}. Not allowed"
)
return False
def _is_api_route_allowed(
@ -185,8 +177,8 @@ def _is_allowed_route(
"""
- Route b/w ui token check and normal token check
"""
if token_type == "ui":
return _is_ui_route_allowed(route=route, user_obj=user_obj)
if token_type == "ui" and _is_ui_route(route=route, user_obj=user_obj):
return True
else:
return _is_api_route_allowed(
route=route,