Restricting user to request only one entitlement from Manage Access

Our requirement is to restrict users from selecting more than one entitlement for a specific application during the access request process. We have explored the following approaches:

Method 1: Workflow Validation

  • Implemented a validation step in the LCM Provisioning workflow. Attached NW LCM Provisioning_message.xml for reference
  • Added custom logic to validate the entitlement count and exit the workflow with a custom validation message.
  • Issue: The custom validation message is displayed correctly for a single user, but when multiple users are selected, the message is not user-friendly .

Method 2: Advanced Policy

  • Implemented an Advanced Policy to validate the entitlement selection.
  • Issue: The policy is not being triggered during the access request process.
  • Could you please confirm whether an Advanced Policy can be used as a preventive during request submission?

Method 3: Entitlement SoD Policy

  • Created an out-of-the-box Entitlement SoD Policy.
  • The policy blocks the request as expected; however, when multiple users are selected, the violation message is not user-friendly and displays differently from the single-user scenario.

Could you please advise on the recommended approach for enforcing this requirement and providing a consistent validation message to users?

Hello Sruthi. Yes, an Advanced Policy can act as a preventive control at request submission, as long as LCM is licensed and the LCM Provisioning workflow is set to check policies. It is also a cleaner fit than Entitlement SoD for “only one entitlement from this application.” I would use a rule that evaluates the projected identity and returns a violation when the entitlement count for that application goes above one. A working example is here: Advanced Policy Violation - Restrict to request only 1 Entitlement.

On Method 2 not triggering, make sure the policy is Active and that policy checking is enabled in the LCM Provisioning workflow. Check policyScheme and policiesToCheck: policyScheme must not disable policy checking, and if policiesToCheck contains specific policies, your Advanced Policy must be included. If policiesToCheck is empty, all active policies are evaluated. These are also the first things suggested to check in this related thread.

For enforcement strength, if this needs to be a hard block, use Fail Workflow. Present Failures to Requester still allows the request to be submitted with the violation (SailPoint docs).

If you need to stop the second entitlement selection before Submit and show one clean message in Manage Access, a plugin/UI validation would be the better option, with the Advanced Policy kept as the enforcement backstop.

Advance policy will be working it seems it is not returning policy and returning rule, sample working code
import java.util.ArrayList;
import java.util.List;

import sailpoint.api.IdentityService;
import sailpoint.api.SailPointContext;
import sailpoint.object.Application;
import sailpoint.object.Identity;
import sailpoint.object.Link;
import sailpoint.object.Policy;
import sailpoint.object.PolicyViolation;
import sailpoint.tools.GeneralException;
import sailpoint.tools.Util;
System.out.println(“Inside Policy violadtion Rule”);
List profiles = new ArrayList();
PolicyViolation polVil =null;
Object obj =null;
String appName =“”;
try {
if(identity !=null && Util.isNotNullOrEmpty(appName)) {
Application app = context.getObjectByName(Application.class, appName);
if(app !=null) {
IdentityService is = new IdentityService(context);
List links = is.getLinks(identity, app);
if(links !=null && !Util.isEmpty(links)) {
Link link = links.get(0);
if(link !=null) {
obj = link.getAttribute(“PROFIL”);
if(obj !=null) {
if(obj instanceof List) {
profiles.addAll((List)obj);
}else if(obj instanceof String) {
profiles.add(obj);
}
}
}
}
}
}
// if(!Util.isEmpty(profiles) && profiles.size() >6) {
polVil = new PolicyViolation();
polVil.setActive(true);
polVil.setIdentity(identity);
polVil.setPolicy(policy);
polVil.setDescription(“More than 6 ACF2 PROFIL not allowed. Please remove the PROFIL and submit request Again”);
polVil.setStatus(PolicyViolation.Status.Open);
polVil.setConstraint(constraint);

// }

}catch(GeneralException e) {
e.getMessage();
}
System.out.println(“out Inside Policy violadtion Rule”+polVil);
return polVil;

Hi Harish/Kumar,

Thank you for the quick response.

I verified that policy checking is enabled in the LCM Provisioning workflow.
policyScheme = interactive / fail
policiesToCheck = Entitlement SOD, Single Entitlement Restriction

I tested with both interactive and fail, but the policy is still not being triggered.
To validate the setup, I simplified the Advanced Policy rule to always return a PolicyViolation:

PolicyViolation policyViolation = new PolicyViolation();
policyViolation.setActive(true);
policyViolation.setIdentity(identity);
policyViolation.setPolicy(policy);
policyViolation.setStatus(PolicyViolation.Status.Open);
return policyViolation;

Could you kindly advise how the UI customization approach can be implemented, or if there is a better place to add this validation?
Any guidance would be greatly appreciated.

Thank you!

Please ensure the workflow/Policy is not using cached Policy rule. Just try to open the updated rule from Policy UI and save and give it a try.

Hi @sruthiPatlolla I think whatever above approach community members suggest should work. One option you can leverage is creating a custom quick link where you can do whatever validation is needed in workflow forms.

Thanks,

PVR.

Hello Sruthi. Before moving to UI customization, I would revisit the Advanced Policy rule. Returning a violation unconditionally is not a reliable test here. IIQ can evaluate the rule against both the current identity and the projected post-request identity, and if both states violate, there may be no new violation to surface at request time. That behavior was reported and resolved by comparing the existing and expected states (related thread).

I would add logging at the start of the rule and log the entitlements from the identity passed into it. Then compare that projected identity against the persisted identity from context, and return a violation only when the request pushes the entitlement count for that application above one. There is a working example for this exact requirement here.

If you specifically need to block the second selection before Submit and show one popup in Manage Access, a plugin/UI validation would still be a good option. A similar requirement and plugin approach are discussed here.

Hi,
kindly set this allowRequestsWithViolations varibale ‘false’ in LCM Provisiong workflow,
and also set policyscheme interactive.

Hi Harish and Pavan,

Thank you for your inputs. The Advanced Policy is now triggering.

Sorry, I have two more questions that I’m stuck on:

  1. Even though I’m setting a custom message in the PolicyViolation object and have also updated the message in the GenericConstraint, the UI is still displaying only “Policy Violations will occur” along with the policy name. It is not showing my custom violation message. Do you have any suggestions on how to display the custom violation message to the requester?

  2. When I remove one of the entitlements and resubmit, the same review page loads again instead of submitting the request. It looks like the violation object is still being created. Is there something I might be missing in the policy logic or workflow configuration?

Could you please share any suggestions or guidance?

Thank you!

Hello Sruthi. On the message, the “Policy Violations will occur” banner is the standard warning. It is not replaced by the custom violation description. The requester can click the violation to view its available details (docs). You can set the violation description with setDescription(...) and associate the constraint with setConstraint(constraint), as shown in this Advanced Policy example.

For the repeated review page, the rule should return null once the projected state no longer violates the policy. I would log the projected entitlement count after the user removes one entitlement and confirm the rule is no longer creating a PolicyViolation.

Also, avoid calling remove() or removeAll() directly on collections returned from the projected identity, since that can modify the future identity object used by later evaluations. Copy the values into a separate list first and compare there (reference).

Thank you. I was able to resolve it.