litellm/tests/proxy_behavior/management/test_key_info.py
Yuneng Jiang 7a38fd33c1
chore(proxy_behavior): drop README + trim comments
Removes the suite README — its contents (local repro, layout, conventions)
were either restated by the file structure or already covered by the
workflow YAML and pyproject.toml. Trims docstrings and inline comments
across every test file to keep only non-obvious WHY (the masking
``_get_user_in_team`` reads, the LiteLLM_VerificationTokenView models-can't-
be-NULL gotcha, the org_admin/peer-visibility surprise, the rotation
contract).

Suite still 129 green locally.
2026-05-20 16:56:15 -07:00

74 lines
3.3 KiB
Python

import pytest
from .actors import Actor
pytestmark = pytest.mark.asyncio(loop_scope="session")
# (id, actor, target_actor, expected_status). Targets are 3 fixed seeded keys
# representing the canonical relations: own, OWNER (same org_a/team_alpha),
# and CROSS_ORG_USER (org_b/team_beta).
#
# Notable pinned behaviors (intentionally surfaced, not endorsed):
# - ORG_ADMIN 403s on individual key info even within its own org —
# visibility is "your own keys" + "your team's keys", not "your org's keys".
# - Same-team peers (internal_user, unrelated_same_org, service_account) DO
# see each other's keys.
_SCENARIOS = [
("own/proxy_admin", Actor.PROXY_ADMIN, Actor.PROXY_ADMIN, 200),
("own/org_admin", Actor.ORG_ADMIN, Actor.ORG_ADMIN, 200),
("own/team_admin", Actor.TEAM_ADMIN, Actor.TEAM_ADMIN, 200),
("own/internal_user", Actor.INTERNAL_USER, Actor.INTERNAL_USER, 200),
("own/owner", Actor.OWNER, Actor.OWNER, 200),
("own/unrelated_same_org", Actor.UNRELATED_SAME_ORG, Actor.UNRELATED_SAME_ORG, 200),
("own/cross_org_user", Actor.CROSS_ORG_USER, Actor.CROSS_ORG_USER, 200),
("own/service_account", Actor.SERVICE_ACCOUNT, Actor.SERVICE_ACCOUNT, 200),
("owner_key/proxy_admin", Actor.PROXY_ADMIN, Actor.OWNER, 200),
("owner_key/org_admin", Actor.ORG_ADMIN, Actor.OWNER, 403),
("owner_key/team_admin", Actor.TEAM_ADMIN, Actor.OWNER, 200),
("owner_key/internal_user", Actor.INTERNAL_USER, Actor.OWNER, 200),
("owner_key/owner", Actor.OWNER, Actor.OWNER, 200),
("owner_key/unrelated_same_org", Actor.UNRELATED_SAME_ORG, Actor.OWNER, 200),
("owner_key/cross_org_user", Actor.CROSS_ORG_USER, Actor.OWNER, 403),
("owner_key/service_account", Actor.SERVICE_ACCOUNT, Actor.OWNER, 200),
("cross_org/proxy_admin", Actor.PROXY_ADMIN, Actor.CROSS_ORG_USER, 200),
("cross_org/org_admin", Actor.ORG_ADMIN, Actor.CROSS_ORG_USER, 403),
("cross_org/team_admin", Actor.TEAM_ADMIN, Actor.CROSS_ORG_USER, 403),
("cross_org/internal_user", Actor.INTERNAL_USER, Actor.CROSS_ORG_USER, 403),
("cross_org/owner", Actor.OWNER, Actor.CROSS_ORG_USER, 403),
(
"cross_org/unrelated_same_org",
Actor.UNRELATED_SAME_ORG,
Actor.CROSS_ORG_USER,
403,
),
("cross_org/cross_org_user", Actor.CROSS_ORG_USER, Actor.CROSS_ORG_USER, 200),
("cross_org/service_account", Actor.SERVICE_ACCOUNT, Actor.CROSS_ORG_USER, 403),
]
@pytest.mark.parametrize(
"actor,target_actor,expected_status",
[(a, t, s) for (_id, a, t, s) in _SCENARIOS],
ids=[s[0] for s in _SCENARIOS],
)
async def test_key_info_authz_matrix(
actor: Actor, target_actor: Actor, expected_status: int, proxy_client, world
):
caller = world.keys[actor]
target = world.keys[target_actor]
resp = await proxy_client.get(
f"/key/info?key={target.cleartext}",
headers={"Authorization": f"Bearer {caller.cleartext}"},
)
assert (
resp.status_code == expected_status
), f"{actor.value} → {target_actor.value}: {resp.status_code} {resp.text}"
if expected_status == 200:
body = resp.json()
# The handler echoes back whatever ?key was passed (cleartext here),
# so accept either form — info.user_id is the canonical identity check.
assert body.get("key") in (target.cleartext, target.hashed)
assert body["info"].get("user_id") == target.user_id