Assign ISC Capabilities with Requestable Entitlements/Roles

Note: There is an existing SaaS loopback connector on the Colab which requires you to configure CLI, Node.js and Typescript configured on your system and supports the following features:

  • Manage user levels as entitlements
  • Manage governance groups as entitlements
  • Manage lifecycle states as entitlements

In case you don’t have permissions to setup the dev tools, you might want to use the below steps to configure Web service based loop back connector

Web Service Loop Back Connector Source

Pre-requisite
Create a personal access token from an IdentityNow admin account.

Configuration

  1. Create a WebService Source and give it a name, a description, an owner and select the relevant VA Cluster.

  2. Choose OAuth 2.0 authentication type and select Client Credentials Grant Type.

  3. Paste your Personal access token ID and secret in Client ID and Client Secret fields.

  4. In the Base URL field, set your tenant API url (https://tenant.api.identitynow.com).

  5. In the Token URL field, set your OAuth token url (https://tenant.api.identitynow.com/oauth/token).

  6. Save the source.

  7. Use Update Source (Partial) API to update the source

[
            {
                "httpMethodType": "POST",
                "pagingInitialOffset": 0,
                "sequenceNumberForEndpoint": "1",
                "uniqueNameForEndPoint": "Test",
                "afterRule": null,
                "curlCommand": null,
                "rootPath": null,
                "body": {
                    "bodyFormData": null,
                    "jsonBody": "{\n\t"query": {\n\t\t"query": "@access(source.name.exact:IdentityNow)"\n\t},\n\t"indices": [\n\t\t"identities"\n\t],\n\t"includeNested": true,\n    "queryResultFilter": {\n        "includes": [\n            "id", "name", "firstName", "lastName", "displayName", "inactive", "status", "access"\n        ]\n    }\n}",
                    "bodyFormat": "raw"
                },
                "customAuthUrl": null,
                "paginationSteps": null,
                "responseCode": [
                    "200"
                ],
                "resMappingObj": null,
                "contextUrl": "/v3/search",
                "pagingSize": 50,
                "curlEnabled": false,
                "header": null,
                "operationType": "Test Connection",
                "beforeRule": null,
                "xpathNamespaces": null,
                "parentEndpointName": null
            },
            {
                "httpMethodType": "POST",
                "pagingInitialOffset": 0,
                "sequenceNumberForEndpoint": "2",
                "uniqueNameForEndPoint": "Account Aggregation",
                "afterRule": null,
                "curlCommand": null,
                "rootPath": "$",
                "body": {
                    "jsonBody": "{\n\t"query": {\n\t\t"query": "@access(source.name.exact:IdentityNow)"\n\t},\n\t"indices": [\n\t\t"identities"\n\t],\n\t"includeNested": true,\n    "queryResultFilter": {\n        "includes": [\n            "id", "name", "firstName", "lastName", "displayName", "inactive", "status", "access"\n        ]\n    }\n}",
                    "bodyFormat": "raw"
                },
                "paginationSteps": null,
                "responseCode": [
                    "200"
                ],
                "resMappingObj": {
                    "firstName": "firstName",
                    "lastName": "lastName",
                    "capabilities": "access[?(@.attribute=='assignedGroups')].value",
                    "inactive": "inactive",
                    "displayName": "displayName",
                    "name": "name",
                    "externalId": "id"
                },
                "contextUrl": "/v3/search?limit=10000",
                "pagingSize": 50,
                "curlEnabled": false,
                "header": null,
                "operationType": "Account Aggregation",
                "beforeRule": null,
                "xpathNamespaces": null
            },
            {
                "httpMethodType": "POST",
                "pagingInitialOffset": 0,
                "sequenceNumberForEndpoint": "3",
                "uniqueNameForEndPoint": "Entitlement Aggregation",
                "afterRule": null,
                "curlCommand": null,
                "rootPath": null,
                "body": {
                    "jsonBody": "{\n  "query": {\n    "query": "source.name.exact:IdentityNow AND attribute:assignedGroups"\n  },\n  "indices": [\n    "entitlements"\n  ],\n  "sort": [\n    "name"\n  ],\n  "includeNested": false\n}",
                    "bodyFormat": "raw"
                },
                "paginationSteps": null,
                "responseCode": [
                    "200"
                ],
                "resMappingObj": {
                    "displayName": "displayName",
                    "name": "name",
                    "description": "description",
                    "value": "value"
                },
                "contextUrl": "/v3/search",
                "pagingSize": 50,
                "curlEnabled": false,
                "header": null,
                "operationType": "Group Aggregation",
                "beforeRule": null,
                "xpathNamespaces": null
            },
            {
                "httpMethodType": "PATCH",
                "pagingInitialOffset": 0,
                "sequenceNumberForEndpoint": "4",
                "uniqueNameForEndPoint": "Add Entitlement",
                "afterRule": null,
                "curlCommand": null,
                "rootPath": null,
                "body": {
                    "bodyFormData": null,
                    "jsonBody": "[{\n    \"op\": \"add\",\n    \"path\": \"/capabilities/-\",\n    \"value\": \"$plan.capabilities$\"\n  }]",
                    "bodyFormat": "raw"
                },
                "customAuthUrl": null,
                "paginationSteps": null,
                "responseCode": null,
                "resMappingObj": null,
                "contextUrl": "/v3/auth-users/$plan.nativeIdentity$",
                "pagingSize": 50,
                "curlEnabled": false,
                "header": {
                    "Content-Type": "application/json-patch+json"
                },
                "operationType": "Add Entitlement",
                "xpathNamespaces": null,
                "parentEndpointName": null
            },
            {
                "httpMethodType": "GET",
                "pagingInitialOffset": 0,
                "sequenceNumberForEndpoint": "5",
                "uniqueNameForEndPoint": "Remove Entitlement",
                "curlCommand": null,
                "rootPath": "$",
                "body": {
                    "bodyFormData": null,
                    "jsonBody": null,
                    "bodyFormat": "raw"
                },
                "customAuthUrl": null,
                "paginationSteps": null,
                "responseCode": null,
                "resMappingObj": {
                    "usercapabilities": "capabilities"
                },
                "contextUrl": "/v3/auth-users/$plan.nativeIdentity$",
                "pagingSize": 50,
                "curlEnabled": false,
                "header": null,
                "operationType": "Remove Entitlement",
                "xpathNamespaces": null,
                "parentEndpointName": null
            },
            {
                "httpMethodType": "PATCH",
                "pagingInitialOffset": 0,
                "sequenceNumberForEndpoint": "6",
                "uniqueNameForEndPoint": "Remove Entitlement 2",
                "afterRule": null,
                "curlCommand": null,
                "rootPath": null,
                "body": {
                    "bodyFormData": null,
                    "jsonBody": "{\n    \"usercapabilities\": $response.usercapabilities$,\n    \"getIndex\": \"$plan.capabilities$\"\n  }",
                    "bodyFormat": "raw"
                },
                "customAuthUrl": null,
                "paginationSteps": null,
                "responseCode": [
                    "2**"
                ],
                "resMappingObj": null,
                "contextUrl": "/v3/auth-users/$plan.nativeIdentity$",
                "pagingSize": 50,
                "curlEnabled": false,
                "header": {
                    "Content-Type": "application/json-patch+json"
                },
                "operationType": "Remove Entitlement",
                "beforeRule": "Rule - WS BeforeProvisioning ISC Capabilities",
                "xpathNamespaces": null,
                "parentEndpointName": null
            },
            {
                "httpMethodType": "GET",
                "pagingInitialOffset": 0,
                "sequenceNumberForEndpoint": "7",
                "uniqueNameForEndPoint": "Account Aggregation 2",
                "afterRule": null,
                "curlCommand": null,
                "rootPath": "$",
                "body": {
                    "bodyFormData": null,
                    "jsonBody": null,
                    "bodyFormat": "raw"
                },
                "customAuthUrl": null,
                "paginationSteps": null,
                "responseCode": [
                    "200"
                ],
                "resMappingObj": {
                    "capabilities": "access[?(@.attribute=='assignedGroups')].value"
                },
                "contextUrl": "/v3/search/identities/$response.externalId$",
                "pagingSize": 50,
                "curlEnabled": false,
                "header": {
                    "Content-Type": "application/x-www-form-urlencoded"
                },
                "operationType": "Account Aggregation",
                "xpathNamespaces": null,
                "parentEndpointName": "Account Aggregation"
            },
            {
                "httpMethodType": "GET",
                "pagingInitialOffset": 0,
                "sequenceNumberForEndpoint": "8",
                "uniqueNameForEndPoint": "create account",
                "curlCommand": null,
                "rootPath": "$",
                "body": {
                    "bodyFormData": null,
                    "jsonBody": null,
                    "bodyFormat": "formData"
                },
                "customAuthUrl": null,
                "paginationSteps": null,
                "responseCode": [
                    "2**"
                ],
                "resMappingObj": {
                    "externalId": "id"
                },
                "contextUrl": "/v3/public-identities?filters=email eq \"$plan.name$\"",
                "pagingSize": 50,
                "curlEnabled": false,
                "header": null,
                "operationType": "Create Account",
                "xpathNamespaces": null,
                "parentEndpointName": null
            }
        ]
  1. Create a Web Service Before Provisioning Rule:

Rule - WS BeforeProvisioning ISC Capabilities

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Map;
    import connector.common.JsonUtil;
    import connector.common.Util;
    import sailpoint.connector.webservices.EndPoint;
    import sailpoint.connector.webservices.WebServicesClient;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    //********** Main Logic ************
    
    String strlog = "Rule - WS BeforeProvisioning ISC Capabilities";
    
    log.debug(strlog +" STARTED");
    Map body = requestEndPoint.getBody();
    String jsonBody = (String) body.get("jsonBody");
    log.debug(strlog + "Rule - Modify Body: running");

    try {
    
        Map jsonMap = JsonUtil.toMap(jsonBody);
        if (jsonMap != null) {

            Object capabilities = jsonMap.get("usercapabilities");
            ArrayList capabilitiesArray = (ArrayList) capabilities;

            String getIndexStr = (String) jsonMap.get("getIndex");

            capabilitiesArray.remove(getIndexStr);
            jsonMap.clear();
            jsonMap.put("op", "replace");
            jsonMap.put("path", "/capabilities");
            jsonMap.put("value", capabilitiesArray);
            String finalBody = JsonUtil.render(jsonMap);

            body.put("jsonBody", "["+ finalBody + "]");
            requestEndPoint.setBody(body);
            log.debug(strlog + "Rule - Modify Body: " + body);
        }
    } catch (Exception ex) {
        log.debug(strlog + "Rule - Modify Body: " + ex);
    }
    return requestEndPoint;​
  1. Configure your account schema like this:

  1. Configure your entitlement schema like this:

  1. Configure correlation like this:

  2. Configure the “Create Account” policy like this:

The entitlements that you aggregate in this source can be made requestable or assigned to roles for automatic provisioning based on membership in AD

Thanks to Fernando de los Ríos Sánchez for this post

3 Likes

Hi @smukhija
Thank you so much for this post.

Quick question : Is there a way to configure single account aggregation for this source? Often times, when new identities are provisioned with ISC capabilities to this source, I’m seeing that the account attributes are usually blank until a full account aggregation is run again. (Screenshot below)


Instead, if there was a "Get-Object" operation configured to pull single identity details, the connector would call it by default once the provisioning is completed (Default behavior of web services connector)

I tried to configure response mapping attributes on “Create Account” HTTP operation but looks like the response here is not similar to the response being fetched in the chained full account aggregation operations. (“Account Aggregation” and “Account Aggregation 2”)

Any insights would be really appreciated.

Thanks,
Arshad.