pingidentity / scim2

The UnboundID SCIM 2.0 SDK for Java

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

No support for removing group members

danielfosse opened this issue · comments

Patchoperation with opType remove does not save the objectNode value .
This makes it hard to support removing group members according to this:
https://help.salesforce.com/s/articleView?id=sf.identity_scim_manage_groups.htm&type=5.

I want to use PatchRequest and PatchOperation as pojos and then handle the deleting in my server, when removing the objectNode, this is not possible.

I'm experiencing a similar issue and wanted to add more details to this bug report:

Describe the bug
PATCH request for removing group member is not working

The 'value' JsonNode is missing in RemoveOperation within PatchOperation.java
The RemoveOperation should be similar to the AddOperation, which includes a 'value' JsonNode field

To Reproduce
Send a sample PATCH request for removing group member:
URL:

http://www.sampleurlsofejfose.online/scim/v2/Groups/459fb1db-9fd7-4036-ba41-35d4f3d0820e

Request body:

{"schemas":["urn:ietf:params:scim:api:messages:2.0:PatchOp"],"Operations":[{"op":"Remove","path":"members","value":[{"value":"f88d9af7-5472-4c16-bbda-bf917d9fdd4e"}]}]}

The error:

Unreconized field "value" (class com.unboundid.scim2.common.messages.PatchOperation$RemoveOperation), not marked as ignorable (one known property: "path"])

Expected behavior
PatchOperation should correctly parse the data for removing group member. If we replace the "op" to "Add", the AddOperation works fine.

Additional context

  • Java version: Java 11
  • SCIM 2 SDK version: [3.0.0, master branch]

The SCIM specification indicates in RFC 7644 section 3.5.2.2 that the remove patch operation does not have a "value" field. This is why the SCIM SDK doesn't allow you to specify this field, as it is different from add and replace operations, which do utilize a value.

However, the specification does provide a standard way to remove a group member from a group, and this is performed with the use of a value filter in the path, such as members[value eq "1e70532e-e6d8-419f-8dee-e9e71ab099d9"].

Fortunately, the SCIM SDK already supports this form of a remove operation. Let's look at an example:

// Construct the initial group and its members.
GroupResource group = new GroupResource();
group.setMembers(List.of(
    new Member().setValue("c00c399a-9d68-4180-89bf-a56297cf4366").setType("Leader"),
    new Member().setValue("1e70532e-e6d8-419f-8dee-e9e71ab099d9").setType("Occasional"),
    new Member().setValue("ba7bd3d9-de59-40d6-8cbc-c5d786fa50be").setType("Part-time")
));

// Create a patch request that will remove the "Occasional" user.
PatchRequest request = new PatchRequest(
    PatchOperation.remove("members[value eq \"1e70532e-e6d8-419f-8dee-e9e71ab099d9\"]")
);

// Apply the patch request.
GenericScimResource groupResource = group.asGenericScimResource();
request.apply(groupResource);
System.out.println(groupResource);

The print statement in the code snippet above provides the following output, indicating that the "occasional" member was successfully removed:

{
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:Group"
  ],
  "members": [
    {
      "value": "c00c399a-9d68-4180-89bf-a56297cf4366",
      "type": "Leader"
    },
    {
      "value": "ba7bd3d9-de59-40d6-8cbc-c5d786fa50be",
      "type": "Part-time"
    }
  ]
}

I think this should provide the functionality that you seek, as long as the service provider supports this type of request. If the service provider requires sending a remove operation with a value parameter (as shown in the Salesforce API documentation above), then this appears to be a direct violation of the spec.

If you need to create a request of this form to a service provider that mandates it, then I would just construct it as a raw ObjectNode. If needed, you could place this JSON data in a GenericScimResource too. For example, if you need to remove a single member, you could create a simple helper method like:

private GenericScimResource createUnorthodoxRemoveRequest(String memberID)
      throws JsonProcessingException {
  final String request = "{"
      + "  \"schemas\": ["
      + "    \"urn:ietf:params:scim:api:messages:2.0:PatchOp\""
      + "  ],"
      + "  \"Operations\": ["
      + "    {"
      + "      \"op\": \"remove\","
      + "      \"path\": \"members\","
      + "      \"value\": {"
      + "        \"members\": ["
      + "          {"
      + "            \"value\": \"" + memberID + "\""
      + "          }"
      + "        ]"
      + "      }"
      + "    }"
      + "  ]"
      + "}";

  ObjectNode node = JsonUtils.getObjectReader().forType(ObjectNode.class).readValue(request);
  return new GenericScimResource(node);
}

Of course, you could expand this to take multiple members and programmatically append them to the members array if needed. You could also consider starting with new PatchRequest(<Patch Remove operation>).asGenericScimResource(), and then modify the embedded ObjectNode to add all the value fields that you need in the members array.

@kqarryzada , thanks for replying this issue.

I checked the SCIM specification link and you are correct : The remove PATCH operation doesn't have a 'value' field. That explains the absence of the 'value' JsonNode in the RemoveOperation.

I'm developing a SCIM server to process SCIM requests from various IDP servers (Azure and Okta). It appears they implement the standard differently.

The example I provided in my post is from Azure, please find the link here:

Thank you for your code examples. I tried similar solution: I manually parse the PATCH request data, then implement the special case if it's for removing group member. The other operations are directed to the PatchRequest. The code looks massy also I had problem to convert the request data from LinkedHashMap to PatchRequest.

In the end, I duplicated the PatchRequest and PatchOperation then created the new ScimGroupPatchRequest and ScimGroupPatchOperation classes. I added the 'value' JsonNode into the RemoveOperation within ScimGroupPatchOperation. The code is now clean, and I'm able to process the remove PATCH request from Azure.

Thanks @tony-zhang-nz. It's unfortunate that Azure also follows that model.

Even though there are workarounds for this, it could be helpful for the SDK to provide some form of support for interpreting requests from SCIM service providers with improper remove operations. However, the SCIM SDK would have to be careful about the distinctions between this and the existing remove operation. I've opened an internal issue to consider this, but we haven't committed to this yet.