Skip to content

Latest commit

 

History

History
226 lines (166 loc) · 9.49 KB

File metadata and controls

226 lines (166 loc) · 9.49 KB

Tutorial

Initialization

scim2-client depends on request engines such as httpx to perform network requests. This tutorial demonstrate how to use scim2-client with httpx, and suppose you have installed the httpx extra for example with pip install scim2-client[httpx].

As a start you will need to instantiate a httpx Client object that you can parameter as your will, and then pass it to a :class:`~scim2_client.SCIMClient` object. In addition to your SCIM server root endpoint, you will probably want to provide some authorization headers through the httpx Client headers parameter:

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)

You need to give to indicate to :class:`~scim2_client.SCIMClient` all the different :class:`~scim2_models.Resource` models that you will need to manipulate, and the matching :class:`~scim2_models.ResourceType` objects to let the client know where to look for resources on the server.

You can either provision those objects manually or automatically.

Automatic provisioning

The easiest way is to let the client discover what configuration and resources are available on the server. The :meth:`~scim2_client.BaseSyncSCIMClient.discover` method looks for the server :class:`~scim2_models.ServiceProviderConfig`, :class:`~scim2_models.Schema` and :class:`~scim2_models.ResourceType` endpoints, and dynamically generate local Python models based on those schemas. They are then available to use with :meth:`~scim2_client.SCIMClient.get_resource_model`.

scim.discover()
User = scim.get_resource_model("User")
EnterpriseUser = User.get_extension_model("EnterpriseUser")

Manual provisioning

To manually register models and resource types, you can simply use the :paramref:`~scim2_client.SCIMClient.resource_models` and :paramref:`~scim2_client.SCIMClient.resource_types` arguments.

from scim2_models import User, EnterpriseUserUser, Group, ResourceType
scim = SyncSCIMClient(
    client,
    resource_models=[User[EnterpriseUser], Group],
    resource_types=[ResourceType(id="User", ...), ResourceType(id="Group", ...)],
)

Tip

If you know that all the resources are hosted at regular server endpoints (for instance /Users for :class:`~scim2_models.User` etc.), you can skip passing the :class:`~scim2_models.ResourceType` objects by hand, and simply call :meth:`~scim2_client.SCIMClient.register_naive_resource_types`.

from scim2_models import User, EnterpriseUserUser, Group, ResourceType
scim = SyncSCIMClient(
    client,
    resource_models=[User[EnterpriseUser], Group],
)
scim.register_naive_resource_types()

Performing actions

scim2-client allows your application to interact with a SCIM server as described in :rfc:`RFC7644 §3 <7644#section-3>`, so you can read and manage the resources. The following actions are available:

Have a look at the :doc:`reference` to see usage examples and the exhaustive set of parameters, but generally it looks like this:

request = User(user_name="bjensen@example.com")
response = scim.create(request)
print(f"User {response.id} has been created!")

By default, if the server returns an error, a :class:`~scim2_client.SCIMResponseErrorObject` exception is raised. The :meth:`~scim2_client.SCIMResponseErrorObject.to_error` method gives access to the :class:`~scim2_models.Error` object:

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}")

PATCH modifications

The :meth:`~scim2_client.BaseSyncSCIMClient.modify` method allows you to perform partial updates on resources using PATCH operations as defined in :rfc:`RFC7644 §3.5.2 <7644#section-3.5.2>`.

from scim2_models import PatchOp, PatchOperation

# Create a patch operation to update the display name
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")

Multiple Operations

You can include multiple operations in a single PATCH request:

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)

Patch Operation Types

SCIM supports three types of patch operations:

Bulk operations

Note

Bulk operation requests are not yet implemented, but :doc:`any help is welcome! <contributing>`

Request and response validation

By default, the data passed to the :class:`SCIM client <scim2_client.SCIMClient>` as well as the server response will be validated against the SCIM specifications, and will raise an error if they don't respect them. However sometimes you want to accept invalid inputs and outputs. To achieve this, all the methods provide the following parameters, all are :data:`True` by default:

Tip

Check the request :class:`Contexts <scim2_models.Context>` to understand which value will excluded from the request payload, and which values are expected in the response payload.

Engines

scim2-client comes with a light abstraction layers that allows for different requests engines. Currently those engines are shipped:

You can easily implement your own engine by inheriting from :class:`~scim2_client.SCIMClient`.

Additional request parameters

Any additional parameter will be passed to the underlying engine methods. This can be useful if you need to explicitly pass a certain URL for example:

scim.query(url="/User/i-know-what-im-doing")