[Fix] Allow non-admin compliance path reads (#27234)
* allow non-admin roles on /compliance/* read routes * Restrict compliance routes to internal users --------- Co-authored-by: Michael Riad Zaky <michaelr@Mac.localdomain> Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
parent
9f1b41d206
commit
db8198faba
@ -656,6 +656,13 @@ class LiteLLMRoutes(enum.Enum):
|
||||
"/health/services",
|
||||
] + info_routes
|
||||
|
||||
# Stateless validators on caller-supplied log data; source logs are
|
||||
# already accessible via spend_tracking_routes, so no scope expansion.
|
||||
compliance_check_routes = [
|
||||
"/compliance/eu-ai-act",
|
||||
"/compliance/gdpr",
|
||||
]
|
||||
|
||||
# Routes in `global_spend_tracking_routes` return proxy-wide spend across
|
||||
# every team, customer, and api_key. They are intentionally NOT included
|
||||
# here — non-admin roles must not see other tenants' spend. Admin roles go
|
||||
@ -675,6 +682,7 @@ class LiteLLMRoutes(enum.Enum):
|
||||
]
|
||||
+ spend_tracking_routes
|
||||
+ key_management_routes
|
||||
+ compliance_check_routes
|
||||
)
|
||||
|
||||
internal_user_view_only_routes = spend_tracking_routes
|
||||
|
||||
@ -53,6 +53,61 @@ def test_non_admin_config_update_route_rejected():
|
||||
assert "Your role=internal_user" in str(exc_info.value)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"route",
|
||||
["/compliance/eu-ai-act", "/compliance/gdpr"],
|
||||
)
|
||||
def test_compliance_routes_open_to_internal_user(route):
|
||||
"""Compliance routes are stateless validators on caller-supplied log data
|
||||
- non-admin internal_user roles can call them."""
|
||||
role = LitellmUserRoles.INTERNAL_USER.value
|
||||
user_obj = LiteLLM_UserTable(
|
||||
user_id="test_user",
|
||||
user_email="test@example.com",
|
||||
user_role=role,
|
||||
)
|
||||
valid_token = UserAPIKeyAuth(user_id="test_user", user_role=role)
|
||||
request = MagicMock(spec=Request)
|
||||
request.query_params = {}
|
||||
|
||||
RouteChecks.non_proxy_admin_allowed_routes_check(
|
||||
user_obj=user_obj,
|
||||
_user_role=role,
|
||||
route=route,
|
||||
request=request,
|
||||
valid_token=valid_token,
|
||||
request_data={},
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"route",
|
||||
["/compliance/eu-ai-act", "/compliance/gdpr"],
|
||||
)
|
||||
def test_compliance_routes_blocked_for_internal_user_view_only(route):
|
||||
"""Deprecated internal_user_viewer role must not gain compliance route access."""
|
||||
role = LitellmUserRoles.INTERNAL_USER_VIEW_ONLY.value
|
||||
user_obj = LiteLLM_UserTable(
|
||||
user_id="test_user",
|
||||
user_email="test@example.com",
|
||||
user_role=role,
|
||||
)
|
||||
valid_token = UserAPIKeyAuth(user_id="test_user", user_role=role)
|
||||
request = MagicMock(spec=Request)
|
||||
request.query_params = {}
|
||||
|
||||
with pytest.raises(Exception) as exc_info:
|
||||
RouteChecks.non_proxy_admin_allowed_routes_check(
|
||||
user_obj=user_obj,
|
||||
_user_role=role,
|
||||
route=route,
|
||||
request=request,
|
||||
valid_token=valid_token,
|
||||
request_data={},
|
||||
)
|
||||
assert "Only proxy admin can be used" in str(exc_info.value)
|
||||
|
||||
|
||||
def test_proxy_admin_viewer_config_update_route_rejected():
|
||||
"""Test that proxy admin viewer users are rejected when trying to call /config/update"""
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user