Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 30 additions & 19 deletions litellm/proxy/management_endpoints/key_management_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,9 +502,7 @@ def _enforce_upperbound_key_params(

for elem in data:
key, value = elem
upperbound_value = getattr(
litellm.upperbound_key_generate_params, key, None
)
upperbound_value = getattr(litellm.upperbound_key_generate_params, key, None)
if upperbound_value is not None:
if value is None:
if fill_defaults:
Expand All @@ -524,9 +522,7 @@ def _enforce_upperbound_key_params(
},
)
elif key in ["budget_duration", "duration"]:
upperbound_duration = duration_in_seconds(
duration=upperbound_value
)
upperbound_duration = duration_in_seconds(duration=upperbound_value)
if value == "-1":
user_duration = float("inf")
else:
Expand Down Expand Up @@ -1759,9 +1755,7 @@ async def _process_single_key_update(
decision = result.get("decision", True)
message = result.get("message", "Authentication Failed - Custom Auth Rule")
if not decision:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, detail=message
)
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=message)

# Enforce upperbound key params on update (don't fill defaults)
_enforce_upperbound_key_params(update_key_request, fill_defaults=False)
Expand Down Expand Up @@ -2638,22 +2632,39 @@ async def info_key_fn_v2(
detail={"message": "Malformed request. No keys passed in."},
)

# Resolve key_aliases to tokens so we never pass token=None (unbounded query)
tokens_to_query = list(data.keys) if data.keys else []
if data.key_aliases:
alias_rows = await prisma_client.db.litellm_verificationtoken.find_many(
where={"key_alias": {"in": data.key_aliases}},
include={"litellm_budget_table": True},
)
Comment on lines +2638 to +2641
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Unnecessary include in alias resolution query

Only row.token is used from alias_rows — the joined litellm_budget_table data is immediately discarded. The include causes an extra DB join on every alias-path request with no benefit.

Suggested change
alias_rows = await prisma_client.db.litellm_verificationtoken.find_many(
where={"key_alias": {"in": data.key_aliases}},
include={"litellm_budget_table": True},
)
alias_rows = await prisma_client.db.litellm_verificationtoken.find_many(
where={"key_alias": {"in": data.key_aliases}},
)

alias_tokens = [row.token for row in alias_rows if row.token]
tokens_to_query.extend(alias_tokens)

if not tokens_to_query:
return {"key": data.keys, "info": []}

key_info = await prisma_client.get_data(
token=data.keys, table_name="key", query_type="find_all"
token=tokens_to_query, table_name="key", query_type="find_all"
)
if key_info is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail={"message": "No keys found"},
)
if not key_info:
return {"key": data.keys, "info": []}

filtered_key_info = []
for k in key_info:
if not await _can_user_query_key_info(
user_api_key_dict=user_api_key_dict,
key=k.token,
key_info=k,
):
continue
try:
k = k.model_dump() # noqa
k_dict = k.model_dump()
except Exception:
# if using pydantic v1
k = k.dict()
filtered_key_info.append(k)
k_dict = k.dict()
k_dict.pop("token", None)
filtered_key_info.append(k_dict)
Comment on lines 2662 to +2667
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Missing attach_object_permission_to_dict — incomplete v1 parity

The PR's stated goal is full v1 parity. The v1 endpoint (info_key_fn, line 2751) calls attach_object_permission_to_dict(key_info, prisma_client) after serializing each key, enriching response objects that carry an object_permission_id with the full object_permission sub-object. The v2 endpoint omits this step entirely, so any key with an object_permission_id will return incomplete data through v2 — silently and with no error.

Suggested change
try:
k = k.model_dump() # noqa
k_dict = k.model_dump()
except Exception:
# if using pydantic v1
k = k.dict()
filtered_key_info.append(k)
k_dict = k.dict()
k_dict.pop("token", None)
filtered_key_info.append(k_dict)
try:
k_dict = k.model_dump()
except Exception:
k_dict = k.dict()
k_dict.pop("token", None)
k_dict = await attach_object_permission_to_dict(k_dict, prisma_client)
filtered_key_info.append(k_dict)

return {"key": data.keys, "info": filtered_key_info}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 "key": null in response for alias-only requests

When the request body contains only key_aliases (no keys), data.keys is None, producing {"key": null, "info": [...]}. Callers querying by alias can't correlate which info objects map to which aliases, and the null field creates an inconsistent response shape vs. v1. Consider reflecting the requested identifiers:

Suggested change
return {"key": data.keys, "info": filtered_key_info}
return {"key": data.keys or data.key_aliases, "info": filtered_key_info}


except Exception as e:
Expand Down
Loading