From 65df501f155dec1b9773816912590bdb0512bb74 Mon Sep 17 00:00:00 2001 From: Ryan Crabbe Date: Sat, 28 Mar 2026 14:50:31 -0700 Subject: [PATCH] =?UTF-8?q?Simplify=20substring=20search=20=E2=80=94=20rem?= =?UTF-8?q?ove=20admin-only=20gating?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Substring matching for user_id and key_alias now applies unconditionally. The admin-only flag was unnecessary since user_ids are typically UUIDs/emails where substring collisions are not a practical concern. Updated existing test to assert the new filter structure. --- .../key_management_endpoints.py | 28 ++++++------------- .../test_key_management_endpoints.py | 4 +-- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/litellm/proxy/management_endpoints/key_management_endpoints.py b/litellm/proxy/management_endpoints/key_management_endpoints.py index 5452f75e26b..d82febea8fe 100644 --- a/litellm/proxy/management_endpoints/key_management_endpoints.py +++ b/litellm/proxy/management_endpoints/key_management_endpoints.py @@ -4280,13 +4280,11 @@ async def list_keys( else: admin_team_ids = None - use_substring_matching = True if not user_id and user_api_key_dict.user_role not in [ LitellmUserRoles.PROXY_ADMIN.value, LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY.value, ]: user_id = user_api_key_dict.user_id - use_substring_matching = False response = await _list_key_helper( prisma_client=prisma_client, @@ -4307,7 +4305,6 @@ async def list_keys( status=status, project_id=project_id, access_group_id=access_group_id, - use_substring_matching=use_substring_matching, ) verbose_proxy_logger.debug("Successfully prepared response") @@ -4525,7 +4522,6 @@ def _build_key_filter_conditions( include_created_by_keys: bool = False, project_id: Optional[str] = None, access_group_id: Optional[str] = None, - use_substring_matching: bool = False, ) -> Dict[str, Union[str, Dict[str, Any], List[Dict[str, Any]]]]: """Build filter conditions for key listing. @@ -4547,21 +4543,15 @@ def _build_key_filter_conditions( # Base conditions for user's own keys user_condition: Dict[str, Any] = {} if user_id and isinstance(user_id, str): - if use_substring_matching: - user_condition["user_id"] = { - "contains": user_id, - "mode": "insensitive", - } - else: - user_condition["user_id"] = user_id + user_condition["user_id"] = { + "contains": user_id, + "mode": "insensitive", + } if key_alias and isinstance(key_alias, str): - if use_substring_matching: - user_condition["key_alias"] = { - "contains": key_alias, - "mode": "insensitive", - } - else: - user_condition["key_alias"] = key_alias + user_condition["key_alias"] = { + "contains": key_alias, + "mode": "insensitive", + } if exclude_team_id and isinstance(exclude_team_id, str): user_condition["team_id"] = {"not": exclude_team_id} if organization_id and isinstance(organization_id, str): @@ -4664,7 +4654,6 @@ async def _list_key_helper( status: Optional[str] = None, project_id: Optional[str] = None, access_group_id: Optional[str] = None, - use_substring_matching: bool = False, ) -> KeyListResponseObject: """ Helper function to list keys @@ -4700,7 +4689,6 @@ async def _list_key_helper( include_created_by_keys=include_created_by_keys, project_id=project_id, access_group_id=access_group_id, - use_substring_matching=use_substring_matching, ) # Calculate skip for pagination diff --git a/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py index 12ec79d3e0b..c32d84b111d 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py @@ -6588,9 +6588,9 @@ async def test_build_key_filter_member_team_service_accounts(): # Should have 2 conditions: user's own keys + member team service accounts assert len(or_conditions) == 2 - # First: user's own keys + # First: user's own keys (substring match) user_cond = or_conditions[0] - assert user_cond["user_id"] == user_id + assert user_cond["user_id"] == {"contains": user_id, "mode": "insensitive"} # Second: service accounts for member teams (user_id=None AND team_id in member teams) service_account_cond = or_conditions[1]