[Bug Fix] SCIM - Ensure new user roles are applied (#12015)

* SCIM fix new user roles

* test_create_user_defaults_to_viewer

* test_create_user_uses_default_internal_user_params_role

* fix default user for SCIM

* fix linting error
This commit is contained in:
Ishaan Jaff 2025-06-24 13:22:56 -07:00 • committed by GitHub
parent a5ce1cd49b
commit b9feb43dac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 103 additions and 1 deletions

View file

@ -18,6 +18,7 @@ from fastapi import (
Response,
)
import litellm
from litellm._logging import verbose_proxy_logger
from litellm.litellm_core_utils.safe_json_dumps import safe_dumps
from litellm.proxy._types import (
@ -361,6 +362,18 @@ async def create_user(
# Create user in database
user_id = user.userName or str(uuid.uuid4())
metadata = _build_scim_metadata(user_data["given_name"], user_data["family_name"])
default_role: Optional[
Literal[
LitellmUserRoles.PROXY_ADMIN,
LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY,
LitellmUserRoles.INTERNAL_USER,
LitellmUserRoles.INTERNAL_USER_VIEW_ONLY,
]
] = LitellmUserRoles.INTERNAL_USER_VIEW_ONLY
if litellm.default_internal_user_params:
default_role = litellm.default_internal_user_params.get("user_role")
new_user_request = NewUserRequest(
user_id=user_id,
user_email=user_data["user_email"],
@ -368,6 +381,7 @@ async def create_user(
teams=user_data["teams"],
metadata=metadata,
auto_create_key=False,
user_role=default_role,
)
# Check if user with email already exists and update if found

View file

@ -3,7 +3,7 @@ from unittest.mock import AsyncMock
import pytest
from fastapi import HTTPException
from litellm.proxy._types import NewUserRequest, ProxyException
from litellm.proxy._types import LitellmUserRoles, NewUserRequest, ProxyException
from litellm.proxy.management_endpoints.scim.scim_v2 import (
UserProvisionerHelpers,
_handle_team_membership_changes,
@ -58,6 +58,94 @@ async def test_create_user_existing_user_conflict(mocker):
mocked_new_user.assert_not_called()
@pytest.mark.asyncio
async def test_create_user_defaults_to_viewer(mocker, monkeypatch):
"""If no role provided, new user should default to viewer"""
scim_user = SCIMUser(
schemas=["urn:ietf:params:scim:schemas:core:2.0:User"],
userName="new-user",
name=SCIMUserName(familyName="User", givenName="New"),
emails=[SCIMUserEmail(value="new@example.com")],
)
mock_prisma_client = mocker.MagicMock()
mock_prisma_client.db = mocker.MagicMock()
mock_prisma_client.db.litellm_usertable = mocker.MagicMock()
mock_prisma_client.db.litellm_usertable.find_unique = AsyncMock(return_value=None)
mock_prisma_client.db.litellm_usertable.find_first = AsyncMock(return_value=None)
monkeypatch.setattr(
"litellm.default_internal_user_params", None, raising=False
)
mocker.patch(
"litellm.proxy.management_endpoints.scim.scim_v2._get_prisma_client_or_raise_exception",
AsyncMock(return_value=mock_prisma_client),
)
new_user_mock = mocker.patch(
"litellm.proxy.management_endpoints.scim.scim_v2.new_user",
AsyncMock(return_value=NewUserRequest(user_id="new-user")),
)
mocker.patch(
"litellm.proxy.management_endpoints.scim.scim_v2.ScimTransformations.transform_litellm_user_to_scim_user",
AsyncMock(return_value=scim_user),
)
await create_user(user=scim_user)
called_args = new_user_mock.call_args.kwargs["data"]
assert called_args.user_role == LitellmUserRoles.INTERNAL_USER_VIEW_ONLY
@pytest.mark.asyncio
async def test_create_user_uses_default_internal_user_params_role(mocker, monkeypatch):
"""If role is set in default_internal_user_params, new user should use that role"""
scim_user = SCIMUser(
schemas=["urn:ietf:params:scim:schemas:core:2.0:User"],
userName="new-user",
name=SCIMUserName(familyName="User", givenName="New"),
emails=[SCIMUserEmail(value="new@example.com")],
)
mock_prisma_client = mocker.MagicMock()
mock_prisma_client.db = mocker.MagicMock()
mock_prisma_client.db.litellm_usertable = mocker.MagicMock()
mock_prisma_client.db.litellm_usertable.find_unique = AsyncMock(return_value=None)
mock_prisma_client.db.litellm_usertable.find_first = AsyncMock(return_value=None)
# Set default_internal_user_params with a specific role
default_params = {
"user_role": LitellmUserRoles.PROXY_ADMIN,
}
monkeypatch.setattr(
"litellm.default_internal_user_params", default_params, raising=False
)
mocker.patch(
"litellm.proxy.management_endpoints.scim.scim_v2._get_prisma_client_or_raise_exception",
AsyncMock(return_value=mock_prisma_client),
)
new_user_mock = mocker.patch(
"litellm.proxy.management_endpoints.scim.scim_v2.new_user",
AsyncMock(return_value=NewUserRequest(user_id="new-user")),
)
mocker.patch(
"litellm.proxy.management_endpoints.scim.scim_v2.ScimTransformations.transform_litellm_user_to_scim_user",
AsyncMock(return_value=scim_user),
)
await create_user(user=scim_user)
called_args = new_user_mock.call_args.kwargs["data"]
assert called_args.user_role == LitellmUserRoles.PROXY_ADMIN
@pytest.mark.asyncio
async def test_handle_existing_user_by_email_no_email(mocker):
"""Should return None when new_user_request has no email"""