Split Provisioning - Certification Campaigns

Continuing the discussion from Split Provisioning in IDN?:

It seems most Provisioning has been fixed for the new request center, sending 1 request even if requesting multiple access objects through the request center. However, when removing multiple access objects in a Certification Campaign provisioning is still bundled into 1 operation for multiple accesses being revoked.

Is this the expected behaviour?

Currently we’re implementing a Web Service Connector where the PATCH operation only permits removing 1 entitlement per API request. How can we implement logic into our BeforeProvisioning Rule to allow sending multiple API requests if the provisioning plan contains more than 1 object (i.e as the situation with the current Certification behaviour).

Best regards,
Seb

@colin_mckibben Could you possibly shed some light on this issue? :slight_smile:

Thanks,
Seb

Yes, this is the current behavior for certifications, which are a separate process from request center. We would treat any changes to the certification process as an enhancement, which will need to be an idea in ideas.sailpoint.com. Can you please submit one with your desired behavior for certifications revoking access.

As for the before provision rule to send multiple APi requests, could you just loop on the entitlements in the provisioning plan and send a patch request per entilement.

Ah I see, ok thanks for the info!

Do you have a good idea of how I could alter this code to fit both scenarios?

if (provisioningPlan != null && provisioningPlan.getAccountRequests().size() == 1) {

    String requestedAccess = provisioningPlan.getAccountRequests().get(0).getAttributeRequests().get(0).getValue();
    String[] entArr = requestedAccess.split(" - ");
    log.error("xxx request: " + entArr[0] + ":" + entArr[1]);

    String jsonBody = "[\n" +
        "{\n" +
            "\"op\": \"RemoveById\",\n" +
            "\"path\": \"/roleAndCompany\",\n" +
            "\"value\": {\n" +
                "\"roleId\": \"" + entArr[0] + "\",\n" +
                "\"companyId\": \"" + entArr[1] + "\",\n" +
            "}\n" +
        "}\n" +
    "]";

    Map body = requestEndPoint.getBody();
    body.put("jsonBody", jsonBody);
    requestEndPoint.setBody(body);
} else if (provisioningPlan != null && provisioningPlan.getAccountRequests().size() > 1){
    log.error("provPlans123: " + provisioningPlan.getAccountRequests().get(0).getAttributeRequests().get(0).getValue());

} else {
    log.info("Rule: plan is null");
}

I’m not familiar enough with rules to know how to do this. I think there is an HTTP client you can use within a rule, so you could just loop over each account request and send the patch API call to the target source for each account request. However, I’m not sure how that will impact the provisioning plan…

Ok thanks for the input!

Is it possible to loop over each account request and in the rule then create separate provisioningPlans without making several custom PATCH API calls? We already have a PATCH operation configured for the source in the GUI so we want to avoid setting up several API calls within the rule.

Maybe you have some input regarding this? @tyler_mairose

Thanks in advance.

Se

I realised just now that the only difference in Certification Campaigns is that the request object values in a campaign is of type PersistentArrayList (if there are more than 1 access-item being revoked), while all other requests outside of Certifications are of type String. My initial thought was that objects from a campaign would be bundled inside of 1 request and therefore an ArrayList, but it seems like this is not the case.

My solution to this was to match these 2 types, and if the request was of type PersistentArrayList i would loop through the requestedItemsList and send a JsonBody equivalent to these items. I realised that the API call was in fact working (200), but I was still receiving an error in IDN.

I then started wondering if perhaps campaigns actually do send 1 request per access-item and so I tried sending my requests from campaigns to a Webhook in stead. The result: It does, in fact, send 1 request per Access-object being removed. The only difference, like I said before, is that these are of type PersistentArrayList.

The error I am receiving now is because the API call finishes all tasks in the 1st API request, crashing on the 2nd request where there no longer is any object to be found (404).

Code as of now:

if (provisioningPlan != null && provisioningPlan.getAccountRequests().get(0).getAttributeRequests().get(0).getValue().getClass() == String.class) {

    String requestedAccess = provisioningPlan.getAccountRequests().get(0).getAttributeRequests().get(0).getValue();
    String[] entArr = requestedAccess.split(" - ");


    String jsonBody = "[\n" +
        "{\n" +
            "\"op\": \"RemoveById\",\n" +
            "\"path\": \"/roleAndCompany\",\n" +
            "\"value\": {\n" +
                "\"roleId\": \"" + entArr[0] + "\",\n" +
                "\"companyId\": \"" + entArr[1] + "\",\n" +
            "}\n" +
        "}\n" +
    "]";

    Map body = requestEndPoint.getBody();
    body.put("jsonBody", jsonBody);
    requestEndPoint.setBody(body);
} else if (provisioningPlan != null && provisioningPlan.getAccountRequests().get(0).getAttributeRequests().get(0).getValue().getClass() == sailpoint.tools.xml.PersistentArrayList.class) {

    List requestedAccessList = provisioningPlan.getAccountRequests().get(0).getAttributeRequests().get(0).getValue();
    StringBuilder jsonBodyBuilder = new StringBuilder("[\n");

    for (int i = 0; i < requestedAccessList.size(); ++i) {
        String[] entArr = requestedAccessList.get(i).toString().split(" - ");

        // Build JSON body for  each item in the list
        String itemJsonBody = 
            "{\n" +
            "  \"op\": \"RemoveById\",\n" +
            "  \"path\": \"/roleAndCompany\",\n" +
            "  \"value\": {\n" +
            "    \"roleId\": \"" + entArr[0] + "\",\n" +
            "    \"companyId\": \"" + entArr[1] + "\"\n" +
            "  }\n" +
            "}";
        
        jsonBodyBuilder.append(itemJsonBody);

        // Add a comma and newline for all items except the last one
        if (i < requestedAccessList.size() - 1) {
            jsonBodyBuilder.append(",\n");
        }
    }
    
    jsonBodyBuilder.append("\n]");

    Map body = requestEndPoint.getBody();
    body.put("jsonBody", jsonBodyBuilder.toString());
    requestEndPoint.setBody(body);

} else {
    log.info("Rule: plan is null");
}



return requestEndPoint;

Fixed this issue by adding the attribute addRemoveEntInSingleReq to the connectorAttributes for the source and setting it to true.

[
    {
        "op": "add",
        "path": "/connectorAttributes/addRemoveEntInSingleReq",
        "value": true
    }
]
2 Likes

Thanks for posting this, it helped me a lot.

I was about to raise a support ticket to ask why multiple Remove Entitlement requests for the same thing. This resolved it.

1 Like