diff --git a/litellm/proxy/management_endpoints/key_management_endpoints.py b/litellm/proxy/management_endpoints/key_management_endpoints.py index 7e159ec90e7..df15209a370 100644 --- a/litellm/proxy/management_endpoints/key_management_endpoints.py +++ b/litellm/proxy/management_endpoints/key_management_endpoints.py @@ -1400,6 +1400,13 @@ async def _common_key_generation_helper( user_api_key_dict=user_api_key_dict, allow_safe_presets=True, ) + _default_allowed_routes: Final = ( + litellm.default_key_generate_params.get("allowed_routes") + if litellm.default_key_generate_params is not None + else None + ) + if isinstance(_default_allowed_routes, list) and not data_json.get("allowed_routes"): + data_json = {**data_json, "allowed_routes": list(_default_allowed_routes)} # if we get max_budget passed to /key/generate, then use it as key_max_budget. Since generate_key_helper_fn is used to make new users if "max_budget" in data_json: diff --git a/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py index aa6be328f4a..4cf369b096e 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py @@ -19248,6 +19248,83 @@ async def _generate_key_and_get_persisted_row(data: GenerateKeyRequest, mock_ins return key_call.kwargs["data"] +@pytest.mark.asyncio +async def test_default_key_generate_params_allowed_routes_fills_unset(monkeypatch): + monkeypatch.setattr( + litellm, + "default_key_generate_params", + {"allowed_routes": ["llm_api_routes", "/key/spend/report"]}, + ) + mock_insert_data = _wire_key_generation_prisma(monkeypatch) + + response = await generate_key_fn( + data=GenerateKeyRequest(), + user_api_key_dict=UserAPIKeyAuth( + user_role=LitellmUserRoles.INTERNAL_USER, + api_key="sk-internal", + user_id="internal-user", + ), + litellm_changed_by=None, + ) + + assert response is not None + key_call = next(c for c in mock_insert_data.call_args_list if c.kwargs["table_name"] == "key") + assert key_call.kwargs["data"]["allowed_routes"] == ["llm_api_routes", "/key/spend/report"] + + +@pytest.mark.asyncio +async def test_default_key_generate_params_allowed_routes_keeps_explicit_value(monkeypatch): + monkeypatch.setattr( + litellm, + "default_key_generate_params", + {"allowed_routes": ["llm_api_routes", "/key/spend/report"]}, + ) + mock_insert_data = _wire_key_generation_prisma(monkeypatch) + + key_row = await _generate_key_and_get_persisted_row( + GenerateKeyRequest(allowed_routes=["info_routes"]), + mock_insert_data, + ) + + assert key_row["allowed_routes"] == ["info_routes"] + + +@pytest.mark.asyncio +async def test_default_key_generate_params_allowed_routes_key_type_preset_wins(monkeypatch): + from litellm.proxy._types import LiteLLMKeyType + + monkeypatch.setattr( + litellm, + "default_key_generate_params", + {"allowed_routes": ["llm_api_routes", "/key/spend/report"]}, + ) + mock_insert_data = _wire_key_generation_prisma(monkeypatch) + + key_row = await _generate_key_and_get_persisted_row( + GenerateKeyRequest(key_type=LiteLLMKeyType.LLM_API), + mock_insert_data, + ) + + assert key_row["allowed_routes"] == ["llm_api_routes"] + + +@pytest.mark.asyncio +async def test_default_key_generate_params_allowed_routes_empty_list_treated_as_unset(monkeypatch): + monkeypatch.setattr( + litellm, + "default_key_generate_params", + {"allowed_routes": ["llm_api_routes", "/key/spend/report"]}, + ) + mock_insert_data = _wire_key_generation_prisma(monkeypatch) + + key_row = await _generate_key_and_get_persisted_row( + GenerateKeyRequest(allowed_routes=[]), + mock_insert_data, + ) + + assert key_row["allowed_routes"] == ["llm_api_routes", "/key/spend/report"] + + @pytest.mark.asyncio async def test_key_generate_explicit_null_budget_duration_beats_default_key_generate_params(monkeypatch): """An explicit `"budget_duration": null` asks for a budget that never resets.