From 6970e1000c6fc874e9351e06eba79e3a6054e642 Mon Sep 17 00:00:00 2001 From: Ryan Crabbe Date: Sat, 14 Mar 2026 15:28:37 -0700 Subject: [PATCH 1/3] fix: align DefaultInternalUserParams Pydantic default with runtime fallback The Pydantic default for user_role was INTERNAL_USER, but all runtime provisioning paths (SSO, SCIM, JWT) fall back to INTERNAL_USER_VIEW_ONLY when no settings are saved. This caused the UI to show "Internal User" on fresh instances while new users actually got "Internal Viewer". --- litellm/proxy/_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index b7ac4212cbd..8bc77d300c6 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -4262,7 +4262,7 @@ class DefaultInternalUserParams(LiteLLMPydanticObjectBase): LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY, ] ] = Field( - default=LitellmUserRoles.INTERNAL_USER, + default=LitellmUserRoles.INTERNAL_USER_VIEW_ONLY, description="Default role assigned to new users created", ) max_budget: Optional[float] = Field( From c60f1dd590c23fe5b79699f9f3c4176a541c6b19 Mon Sep 17 00:00:00 2001 From: Ryan Crabbe Date: Sat, 14 Mar 2026 15:36:26 -0700 Subject: [PATCH 2/3] test: add regression test for fresh-instance default role sync Asserts that GET /get/internal_user_settings returns INTERNAL_USER_VIEW_ONLY on a fresh DB with no saved settings, matching the runtime fallback in SSO/SCIM/JWT provisioning. --- .../test_proxy_setting_endpoints.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py b/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py index 7378b14cdf5..15ec901ab5e 100644 --- a/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py +++ b/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py @@ -111,6 +111,43 @@ def test_get_internal_user_settings(self, mock_proxy_config, mock_auth): assert "user_role" in data["field_schema"]["properties"] assert "description" in data["field_schema"]["properties"]["user_role"] + def test_get_internal_user_settings_fresh_db_defaults_to_viewer( + self, mock_auth, monkeypatch + ): + """ + On a fresh DB with no saved settings, the GET endpoint should return + INTERNAL_USER_VIEW_ONLY as the default role — matching the runtime + fallback in SSO/SCIM/JWT provisioning paths. + """ + # Simulate fresh DB: no default_internal_user_params in config + empty_config = { + "litellm_settings": {}, + "general_settings": {}, + "environment_variables": {}, + } + + from litellm.proxy.proxy_server import proxy_config + + monkeypatch.setattr( + proxy_config, "get_config", lambda: empty_config # sync, wrapped by endpoint + ) + + import asyncio + + async def mock_get_config(): + return empty_config + + monkeypatch.setattr(proxy_config, "get_config", mock_get_config) + + response = client.get("/get/internal_user_settings") + assert response.status_code == 200 + + values = response.json()["values"] + assert values["user_role"] == LitellmUserRoles.INTERNAL_USER_VIEW_ONLY, ( + f"Fresh DB should default to INTERNAL_USER_VIEW_ONLY, got {values['user_role']}. " + "The Pydantic default must match the runtime fallback." + ) + def test_update_internal_user_settings( self, mock_proxy_config, mock_auth, monkeypatch ): From c95783e6411cd3fc4ef23ae45a0e2c4c41dd2c7d Mon Sep 17 00:00:00 2001 From: ryan-crabbe <128659760+ryan-crabbe@users.noreply.github.com> Date: Sat, 14 Mar 2026 15:42:17 -0700 Subject: [PATCH 3/3] Update tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- .../proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py b/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py index 15ec901ab5e..bd9968ae936 100644 --- a/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py +++ b/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py @@ -128,12 +128,6 @@ def test_get_internal_user_settings_fresh_db_defaults_to_viewer( from litellm.proxy.proxy_server import proxy_config - monkeypatch.setattr( - proxy_config, "get_config", lambda: empty_config # sync, wrapped by endpoint - ) - - import asyncio - async def mock_get_config(): return empty_config