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
This commit is contained in:
Jonathan Wrede 2026-05-10 18:12:11 +00:00
parent 0af33fbe70
commit 879d71a84a
2 changed files with 25 additions and 1 deletions

View file

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

View file

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