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
-
Create a WebService Source and give it a name, a description, an owner and select the relevant VA Cluster.
-
Choose OAuth 2.0 authentication type and select Client Credentials Grant Type.
-
Paste your Personal access token ID and secret in Client ID and Client Secret fields.
-
In the Base URL field, set your tenant API url (https://tenant.api.identitynow.com).
-
In the Token URL field, set your OAuth token url (https://tenant.api.identitynow.com/oauth/token).
-
Save the source.
-
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
}
]
- 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;
- Configure your account schema like this:
- Configure your entitlement schema like this:
-
Configure correlation like this:
-
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