Reverse entitlement aggregation for Web Services connector

Description

This is a WebServicesAfterOperation rule that allows you to establish account to entitlement relationships when the target API does not provide endpoints to fetch account to entitlement relationships. Instead, the API provides entitlement to account relationships, which is something our current architecture struggles with. This is a similar challenge to that we face with LDAP servers, which hold group membership at the group object and not the account object. This is solved by specialised versions of our LDAP connector that are able to link accounts to entitlements from the entitlement side. The same is not true for Web Services connector, hence this tool.

The idea behind this rule is leveraging a previous entitlement aggregation where entitlement to account relationship can be found. This assumes the source endpoint provides you with the account IDs of each of the accounts that hold an entitlement. Entitlement aggregation should store that relationship or membership in a multi-valued entitlement attribute. Once this aggregation is run, we run account aggregation with this WSAO rule so the rule connects back to IDN to figure out assigned entitlements based on that multi-valued attribute.

Let me explain by example. This was created to connect to IBM Cloud. At this time, IBM cloud only provides you with group to user relationship endpoints:

Check out the members attribute: that’s the members of the group, but you won’t find that information from an account perspective, only from a group perspective. This kind of endpoint must be leveraged on a child entitlement aggregation call, since we need to point to each group individually:

{
                "httpMethodType": "GET",
                "pagingInitialOffset": 0,
                "sequenceNumberForEndpoint": "4",
                "uniqueNameForEndPoint": "Group Membership Aggregation",
                "afterRule": null,
                "curlCommand": null,
                "rootPath": "$",
                "body": {
                    "jsonBody": null,
                    "bodyFormat": "raw"
                },
                "paginationSteps": null,
                "responseCode": [
                    "200"
                ],
                "resMappingObj": {
                    "members": "$..iam_id"
                },
                "contextUrl": "iam.cloud.ibm.com/v2/groups/$response.id$/members?account_id=2b79f6c62a3e404785acca926698c4c3",
                "pagingSize": 50,
                "curlEnabled": false,
                "header": {
                    "Authorization": "Bearer $application.access_token$"
                },
                "operationType": "Group Aggregation",
                "beforeRule": null,
                "xpathNamespaces": null,
                "parentEndpointName": "Group Aggregation"
            }

That’s the definition of the child call that depends on the parent Group Aggregation call. Note we leverage $response.id$, the entitlement ID from the parent call, and we’re using a JSONPath expression that collects account IDs from the iam_id JSON keys ($…iam_id ). This also requires setting up the entitlement schema correctly, like the one below:

We’ve got our entitlements in, and they also hold membership information:

Well, now it’s time to aggregate accounts and use that membership information to update the account to entitlement link accordingly on the account object. For that, we configure account aggregation as usual but add this WSAO rule to the operation:

{
                "httpMethodType": "GET",
                "pagingInitialOffset": 0,
                "sequenceNumberForEndpoint": "5",
                "uniqueNameForEndPoint": "Account Aggregation",
                "afterRule": "WSReverseMembershipAggregation",
                "curlCommand": null,
                "rootPath": "$.resources",
                "body": {
                    "jsonBody": null,
                    "bodyFormat": "raw"
                },
                "paginationSteps": null,
                "responseCode": [
                    "200"
                ],
                "resMappingObj": {
                    "altphonenumber": "altphonenumber",
                    "firstname": "firstname",
                    "account_id": "account_id",
                    "iam_id": "iam_id",
                    "user_id": "user_id",
                    "phonenumber": "phonenumber",
                    "realm": "realm",
                    "id": "id",
                    "state": "state",
                    "email": "email",
                    "lastname": "lastname"
                },
                "contextUrl": "user-management.cloud.ibm.com/v2/accounts/2b79f6c62a3e404785acca926698c4c3/users",
                "pagingSize": 50,
                "curlEnabled": false,
                "header": {
                    "Authorization": "Bearer $application.access_token$",
                    "X-IDNClientId": "24113546535b45838b3ddf93e05b8088",
                    "Accept": "application/json",
                    "X-IDNClientSecret": "a4c516ba3e33797c27b5957382b52fd203b2cc339db7478567c854d4aa3caa4e",
                    "X-IDNUrl": "https://company259-poc.identitynow-demo.com",
                    "X-IDNAttribute": "group.members",
                    "Content-Type": "application/json"
                },
                "operationType": "Account Aggregation",
                "beforeRule": null,
                "xpathNamespaces": null
            }

Note the afterRule reference. Also, note the X-* headers (more on that later). First, the operation collects all account attributes but entitlements. After that, the WSAO rule authenticates to IDN API, collects the appropriate entitlements, resolves account memberships and updates accounts accordingly. Remember that your account schema must have an attribute that references the previous entitlements for this membership to be updated:

You’re all set. Let’s learn how to set this up.

Prerequisites

An IdentityNow tenant and a system with a entitlement to account relationship reporting endpoints.

Limitations

Full account aggregation depends on a previous full run of entitlement aggregation. Account aggregation should not fail if no previous entitlement aggregation was run, but no entitlement information would be added.

Code may need some changes depending on how entitlement to account relationship data is provided by your endpoints.

Configuration

  1. Configure your Web Services connector as you normally would. Take into account the following points:
  • Account schema must present a multi-valued entitlement attribute of the type of the entitlements you’re trying to relate to.
  • Group schema must present a multi-valued attribute where group membership is held.
  1. Install a new WSAO connector rule with this code:
//WSReverseMembershipAggregation
import java.util.HashMap;
import java.util.Map;

import com.jayway.jsonpath.JsonPath;

import org.apache.logging.log4j.Logger;

import java.util.List;
import java.util.ArrayList;

import sailpoint.connector.webservices.WebServicesClient;
import sailpoint.object.Schema;
import sailpoint.object.Application;
import sailpoint.object.AttributeDefinition;
import connector.common.JsonUtil;

String authenticate(String url, String clientId, String clientSecret) throws Exception {
    WebServicesClient client = new WebServicesClient();
    Map args = new HashMap();
    Map header = new HashMap();
    Map payload = new HashMap();
    List allowedStatuses = new ArrayList();

    String request = String.format("%s/oauth/token?grant_type=client_credentials&client_id=%s&client_secret=%s", url, clientId, clientSecret);
    args.put(WebServicesClient.ARG_URL, request);
    header.put("Accept", "application/json");
    allowedStatuses.add("200");

    client.configure(args);
    try {
        String response = client.executePost(request, payload, header, allowedStatuses);
        Map responseMap = JsonUtil.toMap(response);

        String accessToken = (String) responseMap.get("access_token");

        return accessToken;
    } catch (Exception e) {
        throw new Exception(String.format("Authentication call failed: %s", e.getMessage()));
    }
}

String getJSONEntitlements(String url, String accessToken, String appId, String type) throws Exception {
    WebServicesClient client = new WebServicesClient();
    Map args = new HashMap();
    Map header = new HashMap();
    List allowedStatuses = new ArrayList();

    String request = String.format("%s/beta/entitlements?filters=source.id eq \"%s\" and type eq \"%s\"", url, appId, type);
    args.put(WebServicesClient.ARG_URL, request);
    header.put("Authorization", String.format("Bearer %s", accessToken));
    allowedStatuses.add("200");

    client.configure(args);
    
    try {
        String response = client.executeGet(request, header, allowedStatuses);
        return response;
    } catch (Exception e) {
        throw new Exception(String.format("Entitlements collection call failed: %s", e.getMessage()));
    }
}

Map headers = requestEndPoint.getHeader();
        
String IDN_URL = (String) headers.get("X-IDNUrl");
if (IDN_URL == null) throw new Exception("X-IDNUrl header is missing");
log.debug("IDN_URL: " + IDN_URL);

String IDN_CLIENT_ID = (String) headers.get("X-IDNClientId");
if (IDN_CLIENT_ID == null) throw new Exception("X-IDNClientId header is missing");
log.debug("IDN_CLIENT_ID: " + IDN_CLIENT_ID);

String IDN_CLIENT_SECRET = (String) headers.get("X-IDNClientSecret");
if (IDN_CLIENT_SECRET == null) throw new Exception("X-IDNClientSecret header is missing");
log.debug("IDN_CLIENT_SECRET: " + IDN_CLIENT_SECRET);

String IDN_ATTRIBUTE = (String) headers.get("X-IDNAttribute");
if (IDN_ATTRIBUTE == null) throw new Exception("X-IDNAttribute header is missing");
if (IDN_ATTRIBUTE.indexOf(".") == -1) throw new Exception("X-IDNAttribute header wrong format. Use <entitlement_type>.<attribute> (e.g. group.members)");
log.debug("IDN_ATTRIBUTE: " + IDN_ATTRIBUTE);

String ENTITLEMENT_TYPE = IDN_ATTRIBUTE.split("\\.")[0];
log.debug("ENTITLEMENT_TYPE: " + ENTITLEMENT_TYPE);

String ENTITLEMENT_ATTRIBUTE = IDN_ATTRIBUTE.split("\\.")[1];
log.debug("ENTITLEMENT_ATTRIBUTE: " + ENTITLEMENT_ATTRIBUTE);

String APP_ID = application.getId();
log.debug("APP_ID: " + APP_ID);

Schema ACCOUNT_SCHEMA = application.getSchema("account");
log.debug("ACCOUNT_SCHEMA: " + ACCOUNT_SCHEMA.toString());

String IDENTITY_ATTRIBUTE = ACCOUNT_SCHEMA.getIdentityAttribute();
log.debug("IDENTITY_ATTRIBUTE: " + IDENTITY_ATTRIBUTE);

String ACCOUNT_ATTRIBUTE = null;
for (String attribute : ACCOUNT_SCHEMA.getAttributeNames()) {
    AttributeDefinition attrDef = ACCOUNT_SCHEMA.getAttributeDefinition(attribute);
    if (attrDef.isEntitlement() && ENTITLEMENT_TYPE.equals(attrDef.getSchemaObjectType())) {
        ACCOUNT_ATTRIBUTE = attribute;
        log.debug("ACCOUNT_ATTRIBUTE: " + ACCOUNT_ATTRIBUTE);
        break;
    }
}

if (ACCOUNT_ATTRIBUTE == null) throw new Exception(String.format("ACCOUNT_ATTRIBUTE was not found. Ensure the account schema contains an entitlement attribute of type %s", ENTITLEMENT_TYPE));

String ACCESS_TOKEN = authenticate(IDN_URL, IDN_CLIENT_ID, IDN_CLIENT_SECRET);
log.debug("ACCESS_TOKEN: " + ACCESS_TOKEN);
String JSON_ENTITLEMENTS = getJSONEntitlements(IDN_URL, ACCESS_TOKEN, APP_ID, ENTITLEMENT_TYPE);
log.debug("JSON_ENTITLEMENTS: " + JSON_ENTITLEMENTS);

for (Map account : processedResponseObject) {
    log.debug("Processing record " + account.toString());
    try {
        String id = (String) account.get(IDENTITY_ATTRIBUTE);
        String searchGroups = String.format("$.[?(@.attributes.%s contains '%s')]..value", ENTITLEMENT_ATTRIBUTE ,id);
        List groups = JsonPath.parse(JSON_ENTITLEMENTS).read(searchGroups, List.class);
        log.debug("Membership found: " + groups.toString());
        account.put(ACCOUNT_ATTRIBUTE, groups);
    } catch (NullPointerException npe) {
        throw new Exception(String.format("Failed to find attribute %s on record %s", IDENTITY_ATTRIBUTE, account.toString()));
    }
}

Map responseMap = new HashMap();
Map connectorStateMap = new HashMap();

responseMap.put("data", processedResponseObject);
responseMap.put("connectorStateMap", connectorStateMap);

return responseMap;

You can use this curl command for convenience:

curl --location --request POST 'https://<tenant>/beta/connector-rules' \
--header 'Authorization: Bearer <your_access_token>' \
--header 'Content-Type: application/json' \
--data-raw '{
  "name": "//WSReverseMembershipAggregation\r\nimport java.util.HashMap;\r\nimport java.util.Map;\r\n\r\nimport com.jayway.jsonpath.JsonPath;\r\n\r\nimport org.apache.logging.log4j.Logger;\r\n\r\nimport java.util.List;\r\nimport java.util.ArrayList;\r\n\r\nimport sailpoint.connector.webservices.WebServicesClient;\r\nimport sailpoint.object.Schema;\r\nimport sailpoint.object.Application;\r\nimport sailpoint.object.AttributeDefinition;\r\nimport connector.common.JsonUtil;\r\n\r\nString authenticate(String url, String clientId, String clientSecret) throws Exception {\r\n    WebServicesClient client = new WebServicesClient();\r\n    Map args = new HashMap();\r\n    Map header = new HashMap();\r\n    Map payload = new HashMap();\r\n    List allowedStatuses = new ArrayList();\r\n\r\n    String request = String.format(\"%s/oauth/token?grant_type=client_credentials&client_id=%s&client_secret=%s\", url, clientId, clientSecret);\r\n    args.put(WebServicesClient.ARG_URL, request);\r\n    header.put(\"Accept\", \"application/json\");\r\n    allowedStatuses.add(\"200\");\r\n\r\n    client.configure(args);\r\n    try {\r\n        String response = client.executePost(request, payload, header, allowedStatuses);\r\n        Map responseMap = JsonUtil.toMap(response);\r\n\r\n        String accessToken = (String) responseMap.get(\"access_token\");\r\n\r\n        return accessToken;\r\n    } catch (Exception e) {\r\n        throw new Exception(String.format(\"Authentication call failed: %s\", e.getMessage()));\r\n    }\r\n}\r\n\r\nString getJSONEntitlements(String url, String accessToken, String appId, String type) throws Exception {\r\n    WebServicesClient client = new WebServicesClient();\r\n    Map args = new HashMap();\r\n    Map header = new HashMap();\r\n    List allowedStatuses = new ArrayList();\r\n\r\n    String request = String.format(\"%s/beta/entitlements?filters=source.id eq \\\"%s\\\" and type eq \\\"%s\\\"\", url, appId, type);\r\n    args.put(WebServicesClient.ARG_URL, request);\r\n    header.put(\"Authorization\", String.format(\"Bearer %s\", accessToken));\r\n    allowedStatuses.add(\"200\");\r\n\r\n    client.configure(args);\r\n    \r\n    try {\r\n        String response = client.executeGet(request, header, allowedStatuses);\r\n        return response;\r\n    } catch (Exception e) {\r\n        throw new Exception(String.format(\"Entitlements collection call failed: %s\", e.getMessage()));\r\n    }\r\n}\r\n\r\nMap headers = requestEndPoint.getHeader();\r\n        \r\nString IDN_URL = (String) headers.get(\"X-IDNUrl\");\r\nif (IDN_URL == null) throw new Exception(\"X-IDNUrl header is missing\");\r\nlog.debug(\"IDN_URL: \" + IDN_URL);\r\n\r\nString IDN_CLIENT_ID = (String) headers.get(\"X-IDNClientId\");\r\nif (IDN_CLIENT_ID == null) throw new Exception(\"X-IDNClientId header is missing\");\r\nlog.debug(\"IDN_CLIENT_ID: \" + IDN_CLIENT_ID);\r\n\r\nString IDN_CLIENT_SECRET = (String) headers.get(\"X-IDNClientSecret\");\r\nif (IDN_CLIENT_SECRET == null) throw new Exception(\"X-IDNClientSecret header is missing\");\r\nlog.debug(\"IDN_CLIENT_SECRET: \" + IDN_CLIENT_SECRET);\r\n\r\nString IDN_ATTRIBUTE = (String) headers.get(\"X-IDNAttribute\");\r\nif (IDN_ATTRIBUTE == null) throw new Exception(\"X-IDNAttribute header is missing\");\r\nif (IDN_ATTRIBUTE.indexOf(\".\") == -1) throw new Exception(\"X-IDNAttribute header wrong format. Use <entitlement_type>.<attribute> (e.g. group.members)\");\r\nlog.debug(\"IDN_ATTRIBUTE: \" + IDN_ATTRIBUTE);\r\n\r\nString ENTITLEMENT_TYPE = IDN_ATTRIBUTE.split(\"\\\\.\")[0];\r\nlog.debug(\"ENTITLEMENT_TYPE: \" + ENTITLEMENT_TYPE);\r\n\r\nString ENTITLEMENT_ATTRIBUTE = IDN_ATTRIBUTE.split(\"\\\\.\")[1];\r\nlog.debug(\"ENTITLEMENT_ATTRIBUTE: \" + ENTITLEMENT_ATTRIBUTE);\r\n\r\nString APP_ID = application.getId();\r\nlog.debug(\"APP_ID: \" + APP_ID);\r\n\r\nSchema ACCOUNT_SCHEMA = application.getSchema(\"account\");\r\nlog.debug(\"ACCOUNT_SCHEMA: \" + ACCOUNT_SCHEMA.toString());\r\n\r\nString IDENTITY_ATTRIBUTE = ACCOUNT_SCHEMA.getIdentityAttribute();\r\nlog.debug(\"IDENTITY_ATTRIBUTE: \" + IDENTITY_ATTRIBUTE);\r\n\r\nString ACCOUNT_ATTRIBUTE = null;\r\nfor (String attribute : ACCOUNT_SCHEMA.getAttributeNames()) {\r\n    AttributeDefinition attrDef = ACCOUNT_SCHEMA.getAttributeDefinition(attribute);\r\n    if (attrDef.isEntitlement() && ENTITLEMENT_TYPE.equals(attrDef.getSchemaObjectType())) {\r\n        ACCOUNT_ATTRIBUTE = attribute;\r\n        log.debug(\"ACCOUNT_ATTRIBUTE: \" + ACCOUNT_ATTRIBUTE);\r\n        break;\r\n    }\r\n}\r\n\r\nif (ACCOUNT_ATTRIBUTE == null) throw new Exception(String.format(\"ACCOUNT_ATTRIBUTE was not found. Ensure the account schema contains an entitlement attribute of type %s\", ENTITLEMENT_TYPE));\r\n\r\nString ACCESS_TOKEN = authenticate(IDN_URL, IDN_CLIENT_ID, IDN_CLIENT_SECRET);\r\nlog.debug(\"ACCESS_TOKEN: \" + ACCESS_TOKEN);\r\nString JSON_ENTITLEMENTS = getJSONEntitlements(IDN_URL, ACCESS_TOKEN, APP_ID, ENTITLEMENT_TYPE);\r\nlog.debug(\"JSON_ENTITLEMENTS: \" + JSON_ENTITLEMENTS);\r\n\r\nfor (Map account : processedResponseObject) {\r\n    log.debug(\"Processing record \" + account.toString());\r\n    try {\r\n        String id = (String) account.get(IDENTITY_ATTRIBUTE);\r\n        String searchGroups = String.format(\"$.[?(@.attributes.%s contains '%s')]..value\", ENTITLEMENT_ATTRIBUTE ,id);\r\n        List groups = JsonPath.parse(JSON_ENTITLEMENTS).read(searchGroups, List.class);\r\n        log.debug(\"Membership found: \" + groups.toString());\r\n        account.put(ACCOUNT_ATTRIBUTE, groups);\r\n    } catch (NullPointerException npe) {\r\n        throw new Exception(String.format(\"Failed to find attribute %s on record %s\", IDENTITY_ATTRIBUTE, account.toString()));\r\n    }\r\n}\r\n\r\nMap responseMap = new HashMap();\r\nMap connectorStateMap = new HashMap();\r\n\r\nresponseMap.put(\"data\", processedResponseObject);\r\nresponseMap.put(\"connectorStateMap\", connectorStateMap);\r\n\r\nreturn responseMap;"
  },
  "attributes": {}
}'
  1. Now update your connector operation to use that WSAO rule:
curl --location --request PATCH 'https://<tenant>/beta/sources/<sourceid>' \
--header 'Content-Type: application/json-patch+json' \
--header 'Authorization: Bearer <your_access_token>' \
--data-raw '[
  {
    "op" : "replace",
    "path" : "/connectorAttributes/connectionParameters/x/afterRule",
    "value" : "WSReverseMembershipAggregation"
  }
]'

Where /x/ is the number of the operation trying to patch.

  1. Finally, you need to pass on your configuration parameters using custom headers:
  • X-IDNUrl: IDN API url
  • X-IDNClientId: IDN admin PAT ID
  • X-IDNClientSecret: IDN admin PAT secret
  • X-IDNAttribute: it identifies the entitlement type and the attribute that holds entitlement to account information. In the previous example, it would be group.members.

    You should be good to go. Please try it, have a look at the code and if you find errors or limitations (there will be), let me know.

Hope this helps.

5 Likes

Hello, I think that the script isn’t correctly escaped:

I believe it should be

This is a detail but I believe if someone copies the curl command it would cause the rule to fail.

Thanks Nadim. I stringified it again because I could see parsing errors but not scaping forward slashes. I updated the post.

Take care.

Hello,

After taking a look at the code I realized that there is no paging for the get entitlement call in the WSAO. and according to documentation, the limit of entitlements we can get is 250.

This should be quick fix but I just wanted to report it in case someone is facing this problem.

For info I have implemented the WSAO in one of my sources and it is working as expected. Thank you

Thanks for pointing it out. This is pre-limitations to the endpoints and paging was not needed. I’ll try to implement paging if you can confirm this is useful to you beyond a concept.

Take care.

1 Like

Implementing paging would be a great help for us since the number of entitlements we have for that source is close to 250. and there is a possibility to have more entitlements in the future

Thank you in advance

Is this how the backend is configured of OOB Connectors like Slack, Okta, etc?

While we’re unable to look under the hood for HTTP operations within OOB connectors, it’s clear that they’re performing some magic behind this scenes.

For example, the Okta API responses for /users and /groups are decoupled in that the users response does not return group memberships. Despite this constraint the OOB connector for Okta has Groups in the account schema and omits members from the entitlement schema

Similarly our legacy internal tools solved for this by aggregating both object collections and correlated the memberships like the following python example.

def aggregate_teams(self):
    users = self.get_users()
    teams = self.get_teams()
   
    # Use enumerate function to keep track index (ex. x[0] index, x[1] iter.item) 
    for x in enumerate(users):
      # Creates Dict Key \ List in user object
      users[x[0]]['attributes'].update({'teams':[]})

      # Loop through teams
      for team in teams:

        # Check if member of team
        if x[1]['id'] in team['attributes']['members']:

          # Append team to user object if True
          users[x[0]]['attributes']['teams'].append(team['id'])

There are plenty of SaaS Apps that decouple memberships from the user object, thus we’re trying to find an elegant solution to mapping entitlements.

Otherwise seems like a common use-case that could be solved within IdentityNow having a feature that functions like so:

Flag for disjointed entitlements: True

Logic \ Requirements

  1. Aggregate Users
  2. Aggregate Entitlements
  3. Import > Entitlement Correlation* ← processes \ correlate existing entitlement data

a. entitlement type: team
entitlement attribute: members > operation: contains > identity attribute: uid

b. entitlement type: group
entitlement attribute: members > operation: contains > identity attribute: uid

c. entitlement type: role
entitlement attribute: members > operation: contains > identity attribute: uid

This would effectively reduce the high technical barrier of entry for most teams and provide sufficient tools \ minor steps to achieve the same goal.

Hi Brian,

Very good feedback. I agree 100%. I would advise to create an enhancement request so our PM team takes it into account. It can be done from https://ideas.sailpoint.com/. I’ll be happy to support it and promote it internally.

Thank you.

1 Like

Hello @fernando_delosrios

I have pointed out previously that the WSAO does not take paging into consideration.

Have you been able to make a new version where this after operation handles paging?

Best regards,
Nadim

Hi @fernando_delosrios thank you so much for this - works really well!

Is there a way to encrypt ‘X-IDNClientSecret’ header?

Or if not, can we use one of the already-encrypted fields, like ‘clientKeySpec’? I just dont know then how to get the value and if it needs to be decrypted once we get it…

But this is great!
Thank you!

Hi,
I figured this out - how to use excrypted/sensitive value

set ‘X-IDNClientSecret’ to be $application.private_key_password$ then use curl/postman to set private_key_password value

curl --location --request PATCH 'https://tenant.api.identitynow.com/beta/sources/<source id>' --data-raw '[
    {
        "op": "add",
        "path": "/connectorAttributes/private_key_password",
        "value": "this-is-my-client-secret"
    }
]

Great stuff, Jason. Was the _password suffix that made the trick or that exact name?

Thanks.

Hi,
I used the existing application attribute ‘private_key_password’ as it was already set to be a sensitive attribute, and we weren’t using that attribute for anything else (it was null before).

Hi @fernando_delosrios , @Nadim @jrossicare @brian-short ,
I have a similar issue but my API doesn’t have the capability to filter the memberships based on user id.

For example it does not support /Groups/group_id/members?test_user where it should ideally give out the groups present on that particular user. Rather, what it only supports it /Groups/<group id> which would only return the group in their system and their respective group members.

All I have is /Users endpoint which fetches only user attributes and /Groups endpoint which fetches groups in the system and all the members in it.

Is there any way that I can leverage any content from this post and achieve my use case?

Regarding my use-case, I have mentioned detailed information on a separate post below:

Any help is greatly appreciated.
Thanks in advance.

Regards,
Arshad.

Hi Arshad,

This is exactly the situation described here. Although a perfectly valid solution, it’s probably not the best at this point in time. With the advent of SaaS Connectors, this can be achieved by a custom SaaS connector which you can easily control the necessary calls to the API and put them together by code. It’ll come down to whether you feel comfortable with coding this kind of thing.

HTH

Hi @fernando_delosrios
This solution worked. Appreciate all your effort in providing this great document.

Hi @jrossicare

How did you pull this private_key_password into the WSAO rule?

Currently it is configured as below to pull it from HTTP Operation headers :

String IDN_CLIENT_SECRET = (String) headers.get(“X-IDNClientSecret”);

Post adding the secret to the private_key_password, Can you confirm if this is how you’re pulling it into the rule below? :

String IDN_CLIENT_SECRET = (String) application.get(“private_key_password”);

And if yes, should we also handle decryption of this private_key_password in the WSAO?

Appreciate any help on this.

Thanks in advance,
Arshad.

Have you tried adding something like this to your connector and treating the attribute as a regular attribute? Does ISC automatically encrypt/decrypt it? Please ensure you’re using the regular, VA-dependent version of the connector if testing this:

{
  "connectorAttributes": {
    "encrypted": "private_key_password"
  }
}

Yes @fernando_delosrios, that is exactly how I’m trying to add the attribute to the source JSON.

{
	"connectorAttributes": {
		"encrypted": "private_key_password",
		"private_key_password": "SECRET_VALUE"
	}
}

And to your question, yes ISC would automatically encrypt/decrypt the values which are declared under encrypted.

The confirmation I would require is how to pull this private_key_password into the WSAO rule? Is it as mentioned below?

String IDN_CLIENT_SECRET = (String) application.get(“private_key_password”);

Oh, I see, it’s getStringAttributeValue.

1 Like