Greetings, everyone.
We have an issue regarding re-assignment of roles in IIQ v8.3.
We have a Role SOD that gets triggered when two conflicting roles are found in an identity cube, or when a requester requests a role that would result in a violation.
When an identity is found to be violating of the SOD policy, the manager gets a Policy Violation work item to complete, in which they can allow or revoke access.
When the manager revokes one of the roles from the identity, it is removed normally from the identity cube and the corresponding entitlements from the native application. However, when an Identity Refresh task runs after the revocation, the role and the corresponding entitlements get re-assigned to the identity on both IIQ and the native application.
If the role removal is done by normal LCM means (i.e., Manage User Access quicklink), the role gets removed normally and it doesn’t get re-assigned when an Identity Refresh takes place.
We tried adding assignment attribute and set it to true to each AttributeRequest in all the Provisioning Plans by making use of the Before Provisioning rule, didn’t work.
We tried removing Sticky Entitlements, attributeAssignments, and IdentityEntitlements using the following Task and Rule before and after the revocation, but still didn’t work.
Task:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE TaskDefinition PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<TaskDefinition name="Task-StickyEntRemoval-NEW" resultAction="Rename"
subType="task_item_type_generic" type="Generic">
<Attributes>
<Map>
<entry key="TaskDefinition.runLengthAverage"/>
<entry key="TaskDefinition.runLengthTotal"/>
<entry key="TaskDefinition.runs"/>
<entry key="TaskSchedule.host"/>
<entry key="deleteStickEnt" value="false"/>
<!-- NEW -->
<entry key="deleteIdentityEntitlements" value="false"/>
<entry key="ruleName" value="Rule-StickyEntRemoval-NEW"/>
<entry key="taskCompletionEmailNotify" value="Disabled"/>
<entry key="taskCompletionEmailRecipients"/>
<entry key="taskCompletionEmailTemplate"/>
</Map>
</Attributes>
<Description>
Removes Sticky Entitlements, AttributeAssignments, and IdentityEntitlements.
</Description>
<Owner>
<Reference class="sailpoint.object.Identity" name="spadmin"/>
</Owner>
<Parent>
<Reference class="sailpoint.object.TaskDefinition" name="Run Rule"/>
</Parent>
<Signature>
<Inputs>
<Argument name="deleteStickEnt" type="boolean">
<Prompt>Delete the Sticky Entitlements</Prompt>
</Argument>
<!-- NEW -->
<Argument name="deleteIdentityEntitlements" type="boolean">
<Prompt>Delete IdentityEntitlements</Prompt>
</Argument>
<Argument name="identityValue" type="string">
<Prompt>Identity Distinguished Name</Prompt>
</Argument>
</Inputs>
<Returns>
<Argument name="attrAssignRemovalCount" type="string"/>
<Argument name="attrAssignIdList" type="string"/>
<Argument name="idenEntRemovalCount" type="string"/>
<!-- NEW -->
<Argument name="identityEntRemovalCount" type="int"/>
<Argument name="totalIE" type="int"/>
<Argument name="idenEntList" type="string"/>
</Returns>
</Signature>
</TaskDefinition>
Rule:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule language="beanshell" name="Rule-StickyEntRemoval-NEW">
<Source><![CDATA[
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import sailpoint.api.ObjectUtil;
import sailpoint.api.Terminator;
import sailpoint.object.AttributeAssignment;
import sailpoint.object.Filter;
import sailpoint.object.Identity;
import sailpoint.object.IdentityEntitlement;
import sailpoint.object.QueryOptions;
import sailpoint.object.TaskResult;
import sailpoint.tools.GeneralException;
import sailpoint.tools.Message;
import sailpoint.tools.Util;
if (void == taskResult || taskResult == null || config == null) {
return;
}
int attrAssignRemovalCount = 0;
int idenEntRemovalCount = 0;
int totalIE = 0;
/* NEW */
int identityEntRemovalCount = 0;
boolean isDelete = Util.otob(config.get("deleteStickEnt"));
/* NEW */
boolean deleteIdentityEntitlements =
Util.otob(config.get("deleteIdentityEntitlements"));
String identityValue = Util.otoa(config.get("identityValue"));
List attrAssignIdList = new ArrayList();
List idenEntList = new ArrayList();
Iterator itr = null;
Iterator aaIt = null;
String RESULT = "Success";
/* ================= EXISTING METHODS (UNCHANGED) ================= */
private void getAttrAssignmentsId(IdentityEntitlement idenEnt)
throws GeneralException {
Identity id = null;
try {
id = idenEnt.getIdentity();
if (id != null) {
List attributeAssignmentList = id.getAttributeAssignments();
if (!Util.isEmpty(attributeAssignmentList)) {
aaIt = attributeAssignmentList.iterator();
while (aaIt.hasNext()) {
AttributeAssignment attAssign = (AttributeAssignment) aaIt.next();
if (Util.isNotNullOrEmpty(idenEnt.getAssignmentId())
&& Util.isNotNullOrEmpty(attAssign.getAssignmentId())
&& attAssign.getAssignmentId()
.equalsIgnoreCase(idenEnt.getAssignmentId())) {
attrAssignIdList.add(id.getName());
}
}
}
}
} finally {
if (aaIt != null) Util.flushIterator(aaIt);
}
}
private void removeAttrAssignment(IdentityEntitlement idenEnt)
throws GeneralException {
Identity id = null;
try {
id = ObjectUtil.lockIfNecessary(context,
idenEnt.getIdentity().getName());
if (id != null) {
List attributeAssignmentList = id.getAttributeAssignments();
if (!Util.isEmpty(attributeAssignmentList)) {
aaIt = attributeAssignmentList.iterator();
while (aaIt.hasNext()) {
AttributeAssignment attAssign = (AttributeAssignment) aaIt.next();
if (Util.isNotNullOrEmpty(idenEnt.getAssignmentId())
&& Util.isNotNullOrEmpty(attAssign.getAssignmentId())
&& attAssign.getAssignmentId()
.equalsIgnoreCase(idenEnt.getAssignmentId())) {
aaIt.remove();
attrAssignIdList.add(id.getName());
attrAssignRemovalCount++;
}
}
context.saveObject(id);
}
}
} finally {
if (id != null) ObjectUtil.unlockIdentity(context, id);
if (aaIt != null) Util.flushIterator(aaIt);
}
}
/* ================= RULE EXECUTION ================= */
try {
QueryOptions qo = new QueryOptions();
qo.setCloneResults(true);
qo.setDistinct(true);
if (Util.isNotNullOrEmpty(identityValue)) {
qo.addFilter(Filter.ignoreCase(
Filter.eq("nativeIdentity", identityValue)));
}
qo.addFilter(Filter.ignoreCase(
Filter.eq("aggregationState", "disconnected")));
itr = context.search(IdentityEntitlement.class, qo);
while (itr.hasNext()) {
IdentityEntitlement idenEnt = (IdentityEntitlement) itr.next();
if (isDelete || deleteIdentityEntitlements) {
context.startTransaction();
/* EXISTING */
removeAttrAssignment(idenEnt);
/* NEW – delete IdentityEntitlement */
if (deleteIdentityEntitlements) {
new Terminator(context).deleteObject(idenEnt);
identityEntRemovalCount++;
}
/* EXISTING */
if (isDelete) {
idenEntRemovalCount++;
}
context.commitTransaction();
} else {
getAttrAssignmentsId(idenEnt);
}
if (totalIE < 100) {
idenEntList.add(" [ " + idenEnt.getValue() + " ] ");
}
totalIE++;
}
if (!isDelete && !deleteIdentityEntitlements) {
taskResult.addMessage(
new Message(Message.Type.Info,
"No records were deleted", null));
}
} catch (GeneralException e) {
context.rollbackTransaction();
taskResult.addMessage(
new Message(Message.Type.Error, e.getMessage(), null));
RESULT = "Fail";
} finally {
if (itr != null) Util.flushIterator(itr);
context.decache();
}
/* ================= TASK RESULT ================= */
taskResult.put("attrAssignRemovalCount", attrAssignRemovalCount);
taskResult.put("attrAssignIdList", attrAssignIdList);
taskResult.put("idenEntRemovalCount", idenEntRemovalCount);
/* NEW */
taskResult.put("identityEntRemovalCount", identityEntRemovalCount);
taskResult.put("totalIE", totalIE);
taskResult.put("idenEntList", idenEntList);
return RESULT;
]]></Source>
</Rule>
Note: This also happens in IIQ v8.5, and the same approach did NOT work as well.
Thank you in advance.