PingOne Advanced Identity Cloud

Temporal entitlement grant workflow example

Identity Governance supports temporal workflows, which let you schedule when a user’s access begins and ends. For example, you can grant a contractor access to an application that starts on their first day and automatically ends when their contract expires.

To build these time-based workflows, you use the Wait node to pause the workflow until the specified start or end date has been established.

Administrators need to carry out the following task:

Assumptions

  • You have designated an end user or a test user who can approve the request.

Example

Task: Create a workflow with the Wait node

Create a new workflow called Basic Entitlement Grant Temporal.

An example of an entitlement grant workflow with wait node.
  • 1 Use a Script node to perform a context check for the request.

    Click to display the request context check script
    var content = execution.getVariables();
    var requestId = content.get('id');
    var context = null;
    var enableWait = false;
    var enableEndWait = false;
    var skipApproval = false;
    try {
        // Read request object
        var requestObj = openidm.action('iga/governance/requests/' + requestId, 'GET', {}, {});
        if (requestObj.request.common.context) {
            context = requestObj.request.common.context.type;
            if (context == 'admin') {
                skipApproval = true;
            }
        }
        if (requestObj.request.common.startDate){
            enableWait = true;
        }
        if (requestObj.request.common.endDate){
            enableEndWait = true;
        }
    }
    catch (e) {
        logger.info("Request Context Check failed " + e.message);
    }
    
    logger.info("Context: " + context);
    execution.setVariable("context", context);
    execution.setVariable("enableWait", enableWait);
    execution.setVariable("enableEndWait", enableEndWait);
    execution.setVariable("skipApproval", skipApproval);
  • 2 Use an IF/ELSE node to set the context gateway.

  • 3 Use a 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 skipApproval = content.get('skipApproval');
    var queryParams = {
        "_action": "update"
    }
    try {
        var decision = {
            "decision": "approved",
        }
        if(skipApproval){
            decision.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 If a manual approval is required, use an Approval node for the entitlement owner to approve the request.

  • 5 Using a Switch node to check if the request has a specified start date.

  • 6 If a future start date exists, use a Wait node to pause the workflow until that date is reached.

  • 7 If the Approval node has the Reject outcome, use a Script node to deny the request.

    Click to display the reject request script
    logger.info("Rejecting request");
    
    var content = execution.getVariables();
    var requestId = content.get('id');
    
    // Complete request as rejected
    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 Use a Script node to run 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 the account doesn't 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) {
          var err = e.javaException;
            err = JSON.parse(err.detail);
            var message = err && err.body ? err.body.response : e.message;
            failureReason = "Validation failed: Error provisioning new account to user " + request.common.userId + " for application " + applicationId + ". Error message: " + message;
        }
      }
    }
    
    if (failureReason) {
      logger.info("Validation failed: " + failureReason);
    }
    execution.setVariable("failureReason", failureReason);
  • 9 Use the If/Else node to process validation success or failure.

  • 10 Use the Script node to provision the entitlement.

    Click to provision the entitlement
    logger.info("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,
          "auditContext": {},
          "grantType": "request",
          "requestId": requestObj.id,
        };
        var queryParams = {
          "_action": "add"
        }
    
        var result = openidm.action('iga/governance/user/' + request.common.userId + '/entitlements' , 'POST', payload,queryParams);
      }
      catch (e) {
        var err = e.javaException;
            err = JSON.parse(err.detail);
            var message = err && err.body ? err.body.response : e.message;
            failureReason = "Provisioning failed: Error provisioning entitlement to user " + request.common.userId + " for entitlement " + request.common.entitlementId + ". Error message: " + message;
      }
    
      var decision = {'status': 'complete', 'decision': 'approved'};
      if (failureReason) {
        decision.outcome = 'not provisioned';
        decision.comment = failureReason;
        decision.failure = true;
        execution.setVariable('enableEndWait', false);
      }
      else {
        decision.outcome = 'provisioned';
      }
    
      var queryParams = { '_action': 'update'};
      openidm.action('iga/governance/requests/' + requestId, 'POST', decision, queryParams);
      logger.info("Request " + requestId + " completed.");
    }
  • 11 Use the Script node to process the entitlement grant validation failure.

    Click to view the script for the entitlement grant validation failure
    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);
  • 12 Use a switch node to check if there’s an end date. If there’s an end date, run a removal request. If there’s no end date, complete the workflow.

  • 13 Use a Script node to create a removal request.

    Click to process the removal request
    logger.info("Create Removal Request");
    
    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 = "Create Removal Request failed: Error reading request with id " + requestId;
    }
    
    if(!failureReason){
        try{
            var request = requestObj.request;
            var newRequestPayload = {
                "common":{
                    "context": {
                        "type": "admin"
                    },
                    "entitlementId": request.common.entitlementId,
                    "userId": request.common.userId,
                    "endDate": request.common.endDate,
                    "justification": "Request submitted automatically to remove access granted by request: " + requestId
                }
            };
            var queryParam = {
                '_action': "publish"
            }
            openidm.action('iga/governance/requests/entitlementRemove', 'POST', newRequestPayload, queryParam)
        }catch(e){
            logger.warn('Create Removal Request failed to create')
        }
    }

Download the JSON file for this workflow here.

Learn more about importing or exporting workflows in workflow editor canvas.