mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
fix(credentials): restore GET /credentials for Admin Viewer
PROXY_ADMIN_VIEW_ONLY passed the route gate but fell into the non-admin branch and took the caller-administers-nothing 403, a regression against the Admin Viewer read-parity rule this PR introduced. The viewer now gets the full list like the proxy admin, with every value constant-masked so the read-only role receives no usable secret
This commit is contained in:
parent
a5834bf613
commit
3fe6b46f39
2 changed files with 45 additions and 1 deletions
|
|
@ -307,7 +307,8 @@ async def get_credentials(
|
|||
|
||||
try:
|
||||
is_proxy_admin = _is_proxy_admin(user_api_key_dict)
|
||||
if is_proxy_admin:
|
||||
is_admin_viewer = user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY
|
||||
if is_proxy_admin or is_admin_viewer:
|
||||
visible = list(litellm.credential_list)
|
||||
else:
|
||||
scope = await _caller_admin_scope(user_api_key_dict, prisma_client)
|
||||
|
|
|
|||
|
|
@ -900,6 +900,49 @@ async def test_get_credentials_returns_all_for_proxy_admin(monkeypatch):
|
|||
assert generic["credential_values"]["otel_headers"] == raw_headers
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_credentials_admin_viewer_gets_full_list_fully_masked(monkeypatch):
|
||||
"""PROXY_ADMIN_VIEW_ONLY keeps read parity with PROXY_ADMIN on this endpoint:
|
||||
the full credential list, including provider credentials, with every stored
|
||||
value constant-masked so the read-only role receives no usable secret."""
|
||||
raw_headers = "Authorization=Bearer collector-secret,x-api-key=api-secret"
|
||||
monkeypatch.setattr(
|
||||
litellm,
|
||||
"credential_list",
|
||||
[
|
||||
CredentialItem(
|
||||
credential_name="openai",
|
||||
credential_values={"api_key": "sk-secret"},
|
||||
credential_info={"custom_llm_provider": "openai"},
|
||||
),
|
||||
CredentialItem(
|
||||
credential_name="generic-otel",
|
||||
credential_values={"otel_headers": raw_headers},
|
||||
credential_info={
|
||||
"credential_type": "logging",
|
||||
"description": "generic",
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
response = await endpoints.get_credentials(
|
||||
request=MagicMock(),
|
||||
fastapi_response=MagicMock(),
|
||||
user_api_key_dict=UserAPIKeyAuth(
|
||||
api_key="k", user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY
|
||||
),
|
||||
)
|
||||
names = sorted(c["credential_name"] for c in response["credentials"])
|
||||
assert names == ["generic-otel", "openai"]
|
||||
assert all(
|
||||
value == "********"
|
||||
for c in response["credentials"]
|
||||
for value in c["credential_values"].values()
|
||||
)
|
||||
assert "collector-secret" not in str(response)
|
||||
assert "sk-secret" not in str(response)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_authorize_patch_malformed_stored_access_does_not_500(
|
||||
_patch_team_admin_lookup,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue