### Install scim2-client with httpx support Source: https://github.com/python-scim/scim2-client/blob/main/README.md Install the library using pip with the httpx extra for network request capabilities. ```shell pip install scim2-client[httpx] ``` -------------------------------- ### Query SCIM Resources Source: https://context7.com/python-scim/scim2-client/llms.txt Examples of querying all users, using search filters, and retrieving service provider configurations. ```python all_users = scim.query(User) print(f"Total users: {all_users.total_results}") for user in all_users.resources: print(f" - {user.user_name}") # Query with SCIM filter, sorting, and pagination search = SearchRequest( filter='userName sw "john"', sort_by="userName", sort_order="ascending", start_index=1, count=10, attributes=["userName", "displayName", "emails"], ) filtered_users = scim.query(User, search_request=search) print(f"Found {filtered_users.total_results} users matching filter") # Query ServiceProviderConfig (returns single object, not ListResponse) config = scim.query(scim.get_resource_model("ServiceProviderConfig")) print(f"Patch supported: {config.patch.supported}") ``` -------------------------------- ### Initialize SyncSCIMClient Source: https://context7.com/python-scim/scim2-client/llms.txt Set up a synchronous SCIM client using an httpx Client instance. ```python from httpx import Client from scim2_client.engines.httpx import SyncSCIMClient # Initialize the httpx client with base URL and authentication client = Client( base_url="https://auth.example/scim/v2", headers={"Authorization": "Bearer your-access-token"}, ) # Create the SCIM client wrapper scim = SyncSCIMClient(client) # Discover server configuration and available resource types scim.discover() # Get dynamically generated resource models User = scim.get_resource_model("User") Group = scim.get_resource_model("Group") EnterpriseUser = User.get_extension_model("EnterpriseUser") print(f"Discovered User model: {User}") print(f"Service provider config: {scim.service_provider_config}") ``` -------------------------------- ### Initialize SyncSCIMClient with httpx Source: https://github.com/python-scim/scim2-client/blob/main/doc/tutorial.rst Instantiate a httpx Client with base URL and headers, then pass it to the SyncSCIMClient. ```python from httpx import Client from scim2_client.engines.httpx import SyncSCIMClient client = Client( base_url="https://auth.example/scim/v2", headers={"Authorization": "Bearer foobar"}, ) scim = SyncSCIMClient(client) ``` -------------------------------- ### Initialize AsyncSCIMClient Source: https://context7.com/python-scim/scim2-client/llms.txt Set up an asynchronous SCIM client using an httpx AsyncClient instance. ```python import asyncio from httpx import AsyncClient from scim2_client.engines.httpx import AsyncSCIMClient async def main(): async with AsyncClient( base_url="https://auth.example/scim/v2", headers={"Authorization": "Bearer your-access-token"}, ) as client: scim = AsyncSCIMClient(client) # Discover server configuration asynchronously await scim.discover() User = scim.get_resource_model("User") # Create a user asynchronously new_user = User(user_name="async_user@example.com") created_user = await scim.create(new_user) print(f"Created user: {created_user.id}") # Query users asynchronously users = await scim.query(User) print(f"Total users: {users.total_results}") asyncio.run(main()) ``` -------------------------------- ### Perform SCIM operations with SyncSCIMClient Source: https://github.com/python-scim/scim2-client/blob/main/README.md Demonstrates querying, updating, and creating resources using the synchronous httpx-based client. ```python import datetime from httpx import Client from scim2_client import SCIMResponseErrorObject from scim2_client.engines.httpx import SyncSCIMClient client = Client(base_url="https://auth.example/scim/v2", headers={"Authorization": "Bearer foobar"}) scim = SyncSCIMClient(client) scim.discover() User = scim.get_resource_model("User") # Query resources user = scim.query(User, "2819c223-7f76-453a-919d-413861904646") assert user.user_name == "bjensen@example.com" assert user.meta.last_modified == datetime.datetime( 2024, 4, 13, 12, 0, 0, tzinfo=datetime.timezone.utc ) # Update resources user.display_name = "Babs Jensen" user = scim.replace(user) assert user.display_name == "Babs Jensen" assert user.meta.last_modified == datetime.datetime( 2024, 4, 13, 12, 0, 30, tzinfo=datetime.timezone.utc ) # Create resources user = User(user_name="bjensen@example.com") try: scim.create(user) except SCIMResponseErrorObject as exc: error = exc.to_error() assert error.detail == "One or more of the attribute values are already in use or are reserved." ``` -------------------------------- ### Perform Server Discovery with SyncSCIMClient Source: https://context7.com/python-scim/scim2-client/llms.txt Automatically retrieve server configuration and generate models by querying standard SCIM endpoints. ```python from httpx import Client from scim2_client.engines.httpx import SyncSCIMClient client = Client( base_url="https://auth.example/scim/v2", headers={"Authorization": "Bearer token"}, ) scim = SyncSCIMClient(client) # Full discovery: schemas, resource types, and service provider config scim.discover() # Access discovered configuration print(f"PATCH supported: {scim.service_provider_config.patch.supported}") print(f"Filter supported: {scim.service_provider_config.filter.supported}") print(f"Max results: {scim.service_provider_config.filter.max_results}") # Get available resource models User = scim.get_resource_model("User") Group = scim.get_resource_model("Group") # Get extension models (e.g., EnterpriseUser) EnterpriseUser = User.get_extension_model("EnterpriseUser") # Partial discovery (skip certain endpoints) scim2 = SyncSCIMClient(client) scim2.discover(service_provider_config=False) # Skip SPC endpoint ``` -------------------------------- ### Configure validation options in scim2-client Source: https://context7.com/python-scim/scim2-client/llms.txt Shows how to set global validation defaults during client initialization and how to override these settings for individual requests. ```python from httpx import Client from scim2_client.engines.httpx import SyncSCIMClient client = Client( base_url="https://auth.example/scim/v2", headers={"Authorization": "Bearer token"}, ) # Configure validation at client initialization scim = SyncSCIMClient( client, check_request_payload=True, # Validate outgoing requests check_response_payload=True, # Validate incoming responses check_response_content_type=True, # Check Content-Type headers check_response_status_codes=True, # Check expected status codes raise_scim_errors=True, # Raise exceptions on SCIM errors ) scim.discover() User = scim.get_resource_model("User") # Override validation per-request # Send raw dict without validation raw_response = scim.create( {"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], "userName": "test@example.com"}, check_request_payload=False, # Don't validate the request check_response_payload=False, # Return raw dict response ) print(f"Raw response: {raw_response}") # Accept any status code response = scim.query( User, "some-id", expected_status_codes=None, # Accept any status code ) # Get error objects instead of exceptions response = scim.create( User(user_name="test@example.com"), raise_scim_errors=False, # Return Error object instead of raising ) ``` -------------------------------- ### Perform resource creation Source: https://github.com/python-scim/scim2-client/blob/main/doc/tutorial.rst Create a new resource on the SCIM server using the create method. ```python request = User(user_name="bjensen@example.com") response = scim.create(request) print(f"User {response.id} has been created!") ``` -------------------------------- ### Dynamically discover models from the server Source: https://github.com/python-scim/scim2-client/blob/main/doc/tutorial.rst Use the discover method to automatically generate local Python models based on server configuration. ```python scim.discover() User = scim.get_resource_model("User") EnterpriseUser = User.get_extension_model("EnterpriseUser") ``` -------------------------------- ### Create SCIM Resources Source: https://context7.com/python-scim/scim2-client/llms.txt Create a new resource using the create method, which includes automatic payload validation. ```python from httpx import Client from scim2_client.engines.httpx import SyncSCIMClient from scim2_client import SCIMResponseErrorObject client = Client( base_url="https://auth.example/scim/v2", headers={"Authorization": "Bearer token"}, ) scim = SyncSCIMClient(client) scim.discover() User = scim.get_resource_model("User") # Create a new user with basic attributes new_user = User( user_name="bjensen@example.com", name={"givenName": "Barbara", "familyName": "Jensen"}, emails=[{"value": "bjensen@example.com", "primary": True}], active=True, ) try: created_user = scim.create(new_user) print(f"User created with ID: {created_user.id}") print(f"Username: {created_user.user_name}") print(f"Created at: {created_user.meta.created}") except SCIMResponseErrorObject as exc: error = exc.to_error() print(f"Error [{error.status}]: {error.detail}") # Example: "Error [409]: One or more attribute values are already in use." ``` -------------------------------- ### Manually register models and resource types Source: https://github.com/python-scim/scim2-client/blob/main/doc/tutorial.rst Register models and resource types explicitly during client initialization. ```python from scim2_models import User, EnterpriseUserUser, Group, ResourceType scim = SyncSCIMClient( client, resource_models=[User[EnterpriseUser], Group], resource_types=[ResourceType(id="User", ...), ResourceType(id="Group", ...)], ) ``` ```python from scim2_models import User, EnterpriseUserUser, Group, ResourceType scim = SyncSCIMClient( client, resource_models=[User[EnterpriseUser], Group], ) scim.register_naive_resource_types() ``` -------------------------------- ### Test SCIM Server with TestSCIMClient Source: https://context7.com/python-scim/scim2-client/llms.txt Use the Werkzeug test client to execute requests against a WSGI application without making real HTTP calls. ```python from scim2_client.engines.werkzeug import TestSCIMClient from scim2_models import User, Group from werkzeug.test import Client # Assuming you have a Flask/Werkzeug SCIM server app from myapp import create_scim_app app = create_scim_app() werkzeug_client = Client(app) # Create test client with models pre-registered scim = TestSCIMClient( client=werkzeug_client, scim_prefix="/scim/v2", resource_models=[User, Group], ) scim.register_naive_resource_types() # Test creating a user request_user = User(user_name="testuser@example.com", display_name="Test User") response_user = scim.create(request_user) assert response_user.user_name == "testuser@example.com" assert response_user.id is not None # Test querying queried_user = scim.query(User, response_user.id) assert queried_user.display_name == "Test User" # Test deletion result = scim.delete(User, response_user.id) assert result is None # Successful deletion ``` -------------------------------- ### Perform POST-based Search with SyncSCIMClient Source: https://context7.com/python-scim/scim2-client/llms.txt Use the search method to perform POST requests to the /.search endpoint, which is ideal for long search queries. ```python from httpx import Client from scim2_client.engines.httpx import SyncSCIMClient from scim2_models import SearchRequest client = Client( base_url="https://auth.example/scim/v2", headers={"Authorization": "Bearer token"}, ) scim = SyncSCIMClient(client) scim.discover() # Search across all resource types search_request = SearchRequest( filter='id co "admin"', start_index=1, count=50, ) results = scim.search(search_request=search_request) print(f"Found {results.total_results} resources") for resource in results.resources: print(f" - {resource.__class__.__name__}: {resource.id}") ``` -------------------------------- ### Handle SCIM protocol errors with Python Source: https://context7.com/python-scim/scim2-client/llms.txt Demonstrates catching various SCIM-specific exceptions including network, validation, and server-side errors. It also shows how to disable automatic exception raising to receive error objects instead. ```python from httpx import Client from scim2_client.engines.httpx import SyncSCIMClient from scim2_client import ( SCIMClientError, SCIMRequestError, SCIMResponseError, SCIMResponseErrorObject, RequestNetworkError, RequestPayloadValidationError, ResponsePayloadValidationError, UnexpectedStatusCode, UnexpectedContentType, UnexpectedContentFormat, ) client = Client( base_url="https://auth.example/scim/v2", headers={"Authorization": "Bearer token"}, ) scim = SyncSCIMClient(client) scim.discover() User = scim.get_resource_model("User") # Handle SCIM server errors try: user = User(user_name="existing@example.com") scim.create(user) except SCIMResponseErrorObject as exc: error = exc.to_error() print(f"SCIM Error [{error.status}] {error.scim_type}: {error.detail}") # Handle request payload validation errors try: scim.create({"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], "active": "not-a-bool"}) except RequestPayloadValidationError as exc: print(f"Invalid request payload: {exc}") print(f"Validation details: {exc.__cause__}") # Handle response validation errors try: scim.query(User, "some-id") except ResponsePayloadValidationError as exc: print(f"Invalid server response: {exc}") # Handle network errors try: scim.query(User) except RequestNetworkError as exc: print(f"Network error: {exc}") print(f"Original error: {exc.__cause__}") # Handle unexpected status codes try: scim.query(User, "nonexistent-id") except UnexpectedStatusCode as exc: print(f"Unexpected status: {exc}") # Disable error raising to get error objects instead response = scim.create( User(user_name="test@example.com"), raise_scim_errors=False, ) if hasattr(response, 'status'): # It's an Error object print(f"Got error: {response.detail}") else: print(f"Created user: {response.id}") ``` -------------------------------- ### SCIM2 Client Engines Source: https://github.com/python-scim/scim2-client/blob/main/doc/reference.rst Overview of the available engine implementations for the SCIM2 client, specifically focusing on HTTPX and Werkzeug integration. ```APIDOC ## SCIM2 Client Engines ### Description The scim2-client library provides modular engine support to facilitate communication with SCIM 2.0 service providers. Currently, it supports HTTPX and Werkzeug engines. ### Engines - **scim2_client.engines.httpx**: Provides an asynchronous or synchronous client implementation using the httpx library. - **scim2_client.engines.werkzeug**: Provides a client implementation utilizing Werkzeug utilities for request/response handling. ### Usage Users should initialize the client by selecting the appropriate engine based on their application's requirements (e.g., async support via httpx). ``` -------------------------------- ### Query SCIM Resources Source: https://context7.com/python-scim/scim2-client/llms.txt Retrieve resources using the query method, supporting single resource lookup by ID. ```python from httpx import Client from scim2_client.engines.httpx import SyncSCIMClient from scim2_models import SearchRequest client = Client( base_url="https://auth.example/scim/v2", headers={"Authorization": "Bearer token"}, ) scim = SyncSCIMClient(client) scim.discover() User = scim.get_resource_model("User") # Query a single user by ID user = scim.query(User, "2819c223-7f76-453a-919d-413861904646") print(f"User: {user.user_name}") print(f"Display name: {user.display_name}") print(f"Last modified: {user.meta.last_modified}") ``` -------------------------------- ### Manually Register Resource Models Source: https://context7.com/python-scim/scim2-client/llms.txt Register models and types manually when server discovery is unavailable or not desired. ```python from httpx import Client from scim2_client.engines.httpx import SyncSCIMClient from scim2_models import User, Group, EnterpriseUser, ResourceType client = Client( base_url="https://auth.example/scim/v2", headers={"Authorization": "Bearer token"}, ) # Manual registration with explicit resource types scim = SyncSCIMClient( client, resource_models=[User[EnterpriseUser], Group], resource_types=[ ResourceType( id="User", name="User", endpoint="/Users", schema_="urn:ietf:params:scim:schemas:core:2.0:User", ), ResourceType( id="Group", name="Group", endpoint="/Groups", schema_="urn:ietf:params:scim:schemas:core:2.0:Group", ), ], ) # Or use naive registration (assumes standard endpoints like /Users, /Groups) scim2 = SyncSCIMClient( client, resource_models=[User, Group], ) scim2.register_naive_resource_types() # Now you can use the client user = User(user_name="test@example.com") created = scim2.create(user) ``` -------------------------------- ### Replace SCIM Resources Source: https://context7.com/python-scim/scim2-client/llms.txt Performs a PUT request to completely replace an existing resource. The resource object must have an ID set. ```python from httpx import Client from scim2_client.engines.httpx import SyncSCIMClient client = Client( base_url="https://auth.example/scim/v2", headers={"Authorization": "Bearer token"}, ) scim = SyncSCIMClient(client) scim.discover() User = scim.get_resource_model("User") # First, query the existing user user = scim.query(User, "2819c223-7f76-453a-919d-413861904646") print(f"Original display name: {user.display_name}") # Modify the user object user.display_name = "Barbara Jensen (Updated)" user.title = "Senior Engineer" user.emails = [ {"value": "bjensen@example.com", "primary": True, "type": "work"}, {"value": "babs@personal.com", "primary": False, "type": "home"}, ] # Replace the resource on the server updated_user = scim.replace(user) print(f"Updated display name: {updated_user.display_name}") print(f"New modification time: {updated_user.meta.last_modified}") ``` -------------------------------- ### Pass additional request parameters Source: https://github.com/python-scim/scim2-client/blob/main/doc/tutorial.rst Allows passing custom parameters like explicit URLs directly to the underlying engine. ```python scim.query(url="/User/i-know-what-im-doing") ``` -------------------------------- ### Perform PATCH modifications Source: https://github.com/python-scim/scim2-client/blob/main/doc/tutorial.rst Use the modify method to perform partial updates on resources. ```python from scim2_models import PatchOp, PatchOperation # Create a patch operation to update the display name ``` -------------------------------- ### Perform multiple PATCH operations Source: https://github.com/python-scim/scim2-client/blob/main/doc/tutorial.rst Groups several operations into a single PATCH request. ```python operations = [ PatchOperation( op=PatchOperation.Op.replace_, path="displayName", value="Updated Name" ), PatchOperation( op=PatchOperation.Op.replace_, path="active", value=False ), PatchOperation( op=PatchOperation.Op.add, path="emails", value=[{"value": "new@example.com", "primary": True}] ) ] patch_op = PatchOp[User](operations=operations) response = scim.modify(User, user_id, patch_op) ``` -------------------------------- ### Modify SCIM Resources via PATCH Source: https://context7.com/python-scim/scim2-client/llms.txt Performs partial updates using PATCH requests. Supports add, remove, and replace operations. ```python from httpx import Client from scim2_client.engines.httpx import SyncSCIMClient from scim2_models import PatchOp, PatchOperation client = Client( base_url="https://auth.example/scim/v2", headers={"Authorization": "Bearer token"}, ) scim = SyncSCIMClient(client) scim.discover() User = scim.get_resource_model("User") user_id = "2819c223-7f76-453a-919d-413861904646" # Single operation: replace display name operation = PatchOperation( op=PatchOperation.Op.replace_, path="displayName", value="New Display Name" ) patch_op = PatchOp[User](operations=[operation]) response = scim.modify(User, user_id, patch_op) if response: # Server returned 200 with updated resource print(f"Updated user: {response.display_name}") else: # Server returned 204 (no content) print("User updated successfully") # Multiple operations in a single PATCH request operations = [ PatchOperation( op=PatchOperation.Op.replace_, path="displayName", value="Barbara Jensen" ), PatchOperation( op=PatchOperation.Op.replace_, path="active", value=False ), PatchOperation( op=PatchOperation.Op.add, path="emails", value=[{"value": "new@example.com", "primary": False}] ), PatchOperation( op=PatchOperation.Op.remove, path='emails[type eq "home"]' ), ] patch_op = PatchOp[User](operations=operations) response = scim.modify(User, user_id, patch_op) ``` -------------------------------- ### Handle SCIM response errors Source: https://github.com/python-scim/scim2-client/blob/main/doc/tutorial.rst Catch SCIMResponseErrorObject exceptions to access detailed error information from the server. ```python from scim2_client import SCIMResponseErrorObject try: response = scim.create(request) except SCIMResponseErrorObject as exc: error = exc.to_error() print(f"SCIM error [{error.status}] {error.scim_type}: {error.detail}") ``` -------------------------------- ### Delete SCIM Resources Source: https://context7.com/python-scim/scim2-client/llms.txt Performs a DELETE request to remove a resource. Handles potential errors using SCIMResponseErrorObject. ```python from httpx import Client from scim2_client.engines.httpx import SyncSCIMClient from scim2_client import SCIMResponseErrorObject client = Client( base_url="https://auth.example/scim/v2", headers={"Authorization": "Bearer token"}, ) scim = SyncSCIMClient(client) scim.discover() User = scim.get_resource_model("User") Group = scim.get_resource_model("Group") # Delete a user by ID try: result = scim.delete(User, "2819c223-7f76-453a-919d-413861904646") if result is None: print("User deleted successfully") except SCIMResponseErrorObject as exc: error = exc.to_error() print(f"Failed to delete: {error.detail}") # Delete a group try: scim.delete(Group, "e9e30dba-f08f-4109-8486-d5c6a331660a") print("Group deleted successfully") except SCIMResponseErrorObject as exc: error = exc.to_error() if error.status == "404": print("Group not found") else: print(f"Error: {error.detail}") ``` -------------------------------- ### Perform a single PATCH operation Source: https://github.com/python-scim/scim2-client/blob/main/doc/tutorial.rst Updates a resource attribute using the modify method. ```python operation = PatchOperation( op=PatchOperation.Op.replace_, path="displayName", value="New Display Name" ) patch_op = PatchOp[User](operations=[operation]) # Apply the patch response = scim.modify(User, user_id, patch_op) if response: # Server returned 200 with updated resource print(f"User updated: {response.display_name}") else: # Server returned 204 (no content) print("User updated successfully") ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.