Hi, I am working on a WebService connector and need help with “UpdateAccount” operation. The target endpoint supports HTTP PATCH method to update the roles.
Payload for upldating the role via API is:
{
"email": "[email protected] "
"groups": ["entitlement1", "entitlement2", "entitlement3", ..., ...etc]
}
In the source configuration, the account schema is having an attribute named “groups” configured as “Entitlement” and “Multi-Valued”.
We have Two access profiles each configured with entitlements as:
AccessPrfile1 - “entitlement1”, “entitlement2”
AccessPrfile2 - “entitlement1”, “entitlement2”, “entitlement3”, “entitlement4”
When a user (already having access to AccessProfile1) submits a request for AccessProfile2, SailPoint includes the “entitlement3” and “entitlement4” in the PATCH request, resulting in the target app overriding the current roles of the user.
Referring to the documentation, WebServices Before Operation Rule is an option to fix this. Can you suggest utilizing the WebServices Before Operation Rule is the only option to include the current entitlements? if yes, can you point me to a sample code snippet showing how to refer the user’s current entitlements in the JSON body so they are not removed when adding new ones.
Thanks
Hi
PATCH is a little bit different. Please see Colin’s message below.
You may need to create a Before Operation connector rule, where you make an API call to IDN to get the existing group memberships, then work out your final payload.
We have a PATCH guide for our APIs. Add and remove are tricky, so I highly encourage you to read the guide for both operations very carefully.
add operation guide
remove operation guide
The TLDR is that to add an item to an array, you must provide the array index in the path. For example, the below payload will add the HELPDESK capability at the beginning of the capabilities array.
[
{
"op": "add",
"path": "/capabilities/0",
"value": "HELPDESK"
}
]
Remove works similarly. Yo…
ipobeidi
(Ivan Obeidi)
April 3, 2024, 9:47pm
3
hey , you need to create a Before operation Rule!
To grab th elist from the user link , and add the new entitlement. Same for removal!
Have you tested the PATCH operation in Postman? If so, is the behavior as expected?
Please chare the request body used in Postman that will help us understand how your end system works
@iam_nithesh yep, tested in postman and the behavior is same. Here is the request body for PATCH operation.
url: https://hostname/users/{userId}
{
“email”: “[email protected] ”
“groups”: [“entitlement1”, “entitlement2”, “entitlement3”]
}
@jrossicare Thank you for your reply. At the moment, the back-end system does not support to send the index of the groups to patch. i.e. [ { “op”: “add / replace”, “path”: “/groups/0”, “value”: “entitlement” } ] is not accepted and we should send the existing groups/entitlements in the patch request.
can you pls advise how to make API call to IDN with in before operation rule?
Hi
What I do, is in my Before Operation rule, I make an API call to IDN to get the current account, then I update the groups and set that.
This isnt my full Before Operations rule, but it should give you an idea
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import connector.common.JsonUtil;
import connector.common.Util;
import sailpoint.connector.webservices.EndPoint;
import sailpoint.connector.webservices.WebServicesClient;
import sailpoint.object.Schema;
import sailpoint.object.Application;
import sailpoint.object.Attributes;
import sailpoint.object.ProvisioningPlan;
import sailpoint.object.ProvisioningPlan.AccountRequest;
import sailpoint.object.ProvisioningPlan.AccountRequest.Operation;
import sailpoint.object.ProvisioningPlan.AttributeRequest;
String logPrefix = "MySource WSBO - ";
log.error(logPrefix + "Starting - " + requestEndPoint.getOperationType());
Map body = requestEndPoint.getBody();
String jsonBody = (String) body.get("jsonBody");
String authenticate(String url, String clientId, String clientSecret) throws Exception {
//log.error(logPrefix + "WSlookup - start authenticate");
WebServicesClient client = new WebServicesClient();
Map args = new HashMap();
Map header = new HashMap();
Map payload = new HashMap();
List allowedStatuses = new ArrayList();
String request = String.format("%s/oauth/token?grant_type=client_credentials&client_id=%s&client_secret=%s", url, clientId, clientSecret);
args.put(WebServicesClient.ARG_URL, request);
header.put("Accept", "application/json");
allowedStatuses.add("200");
client.configure(args);
try {
String response = client.executePost(request, payload, header, allowedStatuses);
//log.error(logPrefix + "WSlookup - Authenticate - response: " + response);
Map responseMap = JsonUtil.toMap(response);
String accessToken = (String) responseMap.get("access_token");
log.error(logPrefix + "IDN Authentication successful- got accessToken");
return accessToken;
} catch (Exception e) {
throw new Exception(String.format("IDN Authentication failed: %s", e.getMessage()));
}
}
String getSourceAccount(String url, String accessToken, String appId, String nativeId) throws Exception {
//log.error(logPrefix + "WSlookup - start getSourceAccount");
WebServicesClient client = new WebServicesClient();
Map args = new HashMap();
Map header = new HashMap();
List allowedStatuses = new ArrayList();
String request = String.format("%s/beta/accounts?filters=sourceId eq \"%s\" and nativeIdentity eq \"%s\"", url, appId, nativeId);
log.error(logPrefix + "getSourceAccount - request: " + request);
args.put(WebServicesClient.ARG_URL, request);
header.put("Authorization", String.format("Bearer %s", accessToken));
allowedStatuses.add("200");
client.configure(args);
try {
String response = client.executeGet(request, header, allowedStatuses);
//log.error(logPrefix + "WSlookup - getSourceAccount - returning response");
return response;
} catch (Exception e) {
throw new Exception(String.format("getSourceAccount call failed: %s", e.getMessage()));
}
}
JsonArray groups33 = new JsonArray();
ArrayList<String> myGroupsArray = new ArrayList<String>();
for (AccountRequest accReq : Util.iterate(provisioningPlan.getAccountRequests())) {
log.error(logPrefix + "AccountRequest Operation: " + accReq.getOperation().toString() );
for (ProvisioningPlan.AttributeRequest attReq : Util.iterate(accReq.getAttributeRequests())) {
log.error(logPrefix + "attReq: " + attReq.getName() + " => " + attReq.getValue() + " (" + attReq.getValue().getClass() + ")");
String attrName = attReq.getName();
if (attrName != null && "groups".equalsIgnoreCase(attrName)) {
log.error(logPrefix + "groups in Attribute Request");
if (attReq.getValue() != null && attReq.getValue() instanceof String) {
log.error(logPrefix + "groups (String) in Attribute Request");
//groupValue = (String) attReq.getValue();
// rather than the above, add value to myGroupsArray, then lower down we only care about myGroupsArray
myGroupsArray.add(attReq.getValue());
} else if (attReq.getValue() != null && attReq.getValue() instanceof ArrayList) {
log.error(logPrefix + "groups (ArrayList) in Attribute Request");
myGroupsArray = attReq.getValue();
}
}
}
if ( accReq.getOperation().equals(ProvisioningPlan.AccountRequest.Operation.Modify) || accReq.getOperation().equals(ProvisioningPlan.AccountRequest.Operation.Enable)) {
log.error(logPrefix + "AccountRequest Operation is MODIFY");
String ACCESS_TOKEN = authenticate(IDN_URL, IDN_CLIENT_ID, IDN_CLIENT_SECRET);
//log.error(logPrefix + "ACCESS_TOKEN: " + ACCESS_TOKEN);
String nativeIdentity = accReq.getNativeIdentity();
log.error(logPrefix + "AccountRequest getNativeIdentity: " + nativeIdentity );
log.error(logPrefix + "Looking up existing roles and groups of user: " + nativeIdentity);
String idnAccount = getSourceAccount(IDN_URL, ACCESS_TOKEN, APP_ID, nativeIdentity);
//log.error(logPrefix + "idnAccount: " + idnAccount);
// Check it is not null
JsonParser parser = new JsonParser();
JsonArray jsonArray2 = parser.parse(idnAccount).getAsJsonArray();
JsonObject idnAccountJson = jsonArray2.get(0).getAsJsonObject();
//log.error(logPrefix + "idnAccountJson: " + idnAccountJson.toString());
JsonObject attributes = idnAccountJson.getAsJsonObject("attributes");
groups33 = attributes.getAsJsonArray("groups");
log.error(logPrefix + "groups33: " + groups33);
if (!myGroupsArray.isEmpty()){
if (myOp2 != null && "remove".equalsIgnoreCase(myOp2)) {
log.error(logPrefix + "Removing groups \"" + myGroupsArray + "\" from array: " + groups33);
for (String groupValue : myGroupsArray) {
for (int i = 0; i < groups33.size(); i++) {
JsonElement element2 = groups33.get(i);
if (element2.isJsonPrimitive() && element2.getAsString().equals(groupValue)) {
log.error(logPrefix + "Removing group: " + groupValue);
groups33.remove(i);
// Decrement the index to account for the removed element
i--;
}
}
}
log.error(logPrefix + "After Removing all required groups: " + groups33);
}
if (myOp2 != null && "add".equalsIgnoreCase(myOp2)) {
log.error(logPrefix + "Adding groups \"" + myGroupsArray + "\" to array: " + groups33);
for (String groupValue : myGroupsArray) {
groups33.add(groupValue);
}
log.error(logPrefix + "After Adding group: " + groups33);
}
paramsObject.add("groups", groups33);
}
String finalBody = JsonUtil.render(jsonMap2);
log.error(logPrefix + "Setting finalBody: " + finalBody);
body.put("jsonBody", finalBody);
requestEndPoint.setBody(body);
}
}
}
} catch (Exception ex) {
log.error(logPrefix + "Exception caught: " + ex);
}
return requestEndPoint;
1 Like
system
(system)
Closed
June 2, 2024, 10:48pm
8
This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.