From 3cdde571d161b72dc3ad4c43912a844d3a4a3e9c Mon Sep 17 00:00:00 2001 From: songkuan-zheng <252822057+songkuan-zheng@users.noreply.github.com> Date: Mon, 15 Jun 2026 14:09:31 +0000 Subject: [PATCH] fix(proxy): bypass by-id user.models filter when no user_id (master key) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 32dd2fd5 by-id authorization commit broke test_model_info_endpoint_returns_defaults_for_specific_model_id — when `user_api_key_dict.user_id is None` (master key, service account), `get_available_models_for_user` was running against a MagicMock'd router and returning a non-list, so the membership check 403'd a path that should bypass auth entirely. Short-circuit explicitly when `user_id is None`, matching `_apply_user_models_filter`'s documented master-key bypass and the inference-time `can_user_call_model` semantics. Verified locally: - test_v1_model_info_user_filter.py: 11/11 - test_model_info_default_limits.py: 7/7 --- litellm/proxy/proxy_server.py | 45 +++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 5cd61119418..682d3e47711 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -13446,29 +13446,32 @@ async def model_info_v1( }, ) # Authorize against user.models — by-id was previously - # short-circuiting before the listing-path filter. - from litellm.proxy.utils import get_available_models_for_user + # short-circuiting before the listing-path filter. Master + # key / service accounts (no user_id) bypass, matching + # `_apply_user_models_filter` semantics. + if user_api_key_dict.user_id is not None: + from litellm.proxy.utils import get_available_models_for_user - authorized_models = await get_available_models_for_user( - user_api_key_dict=user_api_key_dict, - llm_router=llm_router, - general_settings=general_settings, - user_model=user_model, - prisma_client=prisma_client, - proxy_logging_obj=proxy_logging_obj, - team_id=None, - include_model_access_groups=False, - only_model_access_groups=False, - return_wildcard_routes=False, - user_api_key_cache=user_api_key_cache, - ) - if deployment_info.model_name not in authorized_models: - raise HTTPException( - status_code=403, - detail={ - "error": f"Model id = {litellm_model_id} not authorized for this user" - }, + authorized_models = await get_available_models_for_user( + user_api_key_dict=user_api_key_dict, + llm_router=llm_router, + general_settings=general_settings, + user_model=user_model, + prisma_client=prisma_client, + proxy_logging_obj=proxy_logging_obj, + team_id=None, + include_model_access_groups=False, + only_model_access_groups=False, + return_wildcard_routes=False, + user_api_key_cache=user_api_key_cache, ) + if deployment_info.model_name not in authorized_models: + raise HTTPException( + status_code=403, + detail={ + "error": f"Model id = {litellm_model_id} not authorized for this user" + }, + ) _deployment_info_dict = _get_proxy_model_info( model=deployment_info.model_dump(exclude_none=True) )