This document details how the SCIM component processes patch requests, and the commands it generates to be passed into the PartialUpdate method on IScimStore.
Patching is by far the most challenging part of any SCIM store implementation. The most straightforward approach is to load the resource into memory, apply the CRUD commands, and then persist the resource. There are, however, some situations where this will not be possible. Consider a group called employees that contains 100K+ users and a patch command that removes user Fred. Loading the entire group membership into memory to remove a single item will be very inefficient. What is required here is mapping the patch command onto a specific underlying store operation, e.g. executing a SQL DELETE command, or if using an Object Relational Mapper (ORM) such as EF Core, only load the user Fred into the group membership, prior to removing Fred.
The SCIM component provides an In-Memory patching service that automates your store's patching process. See below for further details.
Patch Requests
For your store to be able to handle patch requests, you will need to process a collection of PatchCommands. The PatchCommand object will contain the type of operation that it represents, either Add, Remove or Replace. For Add and Replace commands, there will be a Value property that contains the value to be used in the operation and the Path property, which represents the property that needs to be modified on the resource.
In-Memory Patching
The SCIM component provides an out-of-the-box solution to patching resources in-memory by implementing the IPatchCommandExecutor interface. The implementation attempts to modify your target object using a given patch command, using the mappings created when calling MapScimAttributes. To configure the IPatchCommandExecutor, use the extension methods described in Mapping Attributes.
To use the implementation, inject the IPatchCommandExecutor interface into your store and call Execute, passing a patch command and an instance of the target object. A ScimStorePatchException is thrown if a call to Execute is unsuccessful.
foreach (PatchCommand command in commands)
{
try
{
patchCommandExecutor.Execute(user, command);
}
catch (ScimStorePatchException)
{
logger.LogError("Failed to patch user with id {id} with command {cmd}", resourceId, command);
}
}
If you're implementing a Microsoft Entra compatible store and have set the EnableAzureAdCompatibility to true, the IPatchCommandExecutor will behave differently for add operations containing a filter. See the Microsoft Entra integration documentation for more details.
Patch Operation Paths
The Path in a PatchCommand is a PathExpression, containing a collection of AttributePathExpression that makes up the path of the property the patch command is operating against. By default, the SCIM component will output patch commands that always have the Path property set, even if the incoming request omits it. This is to avoid having implementers of PartialUpdate implement two paths for modifying a resource.
Default Behaviour
{
"op": "replace",
"value": {
"name.givenName": "newGivenName"
}
}
{
"op": "replace",
"path": "name",
"value": {
"givenName": "newGivenName"
}
}
Will both produce a PatchCommand that can represented like the following
{
"path": "urn:ietf:params:scim:schemas:core:2.0:User:name:givenName"
"value": "newGivenName"
}
Whole Object Replacements
To change this behaviour, set the EnableWholeObjectPatchCommands flag to true in the ScimServiceProviderConfigOptions object passed into AddScimServiceProvider. When set to true, patch requests without a path will generate a PatchCommand who's Value property is an instance of the Resource with the relevant properties set. As an example, the following replace command:
{
"op": "replace",
"value": {
...
"name": {
...
"familyName": "family"
...
},
"userName": "userName"
...
}
}
Will output a PatchCommand ressembling the following representation:
{
"op": "replace",
"value": {
...
"name": {
...
"familyName": "family"
...
},
"userName": "userName"
...
}
}
When you receive this PatchCommand you will have to check which properties have been updated on the Value object and modify the Resource using these.