Skip to content

Support for MS Azure requests

Pascal Knüppel edited this page Nov 2, 2023 · 6 revisions

In the last time I got several tickets about failing requests initiated by MS Azure. These requests do not follow the SCIM-specificiation but I added some workarounds to make them work. This section will shortly describe what the problem is and what was done to support these requests.

Support limitation

I added only as much workaround support as needed not as much as possible. So far I do only support these cases that were directly addressed by issues. The following sections will mention what is supported and what is not.

patch remove-requests from Azure

The patch definition for remove operations is defined here: https://datatracker.ietf.org/doc/html/rfc7644#section-3.5.2.2

MS Azure sends patch-remove requests that point to specific values with filters in the following form:

PATCH /scim/Groups/2752513

{
     "schemas": [
         "urn:ietf:params:scim:api:messages:2.0:PatchOp"
     ],
     "Operations": [
         {
             "op": "Remove",
             "path": "members",
             "value": [
                 {
                     "value": "2392066"
                 }
             ]
         }
     ]
}

This is an incorrect setup for a delete request. The correct setup would look like this:

{
     "schemas": [
         "urn:ietf:params:scim:api:messages:2.0:PatchOp"
     ],
     "Operations": [
         {
             "op": "Remove",
             "path": "members[value eq \"2392066\"]"
         }
     ]
}

The SCIM-SDK analyzes the request before it is evaluated and notices if a value is set within the remove-operation. It then rebuilds the original request to the correct representation that will eventually be executed.

multiple values in request

If the request from MS Azure contains multiple values they will be treated as if they were an or-expression filter e.g. members[value eq "1" or value eq "2"]. There is no possibilty to support and-expressions or other comparators than eq in this manner.

example:

{
     "schemas": [
         "urn:ietf:params:scim:api:messages:2.0:PatchOp"
     ],
     "Operations": [
         {
             "op": "Remove",
             "path": "members",
             "value": [
                 { "value": "1" },
                 { "value": "2" },
             ]
         }
     ]
}

evaluates to

{
     "schemas": [
         "urn:ietf:params:scim:api:messages:2.0:PatchOp"
     ],
     "Operations": [
         {
             "op": "Remove",
             "path": "members[value eq \"1\" or value eq \"2\"]"
         }
     ]
}

These are the only supported cases for remove. I did not add support for complex or multivalued attributes here.

patch resource add/replace operations

The patch operations for add and replace in the cases when the path-attribute is missing is defined in the following locations:

Ms Azure builds illegal patch requests for values placed in resource-extensions in the following form:

{
    "Operations": [
        {
            "op": "replace",
            "value": {
                ...
                "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeNumber": "1111"
            }
        }
    ],
    "schemas": [
        "urn:ietf:params:scim:api:messages:2.0:PatchOp"
    ]
}

this violates the definition of RFC7644 because the value is expected to represent the resource itself. Thus the request should look like this:

{
    "Operations": [
        {
            "op": "replace",
            "value": {
                ...
                "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
                    "employeeNumber": "1111"
                 }
            }
        }
    ],
    "schemas": [
        "urn:ietf:params:scim:api:messages:2.0:PatchOp"
    ]
}

Rebuilding of add/replace requests

The SCIM-SDK iterates over the attributes and will inject the values of the request-resource one by one into the original-resource. It will detect the attribute style definition that is used by MS Azure and will rebuild the value-node into its correct form of:

"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
   "employeeNumber": "1111"
}

Like this the SCIM-SDK can operate normally and it will add/replace the value in its correct position.

Supported cases

The support for direct resource-value insertion as MS Azure represents it is limited to resource-extensions. The following listed forms are supported. All other variations will be rejected:

  • "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeNumber": "1111"
  • "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager": {"value": "1111"}
  • "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager.value": "1111"

Support for MsAzure outer-bracket filter notation

SCIM filtering is defined here: https://datatracker.ietf.org/doc/html/rfc7644#section-3.4.2.2

MsAzure uses the following filter-notation that is not defined by SCIM:

emails[primary eq true].value eq "my@email.de"

SCIM would require this kind of expression to be expressed like one of these representations:

emails[primary eq true and value eq "my@email.de"]
of
emails.primary eq true and emails.value eq "my@email.de"

The MsAzure expression is automatically resolved to a logical and-operation to match the correct results. It is not needed to activate this workaround explicitly it is supported without doing any configurations.

Support for MsAzure nested patch-attribute-value

Patch operatios are defined here: https://datatracker.ietf.org/doc/html/rfc7644#section-3.5.2

MsAzure creates patch requests in the following style

{
   "schemas" : [ "urn:ietf:params:scim:api:messages:2.0:PatchOp" ],
   "Operations" : [ {
   "path" : "roles",
   "op" : "add",
   "value": [
            {
                "value": "{\"id\":\"827f0d2e-be15-4d8f-a8e3-f8697239c112\",\"value\":\"DocumentMgmt-Admin\",\"displayName\":\"DocumentMgmt Admin\"}"
            },
            {
                "value": "{\"id\":\"8ae06bd4-35bb-4fcd-977e-14e074ad1192\",\"value\":\"Admin\",\"displayName\":\"Admin\"}"
            }
   }]
}

this patch-request has a nested value-attribute that does not belong there. The patch operation should look like this:

{
   "schemas" : [ "urn:ietf:params:scim:api:messages:2.0:PatchOp" ],
   "Operations" : [ {
   "path" : "roles",
   "op" : "add",
   "value" : [ {
          "id":"827f0d2e-be15-4d8f-a8e3-f7697239c112",
          "value":"DocumentMgmt-BuyerAdmin",
          "displayName":"DocumentMgmt BuyerAdmin"
         },
         {
          "id":"8ae06bd4-35bb-4fcd-977e-12e074ad1192",
          "value":"Buyer-Admin",
          "displayName": "Buyer Admin"
         }]
   }]
}

You can activate a fix for this type of expression by activating a workaround that will repair this type of expression:

PatchConfig.builder().activateMsAzureValueSubAttributeWorkaround(true).build();

see also: https://github.com/Captain-P-Goldfish/SCIM-SDK/issues/516

Support for MsAzure patch-filter-expression

MsAzure may send a patch request of the following kind:

{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:PatchOp"
  ],
  "Operations": [
    {
      "op": "add",
      "path": "emails[type eq \"work\"].value",
      "value": "godfrey_stiedemann@vonruedenmueller.com"
    }
  ]
}

This request has no problems in it, but the actual problem is what MsAzure is expecting as result. MsAzure expects the email that has a type with value work to be updated with the given value. Up to this point everything is fine. But what if the attribute does not exist? MSAzure expects the attribute to be created if it does not exist. RFC7644 defines an error message for this case of type invalidPath (https://datatracker.ietf.org/doc/html/rfc7644#section-3.12). You can activate a workaround that will rebuild this patch request in a way that it is handled just as MsAzure is expecting it:

PatchConfig.builder().setMsAzureFilterWorkaroundActive(true).build();

see also: https://github.com/Captain-P-Goldfish/SCIM-SDK/issues/416

Support for MsAzure patch complex-attribute-value reference

MsAzure sends patch requests of the following style:

{
    "schemas": [
        "urn:ietf:params:scim:api:messages:2.0:PatchOp"
    ],
    "Operations": [
        {
            "op": "Add",
            "path": "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager",
            "value": "271"
        }
    ]
}

This request will fail with an error because the simple value 271 has no active target defined in the request. The correct request should look like this:

{
    "schemas": [
        "urn:ietf:params:scim:api:messages:2.0:PatchOp"
    ],
    "Operations": [
        {
            "op": "Add",
            "path": "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager.value",
            "value": "271"
        }
    ]
}

or

{
    "schemas": [
        "urn:ietf:params:scim:api:messages:2.0:PatchOp"
    ],
    "Operations": [
        {
            "op": "Add",
            "path": "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager",
            "value": {
                "value": "271"
            }
        }
    ]
}

These requests would be both valid. You can activate a workaround by configuration that is trying to repair the MsAzure patch-requests:

PatchConfig.builder().setMsAzureComplexSimpleValueWorkaroundActive(true).build();

see also: https://github.com/Captain-P-Goldfish/SCIM-SDK/issues/541