Display multiple account id using transform

I want to use one transform to display more than one account for one of my application. Lets say the field name is ACCOUNT_NAME which has multiple ids for one user in oracle db… can someone suggest me transform example to display all account ids associated for one user. Currently only one account id is displaying without using transform.

Are you trying to display all the values in a multi-valued attribute from an account ?

If yes, then you cannot display more than 1 value using any Transform. Currently Transform doesn’t support fetching all values from multi-valued attributes. It just displays only first value.

If your requirement is to see whether user has a specific id then you can use account property filter attribute in Account Attribute Transform.

Else, if you really need to display all the values in a specific attribute then you need to use Identity Attribute Rule which is Cloud Rule.

Thanks
Krish

2 Likes

Agree - The specific use case seems a little strange that a single database field would have multiple values. Perhaps this is actually multiple records in a database table which could be handled via some other options (i.e. multiple sources most likely)

1 Like

Let me try to make it more simple to undestand, Lets suppose there is one User -
User name - Test User1
This user has multiple accounts in one oracle database
Account names - ABCSP01 ABCSP02 ABCSP03 ABCSP04
In my identity profile I have used the account attribute name (no transform as of now). When I search this Test User1 in Identity List, it displays only ABCSP01 as account name and skip rest of the accounts. I want to display all these account names separated by comma.
Could you please review this use case and suggest me solution.

Ok, So user has 4 accounts on same application.

Do you have any conditions on these accounts, if yes then you can use Account Attribute Transform with Account Property filter.

{
  "attributes": {
    "attributeName": "accountName",
    "sourceName": "Oracle Database",
    "accountPropertyFilter": "!(displayName.startsWith(\"SVC-\"))"
  },
  "type": "accountAttribute",
  "name": "Account Attribute Transform"
}

Using this Transform you can fetch Account name from a specific account, you can use concatenation transform to concatenate all values as CSV.

In case if you don’t have any conditions like that, just multiple accounts user has then you need to look into Rules.

Hi Krishna,
Thanks a lot for your response! In my use case there is no condition for accounts, it just has multiple accounts. If possible, Could you please provide me some sample rule which I can use in my case.

You can try this Rule.

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule name="Get Oracle DB Accounts" type="IdentityAttribute">
  <Description>Describe your rule here.</Description>
  <Source><![CDATA[
	import sailpoint.rule.Account;
	import sailpoint.object.Identity;
	import java.util.List;
	import sailpoint.tools.Util;
	
	String identityName = identity.getName();
	String appName = "oracle DB";
	
	List accountNameList = new ArrayList();
	
	List accountList = new ArrayList();
	accountList = idn.getAllAccounts(appName, identityName);
	
	if (accountList.size() > 0) {
	
		for (Account account : accountList) {
			
			accountNameList.add(account.getNativeIdentity());
		}
	
	}

	String accountNames = "";
	
	if (accountNameList.size() > 0) {
		accountNames = Util.listToCsv(accountNameList);
	}
	
  return accountNames;

  ]]></Source>
</Rule>

Hi Krishna,
Thanks a lot for your response and sample rule. I will try this and update you on how it works!

Part of the key to solving this is knowing the underlying account data. Assuming these accounts are all the same type (i.e. general user accounts vs. general, admin, break-glass, etc.), then I’m assuming something like I’ve seen in numerous legacy internal applications where separate accounts are used to segregate data (I’m “old enough” :wink: to be from these days of development)

The key is having something on each of the individual accounts to correlate them back to a single identity. If this is the case, then I’d suggest aggregating them as separate accounts to a single identity from a single source. Not a great situation in IDN, but definitely better than “accounts” in a multi-value attribute where you give up a lot specific to each of the accounts.

1 Like

Did you deploy this Rule in cloud, what were the test results ?

Hello @hranjan3,

I believe this can be done using a transform, although the code isn’t as pretty when included as a string on the static transform as the rule below is.

Using the $identity object available to you in transforms you can call $identity.getLinks() this will return all accounts tied to that identity.

If you just need a list of all account names you could use the static transform like so.

{
  "attributes": {
    "value": "#foreach($link in $identity.getLinks())#if($foreach.index + 1 != $identity.getLinks().size())$link.getNativeIdentity(),#else$link.getNativeIdentity()#end#end"
  },
  "type": "static"
}

If we look at this outside of the transform, we can see what it is doing a little better.

#foreach($link in $identity.getLinks()) ## Loop through all the Identities Accounts
  #if($foreach.index + 1 != $identity.getLinks().size()) ## Use the index of the loop to determine if we are on the last result
    $link.getNativeIdentity(),  ## If we are not on the last result, return the comma
  #else
    $link.getNativeIdentity() ## If we are on the last result return the account name without the comma
  #end
#end

If I understand correctly, it sounds like you want to go one step further though and you only want the accounts of the identity for a particular source. That can also be accomplished with a little more velocity magic.

The transform

{
    "attributes": {
        "value": "#set($tmpArray=[])#set($applicationAccounts=[])#foreach($link in $identity.getLinks())#if($link.getApplication().getName() == 'webservice-airtable-tyler [source]')#set($tmpArray=$applicationAccounts.add($link))#else#end#end#foreach($account in $applicationAccounts)#if($foreach.index + 1 != $identity.getLinks().size())$account.getNativeIdentity(),#else$account.getNativeIdentity()#end#end"
    },
    "type": "static"
}

The code & explaination

#set($tmpArray=[]) ## Array to hold the returned boolean from the array add function (If we don't include this 'true' will be printed out each time we add an account to the applicationAccounts array)
#set($applicationAccounts=[]) ## Array for storing application accounts that match the application name
#foreach($link in $identity.getLinks()) ## Loop through the identities accounts
  #if($link.getApplication().getName() == 'webservice-airtable-tyler [source]') ## If the accounts application name matches the string provided 
   #set($tmpArray=$applicationAccounts.add($link)) ## Add them to the applicationAccounts array.
  #else
  #end
#end
#foreach($account in $applicationAccounts) ## Loop through the applicationAccounts added
  #if($foreach.index + 1 != $applicationAccounts) ## Only add a comma if it is not the last account in the array.
    $account.getNativeIdentity(),
  #else
    $account.getNativeIdentity()
  #end
#end

Let me know if this solves your use case or if you find this helpful!

3 Likes

Thanks @tyler_mairose didn’t know that Transform supports object model. Not sure from when onwards this made available in static Transform. If it can support more operations to build core logic then we don’t need to depend on Cloud Rules for Identity attributes.

2 Likes

I missed a method on the $identity object, this can actually be done in a more simple way.

There is a method getLinksByAppIdOrName(String appId, String appName) that as it states returns accounts given an id or name of an application.

Including this results in the following transform:

{
    "attributes": {
        "value": "#foreach($link in $identity.getLinksByAppIdOrName(\"2c918088814e6a610181686b56977fa8\",null))#if($foreach.index + 1 != $identity.getLinks().size())$link.getNativeIdentity(),#else$link.getNativeIdentity()#end#end"
    },
    "type": "static"
}

The code:

#foreach($link in $identity.getLinksByAppIdOrName(\"2c918088814e6a610181686b56977fa8\",null)) ## Pull accounts with the app id = 2c918088814e6a610181686b56977fa8
  #if($foreach.index + 1 != $identity.getLinks().size()) ## Use the index of the loop to determine if we are on the last result
    $link.getNativeIdentity(),  ## If we are not on the last result, return the comma
  #else
    $link.getNativeIdentity() ## If we are on the last result return the account name without the comma
  #end
#end
1 Like

@MVKR7T,

I am not sure exactly when it was added or if it was always available. I know that accessing the $identity object instance has been available for a couple years.

I only recently started playing around with it myself, just seeing what is possible/available.

The $identity instance available is equivalent to an instance of sailpoint.object.Identity. With that being said, that is your only entry point into the object model for transforms. You wouldn’t be able to access anything like $application. You could get the application object by calling
$identity.getLinksByAppIdOrName("2c918088814e6a610181686b56977fa8",null).getApplication()

I’m still playing around with what is possible within velocity and the static transform and plan to do a write up or guide on developer.sailpoint.com

1 Like

Hi Tyler,
First of all thanks a lot for your response. I created transform as per your suggestion but while assigning transform in one attribute in provisioning policy, I am getting error message on sailpoint console “Please contact your administrator.” (on click on save button in provisioning policy)

Could you please let me know if I am missing something here.

First Transform

{
        "name": "Org_Transform_AppName_Accounts_1",
        "type": "static",
        "attributes": {
            "value": "#set($tmpArray=[] )#set($applicationAccounts=[] )#foreach($link in $identity.getLinks())#if($link.getApplication().getName() == 'Org_App_AppName_jdbc [source]')#set($tmpArray=$applicationAccounts.add($link))#else#end#end#foreach($account in $applicationAccounts)#if($foreach.index + 1 != $identity.getLinks().size())$account.getNativeIdentity(),#else$account.getNativeIdentity()#end#end"
        },
        "internal": false
    }

Second transform:

{
	"name": "Org_Transform_AppName_Accounts_2",
	"type": "static",
	"attributes": {
		"value": "#foreach($link in $identity.getLinksByAppIdOrName(\"dbda9f25e409412f93996dc3b1aa13e5\",null))#if($foreach.index + 1 != $identity.getLinks().size())$link.getNativeIdentity(),#else$link.getNativeIdentity()#end#end"
	},
	"internal": false
}

Hello @hranjan3,

If you’ve created the transforms you should be able to apply them to a field in the identity profile mappings tab under Admin->Identities->IdentityProfiles->->Mappings.

If you’re trying to use this transform in a provisioning policy it will need to be applied via API. Based upon your description “on click of the save button in provisioning policy” it sounds like you are trying to apply it in the UI.

In order to add the new field to the provisioning policy you can use the following patch provisioning policy call below:

/v3/sources/:sourceId/provisioning-policies/:usageType

Request Body:

[
    {
        "op": "add",
        "path": "/fields/-",
        "value": {
            "name": "accountNames",
            "transform": {
                "type": "static",
                "attributes": {
                    "value": "#foreach($link in $identity.getLinksByAppIdOrName(\"dbda9f25e409412f93996dc3b1aa13e5\",null))#if($foreach.index + 1 != $identity.getLinks().size())$link.getNativeIdentity(),#else$link.getNativeIdentity()#end#end"
                }
            },
            "attributes": {},
            "isRequired": false,
            "type": null,
            "isMultiValued": false
        }
    }
]
1 Like

Hello Tyler,

Finally it worked as per use case. Thank you so much for your help!

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.