cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to update objects in IdentityNow via the REST API with JSONPatch

How to update objects in IdentityNow via the REST API with JSONPatch

If you need to update objects, such as sources, account schemas, identity profiles, provisioning policies, etc., in IdentityNow, some updates can be made in the IdentityNow user interface.   Other types of updates can only be made via the REST API.   A HTTP PUT call to the appropriate API endpoint can be used to make updates, but the entire JSON source definition is required in the request body for PUT calls.  There is an alternative way to update objects if you only need to update one or a few attributes, and that is using the HTTP PATCH method call with a request body that only includes the necessary updates.  The request body is written in JSONPatch format which requires very specific syntax.  Let's explore a few examples.  It is assumed that we are using Postman, cURL, or a similar API client to perform all API calls with a valid JSON Web Token generated with org admin credentials.

We have an OpenLDAP source, Directory, and we want to increase the amount of time the system waits for a response when doing source health checks.  First, we need to get the current source definition.  We use GET /v3/sources/{sourceID} to get the full JSON source definition:

{
    "description": "Directory",
    "owner": {
        "type": "IDENTITY",
        "id": "2c91808a761f515001762e9190731c7f",
        "name": "SailPoint Services"
    },
    "cluster": {
        "type": "CLUSTER",
        "id": "2c9180887625931601762f24d64a1925",
        "name": "VA Cluster"
    },
    "accountCorrelationConfig": {
        "type": "ACCOUNT_CORRELATION_CONFIG",
        "id": "2c918086761f583001762e91cbd3274c",
        "name": "OpenLDAP Template Account Correlation Config"
    },
    "accountCorrelationRule": null,
    "managerCorrelationMapping": null,
    "managerCorrelationRule": null,
    "beforeProvisioningRule": null,
    "schemas": [
        {
            "type": "CONNECTOR_SCHEMA",
            "id": "2c91808478c6d7970178c864bf631ab0",
            "name": "account"
        },
        {
            "type": "CONNECTOR_SCHEMA",
            "id": "2c91808478c6d7970178c864bf631ab1",
            "name": "group"
        }
    ],
    "passwordPolicies": [
        {
            "type": "PASSWORD_POLICY",
            "id": "2c91808a761f515001762e917daf1c20",
            "name": "Default"
        },
        {
            "type": "PASSWORD_POLICY",
            "id": "2c91808a763e8499017647fbfe7b2bde",
            "name": "Corporate Password Policy"
        }
    ],
    "features": [
        "AUTHENTICATE",
        "GROUP_PROVISIONING",
        "PASSWORD",
        "MANAGER_LOOKUP",
        "SYNC_PROVISIONING",
        "SEARCH"
    ],
    "type": "OpenLDAP - Direct",
    "connector": "openldap-angularsc",
    "connectorClass": "sailpoint.connector.LDAPConnector",
    "connectorAttributes": {
        "healthCheckTimeout": 30,
        "filterString": null,
        "groupMemberAttribute": "uniqueMember",
        "pageSize": "100",
        "posixGroup_Member_Attribute": null,
        "enableNetGroups": false,
        "password": "1:zHS2wnHGesz4qHL4Zlal1v6ObS6LBgAXKNtmY252fmcnvT0+pUwi64teYv3XoGhW60lGy1UWx+T1\nTuhD3OCeoGMonqG/bAAwOlAdIWoUb5mEadCbtpaSA5v/ux95sjmqND/xBiYR7Jfdy6nymaKkrT9p\nrxxI0LvJvprfVVHrtNqe8ULtWa4gQjgMugdMl8MZxbQV1CM62u0KMgoJerdlR8ZTTuX9GD4INF2Z\n4VPtNfN7PnVnjcndXmm5NIo+IuZwX3B8zk0QonlwS+oUiYu2jwLoN5pfzRUrcfvXrHrlBVtjvyJz\n+fLqlbJgEYs8uNWkhEw4XiPzgopkxI2ra+vfSEJE+JlNfbPPdpW0oQbWgXLgM7aAqYKWpV5PJmpa\nXtaoSzxVJuYR2h4SM0SKADj4DnKb7vcxm8gSMQFfjNU8ANix+m3O1NqRTKpW+EG604+/XrUJb5S/\nUSQTitxUgQgxBPlx5OA1wPw00UNkpdsxw4lwFfklMMBumQQm6Rtm/GPR69G4zD/SaoWVZ/64rPWw\nlz6kC9ecKXw29hwNCJZpdZvKHElKKbMrlvtVXg4htR63",
        "charsToEscapeWhileProvisioning": "/",
        "host": "10.0.0.2",
        "authorizationType": "simple",
        "cloudExternalId": "223121",
        "keystore": null,
        "nisNetGroupTriple_Member_Attribute": null,
        "group": "[searchScope:SUBTREE, searchDN:ou=groups,dc=example, dc=com, iterateSearchFilter:, filterString:]",
        "cloudUniqueSearchDN": null,
        "group.filterString": null,
        "enablePosixGroups": false,
        "searchScope": "SUBTREE",
        "group.searchScope": "SUBTREE",
        "iterateModeOverride": "DEFAULT",
        "skipBackslashInFilter": "true",
        "iterateSearchFilter": null,
        "useSSL": false,
        "formPath": null,
        "cloudCacheUpdate": 1636747716514,
        "port": "389",
        "charsToEscapeAtStartInDN": " #",
        "nisNetGroupTriple_Brace_Override": null,
        "charsToEscapeInDN": ",+\\\"<>;",
        "authSearchAttributes": [
            "cn",
            "uid",
            "mail"
        ],
        "searchDN": "ou=users,dc=example, dc=com",
        "group.searchDN": "ou=groups,dc=example, dc=com",
        "cloudAuthEnabled": true,
        "deltaAggregation": null,
        "groupEntitlementAttr": "groups",
        "group.iterateSearchFilter": null,
        "groupMemberFilterString": null,
        "deleteThresholdPercentage": 10,
        "passwordAttr": "userPassword",
        "useForAccounts": "true",
        "templateApplication": "OpenLDAP Template",
        "convertHexToCharacter": "true",
        "charsToEscapeAtEndInDN": " ",
        "cloudDisplayName": "Directory",
        "groupMemberSearchDN": "ou=groups,dc=example, dc=com",
        "user": "cn=admin,dc=example,dc=com",
        "beforeProvisioningRule": null
    },
    "deleteThreshold": 10,
    "authoritative": false,
    "healthy": null,
    "status": null,
    "since": null,
    "managementWorkgroup": null,
    "id": "2c91808478c6d7970178c864bf621aaf",
    "name": "Directory",
    "created": "2021-04-12T23:22:29.090Z",
    "modified": "2021-11-12T20:08:36.514Z"
}

Instead of modifying the full JSON source definition and using PUT /v3/sources/{sourceID} to update the entire source definition, we can use PATCH /v3/sources/{sourceID} with a JSON request body that only defines the change we want to make using JSONPatch syntax.  We need to specify the operation that we want to do (add, remove, replace, copy, move, test), the path to the attribute we are performing the operation on, and the new value, if required by the operation.

We see that the healthCheckTimeout attribute is under the top level connectorAttributes attribute, so we construct our JSON request body using JSONPatch syntax using "replace" for the op, "/connectorAttributes/healthCheckTimeout" for the path, and 35 for the value.  More than one change can be defined in one JSON request body which is why the JSON array characters, [ and ] must be used to enclose the request body.

[
    {
        "op": "replace",
        "path": "/connectorAttributes/healthCheckTimeout",
        "value": 35
    }
]

We run the API call PATCH /v3/sources/{sourceID} with this JSON request body to increase the length of the source health check timeout.  When making an HTTP method PATCH API call, you must change the Content-Type header value to "application/json-patch+json" or you will get a "405 Method Not Allowed" error.

Now, we want to make another change.  We want to remove all provisioning capabilities from this source for testing purposes.

We find the current source features in the full JSON source definition:

 "features": [
            "PROVISIONING",
            "MANAGER_LOOKUP",
            "AUTHENTICATE",
            "SYNC_PROVISIONING",
            "PASSWORD",
            "GROUP_PROVISIONING",
            "SEARCH"
        ]

Then we build the JSON request body removing all features related to provisioning from the array of source features.  Notice that quotes are required around each item in the features array but not around the array itself:

[
    {
        "op": "replace",
        "path": "/features",
        "value": [
            "MANAGER_LOOKUP",
            "AUTHENTICATE",
            "PASSWORD",
            "SEARCH"
        ]
    }
]

We run the API call PATCH /v3/sources/{sourceID} with this JSON request body to remove all provisioning capabilities from this source.

The same approach using the PATCH HTTP method can be used to edit the Account Create Profile on a source.  Instead of using PUT /v3/sources/{sourceId}/provisioning-policies/CREATE with the full CREATE provisioning policy JSON definition is detailed in this article, we can use PATCH /v3/sources/{sourceId}/provisioning-policies/CREATE with a JSON request body that defines only the changes we want to make to the Account Create Profile.  

Additional attributes cannot be added to a source Account Create Profile in the user interface, but we can utilize JSONPatch functionality to do so.  We want to add the "title" account attribute to our Directory source Account Create Profile, and we want to derive its value from the "jobTitle" Identity Attribute. 

First, we get the Account Create Profile JSON definition with GET /v3/sources/{sourceId}/provisioning-policies/CREATE:

{
    "name": "Account",
    "description": null,
    "usageType": "CREATE",
    "fields": [
        {
            "name": "uid",
            "transform": {
                "attributes": {
                    "name": "Create Unique LDAP Attribute"
                },
                "type": "rule"
            },
            "attributes": {
                "template": "$(firstname).$(lastname)$(uniqueCounter)",
                "cloudMaxUniqueChecks": "50"
            },
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "password",
            "transform": {
                "type": "rule",
                "attributes": {
                    "name": "Create Password"
                }
            },
            "attributes": {},
            "isRequired": false,
            "type": "secret",
            "isMultiValued": false
        },
        {
            "name": "telephoneNumber",
            "transform": {
                "attributes": {
                    "id": "FormatWorkPhone"
                },
                "type": "reference"
            },
            "attributes": {},
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "mail",
            "transform": {
                "type": "rule",
                "attributes": {
                    "name": "Create Unique LDAP Attribute"
                }
            },
            "attributes": {
                "template": "$(firstname).$(lastname)$(uniqueCounter)@acme",
                "cloudMaxUniqueChecks": "50"
            },
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "givenName",
            "transform": {
                "attributes": {
                    "name": "firstname"
                },
                "type": "identityAttribute"
            },
            "attributes": {},
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "mobile",
            "transform": {
                "attributes": {
                    "values": [
                        {
                            "attributes": {
                                "input": {
                                    "attributes": {
                                        "name": "phone"
                                    },
                                    "type": "identityAttribute"
                                }
                            },
                            "type": "e164phone"
                        },
                        {
                            "attributes": {
                                "value": "no mobile phone"
                            },
                            "type": "static"
                        }
                    ]
                },
                "type": "firstValid"
            },
            "attributes": {},
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "dn",
            "transform": {
                "attributes": {
                    "name": "Create Unique Account ID"
                },
                "type": "rule"
            },
            "attributes": {
                "template": "uid=$(firstname).$(lastname)$(uniqueCounter),ou=users,dc=example,dc=com",
                "cloudMaxUniqueChecks": "50"
            },
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "cn",
            "transform": {
                "attributes": {
                    "name": "displayName"
                },
                "type": "identityAttribute"
            },
            "attributes": {},
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "sn",
            "transform": {
                "attributes": {
                    "name": "lastname"
                },
                "type": "identityAttribute"
            },
            "attributes": {},
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        }
    ]
}

In our JSON request body, we use "add" for the op and "/fields/-" for the path because we are adding a new item to the end of the fields array.  For the value attribute, we copy the JSON definition of an existing attribute in our Account Create Profile, "sn", that derives its value from an Identity Attribute and modify it for our purposes by changing the name to "title" and the name of the Identity Attribute to "jobTitle". 

[
    {
        "op": "add",
        "path": "/fields/-",
        "value": {
            "name": "title",
            "transform": {
                "attributes": {
                    "name": "jobTitle"
                },
                "type": "identityAttribute"
            },
            "attributes": {},
            "type": "string",
            "isMultiValued": false
        }
    }
]

We run the API call PATCH /v3/sources/{sourceId}/provisioning-policies/CREATE with this JSON request body, and the "title" attribute is added as the last attribute to the Directory source Account Create Profile:

jobTitle.png

What if we wanted to add transform logic to our newly added Account Create profile attribute?  We can do that as well using JSONPatch.  We want to uppercase the job title before it gets provisioned to the new Directory source account, so we construct our JSON request body as below.  The op is "replace" because we are changing the "transform" section of the attribute definition.  The path is "/fields/9/transform" because the "title" attribute is the 10th item in the fields array which begins at index 0, and we want to update the nested object, "transform", within that item of the array.  The value is the Identity Attribute primitive operation wrapped in the Upper primitive operation defined in JSON.

[
    {
        "op": "replace",
        "path": "/fields/9/transform",
        "value": {
            "attributes": {
                "input": {
                    "attributes": {
                        "name": "title"
                    },
                    "type": "identityAttribute"
                }
            },
            "type": "upper"
        }
    }
]

We run the API call PATCH /v3/sources/{sourceId}/provisioning-policies/CREATE with this JSON request body, and now the "title" attribute in the Account Create Profile is shown to have a "Custom Transform" applied to it in the user interface because we added extra transform logic around the Identity Attribute value:

title.png

We can view the response body of our most recent PATCH call or run GET /v3/sources/{sourceId}/provisioning-policies/CREATE again to get the Account Create Profile JSON definition and see the changes that we made:

{
    "name": "Account",
    "description": null,
    "usageType": "CREATE",
    "fields": [
        {
            "name": "uid",
            "transform": {
                "attributes": {
                    "name": "Create Unique LDAP Attribute"
                },
                "type": "rule"
            },
            "attributes": {
                "template": "$(firstname).$(lastname)$(uniqueCounter)",
                "cloudMaxUniqueChecks": "50"
            },
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "password",
            "transform": {
                "type": "rule",
                "attributes": {
                    "name": "Create Password"
                }
            },
            "attributes": {},
            "isRequired": false,
            "type": "secret",
            "isMultiValued": false
        },
        {
            "name": "telephoneNumber",
            "transform": {
                "attributes": {
                    "id": "FormatWorkPhone"
                },
                "type": "reference"
            },
            "attributes": {},
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "mail",
            "transform": {
                "type": "rule",
                "attributes": {
                    "name": "Create Unique LDAP Attribute"
                }
            },
            "attributes": {
                "template": "$(firstname).$(lastname)$(uniqueCounter)@acme",
                "cloudMaxUniqueChecks": "50"
            },
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "givenName",
            "transform": {
                "attributes": {
                    "name": "firstname"
                },
                "type": "identityAttribute"
            },
            "attributes": {},
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "mobile",
            "transform": {
                "attributes": {
                    "values": [
                        {
                            "attributes": {
                                "input": {
                                    "attributes": {
                                        "name": "phone"
                                    },
                                    "type": "identityAttribute"
                                }
                            },
                            "type": "e164phone"
                        },
                        {
                            "attributes": {
                                "value": "no mobile phone"
                            },
                            "type": "static"
                        }
                    ]
                },
                "type": "firstValid"
            },
            "attributes": {},
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "dn",
            "transform": {
                "attributes": {
                    "name": "Create Unique Account ID"
                },
                "type": "rule"
            },
            "attributes": {
                "template": "uid=$(firstname).$(lastname)$(uniqueCounter),ou=users,dc=example,dc=com",
                "cloudMaxUniqueChecks": "50"
            },
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "cn",
            "transform": {
                "attributes": {
                    "name": "displayName"
                },
                "type": "identityAttribute"
            },
            "attributes": {},
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "sn",
            "transform": {
                "attributes": {
                    "name": "lastname"
                },
                "type": "identityAttribute"
            },
            "attributes": {},
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "title",
            "transform": {
                "attributes": {
                    "input": {
                        "attributes": {
                            "name": "title"
                        },
                        "type": "identityAttribute"
                    }
                },
                "type": "upper"
            },
            "attributes": {},
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        }
    ]
}
Labels (2)
Version history
Revision #:
4 of 4
Last update:
‎Mar 28, 2023 03:05 PM
Updated by: