Merge pull request #22725 from BerriAI/litellm_team_admin_reset_spend

[Fix] Team Admin Reset Key Spend
This commit is contained in:
yuneng-jiang 2026-03-03 16:50:46 -08:00 • committed by GitHub
commit ab101671c7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 75 additions and 0 deletions

View file

@ -512,6 +512,7 @@ class LiteLLMRoutes(enum.Enum):
KeyManagementRoutes.KEY_UNBLOCK.value,
KeyManagementRoutes.KEY_BULK_UPDATE.value,
KeyManagementRoutes.TEAM_DAILY_ACTIVITY.value,
KeyManagementRoutes.KEY_RESET_SPEND.value,
]
management_routes = [

View file

@ -1116,3 +1116,77 @@ def test_route_in_additional_public_routes_exact_match():
assert route_in_additonal_public_routes("/status") is True
# Non-matching routes should fail
assert route_in_additonal_public_routes("/other") is False
def test_internal_user_can_access_key_reset_spend_route():
"""
Regression test: team admins (role=internal_user) should pass the route-level
check for /key/{hash}/reset_spend. The endpoint itself enforces team admin status.
"""
user_obj = LiteLLM_UserTable(
user_id="team-admin-user",
user_email="teamadmin@example.com",
user_role=LitellmUserRoles.INTERNAL_USER.value,
)
valid_token = UserAPIKeyAuth(
user_id="team-admin-user",
user_role=LitellmUserRoles.INTERNAL_USER.value,
)
request = MagicMock(spec=Request)
request.query_params = {}
key_hash = "baec26d2901589fe9fec76610e6e2be4895cdd8e19b3ada9a4fa2eb85e1901ae"
route = f"/key/{key_hash}/reset_spend"
# Should not raise — the route-level check must pass for team admins
RouteChecks.non_proxy_admin_allowed_routes_check(
user_obj=user_obj,
_user_role=LitellmUserRoles.INTERNAL_USER.value,
route=route,
request=request,
valid_token=valid_token,
request_data={},
)
def test_non_admin_non_team_admin_cannot_access_config_update_but_can_attempt_reset_spend():
"""
An internal_user passes the route check for /key/{hash}/reset_spend
(authorization is deferred to the endpoint), but is still blocked from
admin-only routes like /config/update.
"""
user_obj = LiteLLM_UserTable(
user_id="regular-user",
user_email="user@example.com",
user_role=LitellmUserRoles.INTERNAL_USER.value,
)
valid_token = UserAPIKeyAuth(
user_id="regular-user",
user_role=LitellmUserRoles.INTERNAL_USER.value,
)
request = MagicMock(spec=Request)
request.query_params = {}
key_hash = "baec26d2901589fe9fec76610e6e2be4895cdd8e19b3ada9a4fa2eb85e1901ae"
# /key/{hash}/reset_spend passes the route check for internal_user
RouteChecks.non_proxy_admin_allowed_routes_check(
user_obj=user_obj,
_user_role=LitellmUserRoles.INTERNAL_USER.value,
route=f"/key/{key_hash}/reset_spend",
request=request,
valid_token=valid_token,
request_data={},
)
# /config/update is still blocked
with pytest.raises(Exception) as exc_info:
RouteChecks.non_proxy_admin_allowed_routes_check(
user_obj=user_obj,
_user_role=LitellmUserRoles.INTERNAL_USER.value,
route="/config/update",
request=request,
valid_token=valid_token,
request_data={},
)
assert "Only proxy admin can be used to generate" in str(exc_info.value)