Share all details about your problem, including any error messages you may have received.
Hi All, I am working on Add Entitlement operation with a web service connector application. I am facing a logic issue where I need to make multiple api calls in one request of adding one or many entitlement to an identity through Manage User Access in SailPoint. The scenario is as below:
Identity A to raise a request to add 3 entitlements:
a. Entitlement A
b. Entitlement B
c. Entitlement C
Based on API specs the context url value in <<>> are supposed to be the name of the entitlement raised in the request.
Hi @dylanfoggan, I am unable to use this solution as the api is built to accept the name of the entitlement in the context url and not in the body of the request
What you could perhaps do is then create a WebServiceBeforeOperationRule and execute each of the API calls in the Rule. Then in the operation just configure it to be a “Get Account”.
This is going to allow us to send all of the entitlements that the user requested in a List of items so that we can modify it in the rule.
Next we have the BeforeOperationRule:
<Source>
import java.util.Map;
import java.util.List;
import java.util.HashMap;
import java.util.ArrayList;
import sailpoint.tools.Util;
import connector.common.JsonUtil;
import sailpoint.object.Application;
import sailpoint.object.ProvisioningPlan.AccountRequest;
import sailpoint.object.ProvisioningPlan.AttributeRequest;
List entitlements = new ArrayList();
// Looping through the account requests and attribute requests to get the entitlement values
for(AccountRequests accountRequest : provisioningPlan.getAccountRequests()) // Get the Account Requests
{
for(AttributeRequest attributeRequest : accountRequest.getAttributeRequests()) // Get the Attribute Requests
{
entitlements = attributeRequest.get("entitlements"); // This is going to be an list of entitlements the user requests for *NB this attribute name is based on what you defined in the schema
}
}
if(entitlements.isNotNullOrEmpty())
{
for(String entitlement: entitlements) // looping through the entitlments to make the api calls
{
restClient.executeGet("iam/users/{{entitlement}}/members", requestEndPoint.getHeader(), requestEndPoint.getAllowedStatus()); // This will loop through all the entitlements and make the API calls
}
}
return requestEndPoint;
</Source>
What this is going to do is Only execute the API calls for the request, it is not going to handle the responses for the requests ( If the API responses are needed we would need to make adjustments ).
We have a GET single-user request to kind of trick IIQ to execute the operation anyway. Then you can just return the response from the single-user request and the access request should complete.
Thanks for the code. I am facing issues with getAllowedStatus() method when executing the end point. I am not able to find much information on this method as well. Would need your assistance on this.
What you have configured for the PUT request is correct.
What I would suggest you take a look at would be to ensure in the operation you have set the rule on has the response code set:
Next, if that still presents the same error of 400. I suggest you add all the values in that request you attempted to make in Postman and see if the error happens there.
We would need to ensure the body and URL are in the correct format.
Hi @dylanfoggan, let me share my current configurations. As I am still getting error 400. I tried to hard code all the values using the UI and managed to make the call successfully. Need your help to advise am I missing anything else?
FYI, I am using the before operation rule to dynamically get the access token as well.
<Source>import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import sailpoint.tools.Util;
import sailpoint.object.Application;
import sailpoint.object.ProvisioningPlan.AccountRequest;
import sailpoint.object.ProvisioningPlan.AttributeRequest;
import connector.common.JsonUtil;
import sailpoint.connector.webservices.EndPoint;
String tokenUrl = "tokenUrl";
String clientId = "clientId";
String clientSecret = "clientSecret";
String scope = "scope";
String namespace = "namespace";
HttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(tokenUrl);
List params = new ArrayList();
params.add(new BasicNameValuePair("grant_type", "client_credentials"));
params.add(new BasicNameValuePair("client_id", clientId));
params.add(new BasicNameValuePair("client_secret", clientSecret));
params.add(new BasicNameValuePair("scope", scope));
httpPost.setEntity(new UrlEncodedFormEntity(params));
HttpResponse response = httpClient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
String responseBody = EntityUtils.toString(response.getEntity());
JSONObject jsonResponse = new JSONObject(responseBody);
String accessToken = jsonResponse.getString("access_token");
String finalAccessToken = "";
System.out.println("Access Token: " + accessToken);
log.error("Access Token: " + accessToken);
finalAccessToken = "Bearer " + accessToken;
requestEndPoint.addHeader("x-api-key", "x-api-key");
requestEndPoint.addHeader("Authorization", finalAccessToken );
requestEndPoint.addHeader("Accept", "application/json");
requestEndPoint.addHeader("Content-Type", "application/json");
// Start of getting context url
List entitlements = new ArrayList();
String finalURL = "";
// Looping through the account requests and attribute requests to get the entitlement values
for(AccountRequest accountRequest : provisioningPlan.getAccountRequests()) // Get the Account Requests
{
for(AttributeRequest attributeRequest : accountRequest.getAttributeRequests()) // Get the Attribute Requests
{
String attrName = attributeRequest.getName();
if(attrName.equals("groupName")){
entitlements.add(attributeRequest.getValue());
}
}
}
if(!(entitlements.isEmpty()))
{
for(String entitlement: entitlements) // looping through the entitlments to make the api calls
{
finalURL = requestEndPoint.getFullUrl() + entitlement + "/members";
try {
restClient.executePut(finalURL, requestEndPoint.getBody(), requestEndPoint.getHeader(), requestEndPoint.getResponseCode());
} catch (Exception e) {
log.error("Error executing PUT request: " + e.getMessage(), e);
}
}
}
} else {
System.out.println("Error: " + statusCode + " - " + response.getStatusLine().getReasonPhrase());
}
</Source>
I recommend you avoid trying to authenticate before a single operation unless the API being required to execute is outside the scope. Instead, you should attempt to create the Custom Authentication operation and return the access_token from there so that it is saved to the connector.
I don’t see anything evident that is incorrect in how you set the API execution line but can you perhaps try logging all the values you are trying to set and confirm to me that they all look correct and are expected as is? ( Please log it where the red line is in the image ). It would look like this:
Hi @dylanfoggan thank you for the advice on the custom authentication. That part works. However, I am still getting error 400. Let me share the latest code I have put in place.
Hi @dylanfoggan I am getting a different error now with the new code.The application script threw an exception: sailpoint.connector.ConnectorException: 401 but the 4 lines of logging looks the same.
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import sailpoint.tools.Util;
import sailpoint.object.Application;
import sailpoint.object.ProvisioningPlan.AccountRequest;
import sailpoint.object.ProvisioningPlan.AttributeRequest;
import connector.common.JsonUtil;
import connector.common.Util;
import sailpoint.connector.webservices.EndPoint;
public String httpRequest(String requestUrl)
{
return application.getAttributeValue("genericWebServiceBaseUrl") + requestUrl;
}
// Start of getting context url
List entitlements = new ArrayList();
String finalUrl = "";
String contextUrl = "";
// Looping through the account requests and attribute requests to get the entitlement values
for(AccountRequest accountRequest : provisioningPlan.getAccountRequests()) // Get the Account Requests
{
//emailList.add(accountRequest.getNativeIdentity());
for(AttributeRequest attributeRequest : accountRequest.getAttributeRequests()) // Get the Attribute Requests
{
String attrName = attributeRequest.getName();
if(attrName.equals("groupName")){
entitlements.add(attributeRequest.getValue());
}
}
}
if(!(entitlements.isEmpty()))
{
for(String entitlement: entitlements) // looping through the entitlments to make the api calls
{
log.error("Final URL: " + httpRequest("/iam/namespace/group/" + entitlement + "/members"));
log.error("Body: " + requestEndPoint.getBody());
log.error("Header: " + requestEndPoint.getHeader());
log.error("Reponse Code: " + requestEndPoint.getResponseCode());
String response = restClient.executePut(httpRequest("/iam/namespace/group/" + entitlement + "/members"),requestEndPoint.getBody(),requestEndPoint.getHeader(),requestEndPoint.getResponseCode());
log.error("Response: " + response);
}
}
return requestEndPoint;
Hi @dylanfoggan sorry I realised the configuration is wrong as it does not call the executeput each time so it will only run for the last entitlement selected. I need your help again. So Sorry.