From 879d71a84aa62dd34f15ff35a36060334e44e4d6 Mon Sep 17 00:00:00 2001 From: Jonathan Wrede Date: Sun, 10 May 2026 18:12:11 +0000 Subject: [PATCH] fix(proxy): allow proxy_admin_viewer to list all API keys validate_key_list_check() only allowed PROXY_ADMIN to bypass the user_id requirement. PROXY_ADMIN_VIEW_ONLY users hit the user_id check, which throws 403 because viewer accounts typically have no associated user_id. This blocked the /ui/?page=api-keys page entirely for Admin Viewer users. Add PROXY_ADMIN_VIEW_ONLY to the role check so viewers can list keys (read-only parity with PROXY_ADMIN). Fixes #26689 --- .../key_management_endpoints.py | 5 ++++- .../test_key_management_endpoints.py | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/management_endpoints/key_management_endpoints.py b/litellm/proxy/management_endpoints/key_management_endpoints.py index b112af1fe20..2daa8463964 100644 --- a/litellm/proxy/management_endpoints/key_management_endpoints.py +++ b/litellm/proxy/management_endpoints/key_management_endpoints.py @@ -4399,7 +4399,10 @@ async def validate_key_list_check( key_hash: Optional[str], prisma_client: PrismaClient, ) -> Optional[LiteLLM_UserTable]: - if user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN.value: + if user_api_key_dict.user_role in [ + LitellmUserRoles.PROXY_ADMIN.value, + LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY.value, + ]: return None if user_api_key_dict.user_id is None: 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 b292e8d0cae..8891c83cd78 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 @@ -6509,6 +6509,27 @@ async def test_validate_key_list_check_proxy_admin(): assert result is None +@pytest.mark.asyncio +async def test_validate_key_list_check_proxy_admin_viewer(): + mock_prisma_client = AsyncMock() + user_api_key_dict = UserAPIKeyAuth( + user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY, + user_id=None, + ) + + result = await validate_key_list_check( + user_api_key_dict=user_api_key_dict, + user_id=None, + team_id=None, + organization_id=None, + key_alias=None, + key_hash=None, + prisma_client=mock_prisma_client, + ) + + assert result is None + + @pytest.mark.asyncio async def test_validate_key_list_check_team_admin_success(): mock_prisma_client = AsyncMock()