Adding directPermissions to Entitlements in SaaS Connectors

I noticed that some off-the-shelf SaaS connectors include a directPermissions property in their entitlement metadata, and I was curious how I could include this in my own SaaS connector? From what I can tell, it’s not documented, and the only example I can see is @colin_mckibben’s SaaS connector

Specifically, here in the std:entitlement:list

.stdEntitlementList(async (context: Context, input: any, res: Response<StdEntitlementListOutput>) => {
            const response1 = await client.getRoles()
            for (const gr of response1.data) {
                const group: Group = new Group(gr)
                const response2 = await client.getRoleDuties(group.identity)
                const duties = new Set<string>(response2.data.map((x: { name: string }) => x.name))
                group.attributes.duties = Array.from(duties).sort()
                const dutyIds = response2.data.map((x: { dutyId: any }) => x.dutyId)
                if (config.includePermissions) {
                    const permissions = new Set<string>()
                    for (const duty of dutyIds) {
                        const response3 = await client.getDutyPrivileges(duty)
                        for (const privilege of response3.data) {
                            permissions.add(privilege.permission.name)
                        }
                    }
                    group.attributes.permissions = Array.from(permissions).sort()
                }

                logger.info(group)
                res.send(group)
            }
        })

it appears it’s being added to the entitlement attribute as an array of string values, but I’ve seen in the off-the-shelf CyberArk connector that it appears an array of objects is also possible

"directPermissions": [
            {
                "target": "PRIV_CHK_MCHEEK",
                "rights": [
                    "accessWithoutConfirmation",
                    "addAccounts",
                    "backupSafe",
                    "createFolders",
                    "deleteAccounts",
                    "deleteFolders",
                    "initiateCPMAccountManagementOperations",
                    "listAccounts",
                    "manageSafe",
                    "manageSafeMembers",
                    "moveAccountsAndFolders",
                    "renameAccounts",
                    "requestsAuthorizationLevel1",
                    "retrieveAccounts",
                    "specifyNextAccountContent",
                    "unlockAccounts",
                    "updateAccountContent",
                    "updateAccountProperties",
                    "useAccounts",
                    "viewAuditLog",
                    "viewSafeMembers"
                ]
            }
]

So, what’s within the realm of possibility here? Do we just return an object mapped to a permissions property?

Looking for guidance from @fernando_delosrios and @philip-ellis as well

Hooray… I figured it out.

If you look at the definition of StdEntitlementListOutput, you’ll see that permissions is a defined property

import { Attributes, CommandState, EntitlementSchema, ObjectOutput, Permission } from './command';
/**
 * Input object of `std:entitlement:list` command
 */
export type StdEntitlementListInput = {
    type: string;
    stateful?: boolean;
    state?: CommandState;
    schema?: EntitlementSchema;
};
/**
 * Output object of `std:entitlement:list` command
 */
export type StdEntitlementListOutput = ObjectOutput & {
    type: string;
    deleted?: boolean;
    attributes: Attributes;
    permissions?: Permission[];
};
//# sourceMappingURL=std-entitlement-list.d.ts.map

You’ll see that the value is Permission, and if you look at that definition, you’ll see

export type Permission = {
    target: string;
    rights: string;
    annotation?: string;
};

That is notably absent from the example in the documentation

Ok, so we know that there is a way to send permissions from the connector to ISC… so how do you do it?

First, you need to make sure that object type is imported into your entitlement model

import { Permission } from "@sailpoint/connector-sdk";

Then you need to add it to your class and constructor… here’s how I did it

import { Attributes } from "@sailpoint/connector-sdk";
import { Permission } from "@sailpoint/connector-sdk";

export class SafeRole {
    identity: string
    uuid: string
    type: string = 'safeRole'
    attributes: Attributes
    permissions: Permission[]

    constructor(object: any) {
        this.attributes = {
            id: object.id?.toString(),
            displayName: object.displayName,
            description: object.description
        }
        this.identity = this.attributes.id as string
        this.uuid = this.attributes.displayName as string
        this.permissions = object.permissions
    }
}

You’ll note that in my original post, the example entitlement had a permission where its rights property was an array of strings, so that must mean you’d be fine to return an array as the value of your rights property, right? Wrong!

Remember that the rights property as defined in the Permission definition is a string, so if you want to send multiple rights back for a permission, make sure you convert your array to a string

const safePermissions: any = [{
    "target": safe.id,
    "rights": role.rights.toString()
}]
const response: SafeRole = new SafeRole({
    id: `${safe.id} - ${role.name}`,
    displayName: `${safe.id} - ${role.name}`,
    description: role.description,
    permissions: safePermissions

})
res.send(response)

then somehow magically, that array that you converted to a string is somehow magically converted back to an array when you look at the entitlement

"directPermissions": [
        {
            "target": "PRIV_CHK_MCHEEK",
            "rights": [
                "listAccounts",
                "viewAuditLog",
                "viewSafeMembers"
            ]
        }
    ]

@philip-ellis I’m curious why permission.rights expects a string even though it can convert a comma-separated string back into an array?

Thanks Mark. I will have @jthaytko look into the documentation.

Thanks, and welcome back!