Java class responsible for identityiq/ui/rest/requestAccess - Documentation?

Which IIQ version are you inquiring about?

Version 8.2

Share all details related to your problem, including any error messages you may have received.

I have a custom validation code for Access Request. The validation works fine, but there is a different behavior for single identity and multiple identities described below.

I can see the endpoint identityiq/ui/rest/requestAccess for single identity is returned with the data, for multiple identities this endpoint does not return anything.

Single identity → Access Request → Submit → Violated → my message (form) is displayed and can proceed with canceling the request. → End
Multiple identities → Access Request → Submit → Violated → End → Manual WorkItems → My forms waiting for me to cancel it.

Is there any documentation regarding the endpoint? Any other suggestions?

Hi @roku1

Could you provide more details about this issue or provide validation code?

From other side, can see class AccessItemResource, in the identityiq jar file, in package sailpoint.rest.ui.requestaccess.

In class you can see the following path definition

POST additionalQuestions
POST checkUniqueAssignment
GET roleDetails
GET population
GET managedAttributeDetails

  @POST
  @Path("additionalQuestions")
  public AdditionalQuestionsService.AdditionalQuestions getAdditionalQuestions(Map<String, Object> inputs) throws GeneralException {
    Map<String, Object> map = inputs;
    JoinPoint joinPoint = Factory.makeJP(ajc$tjp_1, this, this, map);
    try {
      AdditionalQuestionsService.AdditionalQuestions additionalQuestions1, additionalQuestions2;
      ajc$sailpoint_tools_TracingAspect$localAspectOf().traceMethodEntry(joinPoint);
      authorize(new Authorizer[] { (Authorizer)new LcmActionAuthorizer(QuickLink.LCM_ACTION_REQUEST_ACCESS) });
      AdditionalQuestionsRequest request = new AdditionalQuestionsRequest(inputs);
      AdditionalQuestionsService service = new AdditionalQuestionsService(this);
      AdditionalQuestionsService.AdditionalQuestions questions = service.getAdditionalQuestions(getAccessItem(), request);
      if (!Util.isEmpty(questions.getPermittedRoles()))
        questions.setPermittedRoles(convertRows(questions.getPermittedRoles(), "uiAccessItemsColumns")); 
      ajc$sailpoint_tools_TracingAspect$localAspectOf().traceMethodExit(joinPoint, additionalQuestions2);
      return additionalQuestions1;
    } catch (Throwable throwable) {
      ajc$sailpoint_tools_TracingAspect$localAspectOf().traceMethodThrow(joinPoint, throwable);
      throw throwable;
    } 
  }
  
  @POST
  @Path("checkUniqueAssignment")
  public Response checkUniqueAssignment(Map<String, Object> data) throws GeneralException {
    Map<String, Object> map = data;
    JoinPoint joinPoint = Factory.makeJP(ajc$tjp_2, this, this, map);
    try {
      Response response1, response2;
      ajc$sailpoint_tools_TracingAspect$localAspectOf().traceMethodEntry(joinPoint);
      Configuration configuration = getContext().getConfiguration();
      boolean isSelfServicePermittedEnabled = configuration.getAttributes().getBoolean("requestRolesPermittedSelfEnabled");
      boolean isSelfServiceAssignedEnabled = configuration.getAttributes().getBoolean("requestRolesAssignableSelfEnabled");
      authorize(new Authorizer[] { (Authorizer)new LcmActionAuthorizer(QuickLink.LCM_ACTION_REQUEST_ACCESS, (isSelfServiceAssignedEnabled || isSelfServicePermittedEnabled)) });
      Response.Status status = Response.Status.OK;
      UniqueAssignmentRequest uniqueAssignmentRequest = new UniqueAssignmentRequest(this.accessItemId, data);
      boolean isVerified = verifyAccountInfos(uniqueAssignmentRequest.getAccountSelections());
      if (isVerified) {
        boolean uniqueAssignment = (new RequestAccessService((UserContext)this)).isUniqueAssignment(uniqueAssignmentRequest);
        if (!uniqueAssignment)
          status = Response.Status.CONFLICT; 
      } else {
        status = Response.Status.INTERNAL_SERVER_ERROR;
      } 
      ajc$sailpoint_tools_TracingAspect$localAspectOf().traceMethodExit(joinPoint, response2);
      return response1;
    } catch (Throwable throwable) {
      ajc$sailpoint_tools_TracingAspect$localAspectOf().traceMethodThrow(joinPoint, throwable);
      throw throwable;
    } 
  }
  
  @Path("roleDetails")
  public RoleDetailResource getRoleDetails(@QueryParam("quickLink") String quickLink, @QueryParam("identityId") String identityId, @QueryParam("assignmentId") String assignmentId) throws GeneralException {
    String str1 = quickLink, str2 = identityId, str3 = assignmentId;
    Object[] arrayOfObject = new Object[3];
    arrayOfObject[0] = str1;
    arrayOfObject[1] = str2;
    arrayOfObject[2] = str3;
    JoinPoint joinPoint = Factory.makeJP(ajc$tjp_3, this, this, arrayOfObject);
    try {
      RoleDetailResource roleDetailResource1, roleDetailResource2;
      ajc$sailpoint_tools_TracingAspect$localAspectOf().traceMethodEntry(joinPoint);
      authorize(new Authorizer[] { (Authorizer)new LcmActionAuthorizer(QuickLink.LCM_ACTION_REQUEST_ACCESS), (Authorizer)new AccessItemAuthorizer(this.accessItemId, identityId, quickLink) });
      Bundle role = (Bundle)getContext().getObjectById(Bundle.class, this.accessItemId);
      if (role == null)
        throw new ObjectNotFoundException(); 
      Boolean classificationsEnabled = Boolean.valueOf(Configuration.getSystemConfig().getBoolean("displayClassificationsInAccessRequest"));
      ajc$sailpoint_tools_TracingAspect$localAspectOf().traceMethodExit(joinPoint, roleDetailResource2);
      return roleDetailResource1;
    } catch (Throwable throwable) {
      ajc$sailpoint_tools_TracingAspect$localAspectOf().traceMethodThrow(joinPoint, throwable);
      throw throwable;
    } 
  }
  
  @Path("population")
  public AccessItemPopulationResource getAccessItemPopulationResource() throws GeneralException {
    JoinPoint joinPoint = Factory.makeJP(ajc$tjp_4, this, this);
    try {
      AccessItemPopulationResource accessItemPopulationResource1, accessItemPopulationResource2;
      ajc$sailpoint_tools_TracingAspect$localAspectOf().traceMethodEntry(joinPoint);
      ajc$sailpoint_tools_TracingAspect$localAspectOf().traceMethodExit(joinPoint, accessItemPopulationResource2);
      return accessItemPopulationResource1;
    } catch (Throwable throwable) {
      ajc$sailpoint_tools_TracingAspect$localAspectOf().traceMethodThrow(joinPoint, throwable);
      throw throwable;
    } 
  }
  
  @Path("managedAttributeDetails")
  public ManagedAttributeDetailResource getManagedAttributeDetails(@QueryParam("quickLink") String quickLink, @QueryParam("identityId") String identityId) throws GeneralException {
    String str1 = quickLink, str2 = identityId;
    JoinPoint joinPoint = Factory.makeJP(ajc$tjp_5, this, this, str1, str2);
    try {
      ManagedAttributeDetailResource managedAttributeDetailResource1, managedAttributeDetailResource2;
      ajc$sailpoint_tools_TracingAspect$localAspectOf().traceMethodEntry(joinPoint);
      authorize(new Authorizer[] { (Authorizer)new LcmActionAuthorizer(QuickLink.LCM_ACTION_REQUEST_ACCESS), (Authorizer)new AccessItemAuthorizer(this.accessItemId, identityId, quickLink) });
      ManagedAttribute ma = (ManagedAttribute)getContext().getObjectById(ManagedAttribute.class, this.accessItemId);
      if (ma == null)
        throw new ObjectNotFoundException(); 
      Boolean classificationsEnabled = Boolean.valueOf(Configuration.getSystemConfig().getBoolean("displayClassificationsInAccessRequest"));
      ajc$sailpoint_tools_TracingAspect$localAspectOf().traceMethodExit(joinPoint, managedAttributeDetailResource2);
      return managedAttributeDetailResource1;
    } catch (Throwable throwable) {
      ajc$sailpoint_tools_TracingAspect$localAspectOf().traceMethodThrow(joinPoint, throwable);
      throw throwable;
    } 
  }

HI @ismaelmoreno1
thank you for taking time to check. When i check the the payload and POST, it does not seem correlate with the methods you shared. I am sharing the screenshots and information below. I will also share the Violation form that I have.

Is it also possible to provide/check the javascript? It seems using the same functions, but for multiple items it is skipped. List listOfViolations is a name, it is custom validation.

I can see from the network logs that it choose different path and the difference is response. Single item vs multiple items.

Multiple identities:
requestAccess POST
accessRequest.jsf - message
home - dashboard

Multiple identities workitems:
cannot add image

Payload:

{identities: ["0a017c5a8bf71d1e818bf7c0587e065f", "0a017c5a8bf71d1e818bf7c0372b0417"], addedRoles: [],…}
addedEntitlements
: 
[{id: "0a017c5a8b8b1c33818b94e882fc1cc3", comment: null, sunrise: null, sunset: null,…}]
addedRoles
: 
[]
identities
: 
["0a017c5a8bf71d1e818bf7c0587e065f", "0a017c5a8bf71d1e818bf7c0372b0417"]
priority
: 
null
quickLink
: 
"Request Access"
removedEntitlements
: 
[]
removedRoles
: 
[]

Response:

[
    {
        "identityRequestId": "",
        "workflowStatus": "approving",
        "workflowWorkItemType": "Form",
        "workflowWorkItemId": "0a017c5a8c5d18ef818c5db3ec0e00a9",
        "messages": [],
        "approvalItems": [],
        "allowViolations": false,
        "requireViolationComment": false
    },
    {
        "identityRequestId": "",
        "workflowStatus": "approving",
        "workflowWorkItemType": "Form",
        "workflowWorkItemId": "0a017c5a8c5d18ef818c5db3ef4800ac",
        "messages": [],
        "approvalItems": [],
        "allowViolations": false,
        "requireViolationComment": false
    }
]

Single identity:
requestAccess POST
load FORM workitem details
load FORM workitem details
submit - cancel request
accessRequest.jsf - message
home - dashboard

Javascript same for both cases in requestAccess POST:
cannot add image

The form is displayed at the end of access request page.
cannot add image

Custom Validate Access Request – Subprocess
Before LCM Provisioning Initialize
Then Initialize is called and if listOfViolations exist and ends the workflowcase.

  <Step action="rule:Validate If AAD Account Exists Rule" icon="Default" name="Validate AAD Account Exists" posX="668" posY="208" resultVariable="listOfViolations">
    <Transition to="Build Violations Form" when="!sailpoint.tools.Util.isEmpty(listOfViolations);"/>
    <Transition to="end"/>
  </Step>
  <Step action="rule:Build Violations Form Rule" icon="Default" name="Build Violations Form" posX="730" posY="36" resultVariable="violationsForm">
    <Transition to="Show Violations Info"/>
  </Step>
  <Step icon="Default" name="Show Violations Info" posX="905" posY="35">
    <Approval name="Show Access Request Violations Form" owner="ref:launcher">
      <Arg name="workItemType" value="Form"/>
      <Arg name="workItemDescription" value="List of Violations"/>
      <Arg name="workItemForm" value="ref:violationsForm"/>
    </Approval>
    <Transition to="Add WF Message"/>
  </Step>

Form, go through list of policyViolations and add Fields:
When i was running debug. The list of listOfViolations is always 1, not 2 or 3 based on number of identities. Therefore the violation seems to be split to each workflowCase
cannot add image

public static Form runBuildFormRule(List<Map<Object,Object>> listOfViolations)
	{
		Form form = new Form();

	       form.put(Form.ATT_PAGE_TITLE, Utils.getLocalizedMessage("access_request_violation_pagetitle"));
	       form.put(Form.ATT_SUBTITLE, Utils.getLocalizedMessage("access_request_violation_subtitle"));

	       int counter = 1;
	       if (log.isDebugEnabled()) {
	    	   log.debug("number of violations: " + listOfViolations.size());
	    	   for (Map<Object,Object> violationMap : Util.safeIterable(listOfViolations)) {
	    		   for (Map.Entry<Object, Object> entry : violationMap.entrySet()) {
		    	        String key = (String) entry.getKey();
		    	        String value = (String) entry.getValue();
		    	        log.debug("key: " + key + " value: " + value);
		    	    }
		       }   
	       }
	       
	       // Build and populate the violation form
	       
	       
	       // Code Review: Replaced 'Map' with 'Map<Object,Object>' on following line:
	       for (Map<Object,Object> violationMap : Util.safeIterable(listOfViolations)) {
	           String violationApplicationName = (String) violationMap.get("applicationName");
	           String violationAttributeName = (String) violationMap.get("name");
	           String violationAttributeValue = (String) violationMap.get("value");
	           String violationMessage = (String) violationMap.get("violationMessage");
	           String violationDescription = (String) violationMap.get("violationDescription");
	           String violationIdentityName = (String) violationMap.get("identityName");
	           String violationNativeIdentity = (String) violationMap.get("nativeIdentity");

	           Form.Section violationsSection = new Form.Section();
	           violationsSection.setLabel("Error: " + violationMessage);
	           violationsSection.setType("datatable");
	           violationsSection.setName("Violation" + counter);
				
	           if (Util.isNotNullOrEmpty(violationIdentityName)) {
	               addFieldToViolationsSection(violationsSection, "identityName", "Identity", violationIdentityName);
	           }
	           if (Util.isNotNullOrEmpty(violationNativeIdentity)) {
	               addFieldToViolationsSection(violationsSection, "nativeIdentity", "Native Identity", violationNativeIdentity);
	           }
	           if (Util.isNotNullOrEmpty(violationApplicationName)) {
	               addFieldToViolationsSection(violationsSection, "applicationName", "Application", violationApplicationName);
	           }
	           if (Util.isNotNullOrEmpty(violationAttributeName)) {
	               addFieldToViolationsSection(violationsSection, "attributeName", "Attribute", violationAttributeName);
	           }
	           if (Util.isNotNullOrEmpty(violationAttributeValue)) {
	               addFieldToViolationsSection(violationsSection, "attributeValue", "Value", violationAttributeValue);
	           }
	           if (Util.isNotNullOrEmpty(violationMessage)) {
	               addFieldToViolationsSection(violationsSection, "violationMessage", "Message", violationMessage);
	           }
	           if (Util.isNotNullOrEmpty(violationDescription)) {
	               addFieldToViolationsSection(violationsSection, "violation", "Description", violationDescription);
	           }
	           form.add(violationsSection);
	           counter++;
	       }

	       Form.Button button = new Form.Button();
	       button.setAction("next");
	       button.setLabel("Cancel Request");
	       button.setValue("next");
	       form.add(button);

	       return form;
	}
	
	// private method to add a filed to the form 
	private static void addFieldToViolationsSection(Form.Section section, String name, String displayName, String value) {
        Field violationField = new Field();
        violationField.setDisplayName(displayName);
        violationField.setName(name);
        violationField.setValue(value);
        violationField.setType("string");
        violationField.setReadOnly(true);
        section.add(violationField);
    }

This is javascript usage:

This one is single identity requestAccess path

This is example for for single identity appearing at the end of the Access Request page - correct. For multiple identities, it is skipped and moved to manual workItems page (not part of the request and misleading)

After reading more about workflows, this seems like ootb setup above the workflow LCM Provisioning for multiple users.

Even though this is request violation section in the article, it applies same way to my request validation and separating the workflowcases/accessrequests for individual identities.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.