Storages#

The contract between the server and the data of an application, and the storages that keep the resources in memory.

class scim2_server.storage.ScimStorage[source]#

Where a SCIM server reads and writes its resources.

Subclass it to connect the server to a data source, such as a SQL database or a directory. The server handles the SCIM protocol, and calls these methods to read, search, create, update and delete resources. Every method receives the ResourceType it applies to, so a single storage can serve several resource types.

A storage follows these rules, which ScimStorageContract checks:

  • Every resource it returns is a copy. Changing it does not change the stored resource, and the resources it receives are not changed either.

  • It fills id, meta.resourceType, meta.created, meta.lastModified and meta.version. It leaves meta.location to the server, which knows the URLs.

  • The version changes whenever the resource changes. An update that changes nothing may keep it.

  • A resource that does not exist raises NotFoundException.

  • A value already taken by an attribute whose uniqueness is server or global raises UniquenessException.

  • It supports what the ServiceProviderConfig of the server announces, such as sorting. The server refuses the rest before calling the storage.

abstractmethod get(resource_type: ResourceType, resource_id: str) → Resource[source]#

Return a resource.

Raises:

NotFoundException – When no resource of this type has this identifier.

abstractmethod search(resource_types: list[ResourceType], search_request: SearchRequest[Any]) → tuple[int, list[Resource]][source]#

Return the number of matching resources, and one page of them.

The search request is already validated, and its count is already bounded by the maxResults of the server. The storage filters, sorts and pages the resources of every given resource type as a single collection. Several resource types mean a search at the server root.

An attribute that a resource type does not declare matches none of its resources (RFC 7644 §3.4.2.1).

Raises:
  • InvalidFilterException – When the storage cannot evaluate the filter. Filtering the page afterwards would make totalResults and the paging wrong.

  • NotImplementedException – When the storage does not support searching several resource types at once.

abstractmethod create(resource_type: ResourceType, resource: Resource) → Resource[source]#

Store a new resource, and return the stored resource.

Raises:

UniquenessException – When a unique value is taken.

abstractmethod update(resource_type: ResourceType, resource: Resource, *, expected_version: str | None = None) → Resource[source]#

Replace the stored resource that has the identifier of resource, and return the stored resource.

The server calls it for PUT and PATCH requests. It applies the request to the stored resource first, so resource is the whole new state of the resource.

Parameters:

expected_version – The version the stored resource must still have, when given.

Raises:
abstractmethod delete(resource_type: ResourceType, resource_id: str, *, expected_version: str | None = None) → None[source]#

Delete a resource.

Parameters:

expected_version – The version the stored resource must still have, when given.

Raises:
operation() → AbstractContextManager[None][source]#

Enclose one SCIM operation: a single request, or one operation of a bulk request.

It does nothing by default. A SQL storage can open a savepoint here, so that a failed operation does not prevent the next ones of a bulk request (RFC 7644 §3.7). Committing the request is left to the application.

class scim2_server.storage.AsyncScimStorage[source]#

The asynchronous variant of ScimStorage.

Its methods are coroutines, and follow the rules of ScimStorage, which AsyncScimStorageContract checks.

abstractmethod async get(resource_type: ResourceType, resource_id: str) → Resource[source]#

Return a resource. See ScimStorage.get().

abstractmethod async search(resource_types: list[ResourceType], search_request: SearchRequest[Any]) → tuple[int, list[Resource]][source]#

Return the number of matching resources, and one page of them. See ScimStorage.search().

abstractmethod async create(resource_type: ResourceType, resource: Resource) → Resource[source]#

Store a new resource. See ScimStorage.create().

abstractmethod async update(resource_type: ResourceType, resource: Resource, *, expected_version: str | None = None) → Resource[source]#

Replace a stored resource. See ScimStorage.update().

abstractmethod async delete(resource_type: ResourceType, resource_id: str, *, expected_version: str | None = None) → None[source]#

Delete a resource. See ScimStorage.delete().

operation() → AbstractAsyncContextManager[None][source]#

Enclose one SCIM operation. See ScimStorage.operation().

class scim2_server.memory.InMemoryStorage(clock: Callable[[], ~datetime.datetime]=<function utcnow>)[source]#

A storage keeping the resources in memory, for tests, debugging and demos.

It is not optimized for performance: a search walks every resource. A lock serializes every access, so it can serve a threaded server.

Parameters:

clock – Return the date of a write, for meta.created and meta.lastModified. Pass a fixed clock to get predictable dates.

generate_id(resource_type: ResourceType, resource: Resource) → str[source]#

Return the identifier of a new resource.

Override this method to get predictable identifiers.

next_version() → str[source]#

Return the version of a new write.

Versions come from a counter, so two writes never share a version, even within the same microsecond.

operation() → Generator[None][source]#

Hold the lock for the whole operation.

class scim2_server.memory.AsyncInMemoryStorage(storage: InMemoryStorage | None = None)[source]#

The asynchronous variant of InMemoryStorage.

It serves the resources of an InMemoryStorage. Every call runs without awaiting anything, so a call is never interrupted by another coroutine. operation() takes no lock: holding the lock of the storage across an await would block the event loop. Two concurrent updates of a resource are still told apart by expected_version.

Parameters:

storage – The storage to serve. Pass a subclass of InMemoryStorage to change how identifiers are generated.

property resources: list[Resource]#

The stored resources.