From 43e7d5288f9adfeef2c694e6dba476008d9a3465 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 16 Jul 2025 15:43:25 -0700 Subject: [PATCH] [Bug Fix] SCIM - add GET /ServiceProviderConfig (#12664) * add scimv2 config endpoint * test_get_service_provider_config --- .../management_endpoints/scim/scim_v2.py | 15 +++++++++++++ .../proxy/management_endpoints/scim_v2.py | 22 +++++++++++++++++++ .../scim/test_scim_v2_endpoints.py | 22 +++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/litellm/proxy/management_endpoints/scim/scim_v2.py b/litellm/proxy/management_endpoints/scim/scim_v2.py index 1ef4cdc974d..221ddb9688d 100644 --- a/litellm/proxy/management_endpoints/scim/scim_v2.py +++ b/litellm/proxy/management_endpoints/scim/scim_v2.py @@ -245,6 +245,21 @@ async def set_scim_content_type(response: Response): response.headers["Content-Type"] = "application/scim+json" +@scim_router.get( + "/ServiceProviderConfig", + response_model=SCIMServiceProviderConfig, + status_code=200, + dependencies=[Depends(user_api_key_auth), Depends(set_scim_content_type)], +) +async def get_service_provider_config(request: Request): + """Return SCIM Service Provider Configuration.""" + meta = { + "resourceType": "ServiceProviderConfig", + "location": str(request.url), + } + return SCIMServiceProviderConfig(meta=meta) + + # User Endpoints @scim_router.get( "/Users", diff --git a/litellm/types/proxy/management_endpoints/scim_v2.py b/litellm/types/proxy/management_endpoints/scim_v2.py index f97553f3714..bff9f0b876c 100644 --- a/litellm/types/proxy/management_endpoints/scim_v2.py +++ b/litellm/types/proxy/management_endpoints/scim_v2.py @@ -90,3 +90,25 @@ class SCIMPatchOperation(BaseModel): class SCIMPatchOp(BaseModel): schemas: List[str] = ["urn:ietf:params:scim:api:messages:2.0:PatchOp"] Operations: List[SCIMPatchOperation] + + +# SCIM Service Provider Configuration Models +class SCIMFeature(BaseModel): + supported: bool + maxOperations: Optional[int] = None + maxPayloadSize: Optional[int] = None + maxResults: Optional[int] = None + + +class SCIMServiceProviderConfig(BaseModel): + schemas: List[str] = [ + "urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig" + ] + patch: SCIMFeature = SCIMFeature(supported=True) + bulk: SCIMFeature = SCIMFeature(supported=False) + filter: SCIMFeature = SCIMFeature(supported=False) + changePassword: SCIMFeature = SCIMFeature(supported=False) + sort: SCIMFeature = SCIMFeature(supported=False) + etag: SCIMFeature = SCIMFeature(supported=False) + authenticationSchemes: Optional[List[Dict[str, Any]]] = None + meta: Optional[Dict[str, Any]] = None diff --git a/tests/test_litellm/proxy/management_endpoints/scim/test_scim_v2_endpoints.py b/tests/test_litellm/proxy/management_endpoints/scim/test_scim_v2_endpoints.py index f93728b90c3..274ee8b77f0 100644 --- a/tests/test_litellm/proxy/management_endpoints/scim/test_scim_v2_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/scim/test_scim_v2_endpoints.py @@ -8,12 +8,15 @@ from litellm.proxy.management_endpoints.scim.scim_v2 import ( UserProvisionerHelpers, _handle_team_membership_changes, create_user, + get_service_provider_config, patch_user, update_user, ) from litellm.types.proxy.management_endpoints.scim_v2 import ( + SCIMFeature, SCIMPatchOp, SCIMPatchOperation, + SCIMServiceProviderConfig, SCIMUser, SCIMUserEmail, SCIMUserGroup, @@ -557,3 +560,22 @@ async def test_patch_user_not_found(mocker): # Should raise ProxyException (which wraps the HTTPException) with pytest.raises(ProxyException): await patch_user(user_id="nonexistent-user", patch_ops=patch_ops) + + +@pytest.mark.asyncio +async def test_get_service_provider_config(mocker): + """Test the get_service_provider_config endpoint""" + # Mock the Request object + mock_request = mocker.MagicMock() + mock_request.url = "https://example.com/scim/v2/ServiceProviderConfig" + + # Call the endpoint + result = await get_service_provider_config(mock_request) + + # Verify it returns the correct response + assert isinstance(result, SCIMServiceProviderConfig) + assert result.schemas == ["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"] + assert result.patch.supported is True + assert result.bulk.supported is False + assert result.meta is not None + assert result.meta["resourceType"] == "ServiceProviderConfig"