fix(auth): limit team service account route carve-out to /key/generate and /key/update

The key_management_routes group also contains /spend/logs, /team/daily/activity
and other routes whose handlers scope non-admin callers by user_id. A userless
service account key would have reached them unscoped, so the route check now
uses a dedicated two-route allowlist

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
ryan 2026-09-18 09:16:57 +00:00
parent 2542b9cfe8
commit b78793b153
3 changed files with 13 additions and 4 deletions

View file

@ -660,6 +660,11 @@ class LiteLLMRoutes(enum.Enum):
KeyManagementRoutes.AUTO_ROUTER_MANAGE.value,
]
team_service_account_key_routes = [
KeyManagementRoutes.KEY_GENERATE.value,
KeyManagementRoutes.KEY_UPDATE.value,
]
management_routes = (
[
# user

View file

@ -328,7 +328,9 @@ class RouteChecks:
pass # authN/authZ handled by api itself
elif RouteChecks.check_passthrough_route_access(route=route, user_api_key_dict=valid_token) or (
valid_token.is_team_service_account
and RouteChecks.check_route_access(route=route, allowed_routes=LiteLLMRoutes.key_management_routes.value)
and RouteChecks.check_route_access(
route=route, allowed_routes=LiteLLMRoutes.team_service_account_key_routes.value
)
):
pass
elif valid_token.allowed_routes is not None:

View file

@ -3993,8 +3993,10 @@ def test_team_service_account_key_allowed_key_management_routes(route):
assert result is None
def test_team_service_account_key_rejected_for_non_key_management_route():
"""The service account carve-out does not extend past key-management routes."""
@pytest.mark.parametrize("route", ["/team/new", "/spend/logs", "/key/delete", "/key/regenerate"])
def test_team_service_account_key_rejected_outside_generate_and_update(route):
"""The service account carve-out covers only /key/generate and /key/update; other
key-management routes lack team scoping for a userless caller and stay denied."""
valid_token = UserAPIKeyAuth(
api_key="sk",
team_id="t1",
@ -4008,7 +4010,7 @@ def test_team_service_account_key_rejected_for_non_key_management_route():
RouteChecks.non_proxy_admin_allowed_routes_check(
user_obj=None,
_user_role=None,
route="/team/new",
route=route,
request=request,
valid_token=valid_token,
request_data={},