[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:
Michael-RZ-Berri 2026-05-07 12:07:23 -07:00 committed by GitHub
parent 9f1b41d206
commit db8198faba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 63 additions and 0 deletions

View File

@ -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

View File

@ -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"""