Entitlement grant workflow
In this example, an administrator wants to create an entitlement grant workflow that:
-
An entitlement owner must approve the request. If an administrator sends a request, the request is auto-approved.
-
If approved, check if the entitlement is marked as
privileged
.Companies can define what privileged
means in the glossary. However, in most cases, aprivileged
entitlement gives administrative rights to sensitive data, such as view access to quarterly reports before public release. -
If the entitlement is privileged or null, the end user’s manager must approve the request.
Assumptions
-
Each entitlement has an entitlement owner.
-
You create a boolean entitlement glossary attribute ,
isPrivileged
. This attribute is populated for each entitlement. -
Your end users have a manager assigned to them. An administrator populates this property and isn’t modifiable by the end user.
Example
-
1 Use a Script node to do a context check for the request.
Click to display the request context check script
/* Script nodes are used to invoke APIs or execute business logic. You can invoke governance APIs or IDM APIs. Learn more in https://backstage.forgerock.com/docs/idcloud/latest/identity-governance/administration/workflow-configure.html. Script nodes should return a single value and should have the logic enclosed in a try-catch block. Example: try { var requestObj = openidm.action('iga/governance/requests/' + requestId, 'GET', {}, {}); applicationId = requestObj.application.id; } catch (e) { failureReason = 'Validation failed: Error reading request with id ' + requestId; } */ var content = execution.getVariables(); var requestId = content.get('id'); var context = null; var skipApproval = false; var lineItemId = false; try { var requestObj = openidm.action('iga/governance/requests/' + requestId, 'GET', {}, {}); if (requestObj.request.common.context) { context = requestObj.request.common.context.type; lineItemId = requestObj.request.common.context.lineItemId; if (context == 'admin') { skipApproval = true; } } } catch (e) { logger.info("Request Context Check failed "+e.message); } logger.info("Context: " + context); execution.setVariable("context", context); execution.setVariable("lineItemId", lineItemId); execution.setVariable("skipApproval", skipApproval);
-
2 Use an IF/ELSE node to set the context gateway.
-
3 Use the Script node to run an auto approval:
Click to display the auto approval script
var content = execution.getVariables(); var requestId = content.get('id'); var context = content.get('context'); var lineItemId = content.get('lineItemId'); var queryParams = { "_action": "update" } var lineItemParams = { "_action": "updateRemediationStatus" } try { var decision = { "decision": "approved", "comment": "Request auto-approved due to request context: " + context } openidm.action('iga/governance/requests/' + requestId, 'POST', decision, queryParams); } catch (e) { var failureReason = "Failure updating decision on request. Error message: " + e.message; var update = {'comment': failureReason, 'failure': true}; openidm.action('iga/governance/requests/' + requestId, 'POST', update, queryParams); }
-
4 Using an Approval node, the entitlement owner must approve the request.
-
5 Use a Script node to check the value of the entitlement glossary attribute
isPrivileged
and set the outcome.Click to display the entitlement privileged script
var content = execution.getVariables(); var requestId = content.get('id'); var requestObj = null; var entId = null; var entGlossary = null; var entPriv = null; //Check entitlement exists and grab entitlement info try { requestObj = openidm.action('iga/governance/requests/' + requestId, 'GET', {}, {}); entId = requestObj.assignment.id; } catch (e) { logger.info("Validation failed: Error reading entitlement grant request with id " + requestId); } //Check glossary for entitlement exists and grab glossary info try { entGlossary = openidm.action('iga/governance/resource/' + entId + '/glossary', 'GET', {}, {}); // Sets entPriv based on the glossary contents, if present, otherwise defaults to true entPriv = (entGlossary.hasOwnProperty("isPrivileged")) ? entGlossary.isPrivileged : true; //Sets entPriv based on glossary contents, if present, otherwise defaults to false //entPriv = !!entGlossary.isPrivileged; execution.setVariable("entPriv", entPriv); } catch (e) { logger.info("Could not retrieve glossary with entId " + entId + " from entitlement grant request ID " + requestId); }
-
6 Use a switch node to route outcomes based on the script. If the outcome is:
-
privileged
(entPriv==true
) — An additional Approval node requires the end user’s manager to approve the request. -
notPrivileged
(entPriv==false`) — An additional approval isn’t required.
-
-
7 If the required approvals are met, the Script node runs a validation check.
Click to display the entitlement grant validation script
logger.info("Running entitlement grant request validation"); var content = execution.getVariables(); var requestId = content.get('id'); var failureReason = null; var applicationId = null; var assignmentId = null; var app = null; var assignment = null; var existingAccount = false; try { var requestObj = openidm.action('iga/governance/requests/' + requestId, 'GET', {}, {}); applicationId = requestObj.application.id; assignmentId = requestObj.assignment.id; } catch (e) { failureReason = "Validation failed: Error reading request with id " + requestId; } // Validation 1 - Check application exists if (!failureReason) { try { app = openidm.read('managed/alpha_application/' + applicationId); if (!app) { failureReason = "Validation failed: Cannot find application with id " + applicationId; } } catch (e) { failureReason = "Validation failed: Error reading application with id " + applicationId + ". Error message: " + e.message; } } // Validation 2 - Check entitlement exists if (!failureReason) { try { assignment = openidm.read('managed/alpha_assignment/' + assignmentId); if (!assignment) { failureReason = "Validation failed: Cannot find assignment with id " + assignmentId; } } catch (e) { failureReason = "Validation failed: Error reading assignment with id " + assignmentId + ". Error message: " + e.message; } } // Validation 3 - Check the user has application granted if (!failureReason) { try { var user = openidm.read('managed/alpha_user/' + requestObj.user.id, null, [ 'effectiveApplications' ]); user.effectiveApplications.forEach(effectiveApp => { if (effectiveApp._id === applicationId) { existingAccount = true; } }) } catch (e) { failureReason = "Validation failed: Unable to check existing applications of user with id " + requestObj.user.id + ". Error message: " + e.message; } } // Validation 4 - If account does not exist, provision it if (!failureReason) { if (!existingAccount) { try { var request = requestObj.request; var payload = { "applicationId": applicationId, "startDate": request.common.startDate, "endDate": request.common.endDate, "auditContext": {}, "grantType": "request" }; var queryParams = { "_action": "add" } logger.info("Creating account: " + payload); var result = openidm.action('iga/governance/user/' + request.common.userId + '/applications' , 'POST', payload,queryParams); } catch (e) { failureReason = "Validation failed: Error provisioning new account to user " + request.common.userId + " for application " + applicationId + ". Error message: " + e.message; } } } if (failureReason) { logger.info("Validation failed: " + failureReason); } execution.setVariable("failureReason", failureReason);
If any Approval node has the
Reject
outcome, a Script node denies the request.Click to display the reject request script
logger.info("Rejecting request"); var content = execution.getVariables(); var requestId = content.get('id'); logger.info("Execution Content: " + content); var requestIndex = openidm.action('iga/governance/requests/' + requestId, 'GET', {}, {}); var decision = {'outcome': 'denied', 'status': 'complete', 'decision': 'rejected'}; var queryParams = { '_action': 'update'}; openidm.action('iga/governance/requests/' + requestId, 'POST', decision, queryParams);
-
8 If the
If/else
node outcome is:-
validationFlowSuccess
— A Script node provisions the application to the end user.Click to display the auto provisioning script
logger.info("Auto-Provisioning"); var content = execution.getVariables(); var requestId = content.get('id'); var failureReason = null; try { var requestObj = openidm.action('iga/governance/requests/' + requestId, 'GET', {}, {}); logger.info("requestObj: " + requestObj); } catch (e) { failureReason = "Provisioning failed: Error reading request with id " + requestId; } if(!failureReason) { try { var request = requestObj.request; var payload = { "entitlementId": request.common.entitlementId, "startDate": request.common.startDate, "endDate": request.common.endDate, "auditContext": {}, "grantType": "request" }; var queryParams = { "_action": "add" } var result = openidm.action('iga/governance/user/' + request.common.userId + '/entitlements' , 'POST', payload,queryParams); } catch (e) { failureReason = "Provisioning failed: Error provisioning entitlement to user " + request.common.userId + " for entitlement " + request.common.entitlementId + ". Error message: " + e.message; } var decision = {'status': 'complete', 'decision': 'approved'}; if (failureReason) { decision.outcome = 'not provisioned'; decision.comment = failureReason; decision.failure = true; } else { decision.outcome = 'provisioned'; } var queryParams = { '_action': 'update'}; openidm.action('iga/governance/requests/' + requestId, 'POST', decision, queryParams); logger.info("Request " + requestId + " completed."); }
-
validationFlowFailure
— A Script node doesn’t provision the application to the end user.Click to display the validation failure script
var content = execution.getVariables(); var requestId = content.get('id'); var failureReason = content.get('failureReason'); var decision = {'outcome': 'not provisioned', 'status': 'complete', 'comment': failureReason, 'failure': true, 'decision': 'approved'}; var queryParams = { '_action': 'update'}; openidm.action('iga/governance/requests/' + requestId, 'POST', decision, queryParams);
-
Download the JSON file for this workflow here. For information on how to import or export workflows, refer to workflow editor canvas. |