I have a certification definition that I would like to use to create a certification from a workflow within a rule. I don’t see any documentation on this. I tried to create a certification and a certification schedule, but I’m missing something. I also tried to use a certification event, which works, but adds all certifications to the same certification group, which we do not want. Any suggestions would be appreciated.
The CertificationDefinition (i.e. Certification Template)
A CertificationGroup (an entity used to group certifications generated by a given CertificationDefinition)
A Certification (individual access review containing what is to be reviewed and an assigned owner to complete it)
All 3 are required. Usually IIQ handles creating the CertificationGroup whenever a Certification is launched based on a CertificationDefinition.
If you’d like to share more details around your specific use-case, perhaps there are other options to accomplish your desired goal. I do know Certification IdentityTriggers all generally end up in a single CertificationGroup that encompasses all events triggered, and many customers don’t like that behavior, so perhaps making a “monthly” version where the CertificationGroup changes on a monthly basis could be a reasonable compromise.
In my code, I created a Certification and set the type, name and owner. I then pulled the CertificationDefinition out of the context, created a CertificationGroup and set the type, definition, owner and name. I saved the group to the context and added it to the Certification that I created. I created CertificationEntity objects and CertificationItems, which maybe I didn’t have to do. Then I created a CertificationSchedule and set run now to true.
After doing all this, the only thing that shows up in the certification is the CertificationGroup, the owner, and the CertificationEntity with the CertificationItems that I created. All the configuration that I would expect to come from the CertificationDefinition is missing. I assume I am not kicking off the certification correctly or I’m missing some step in the process.
So if you’re creating the Certification object itself (which sounds like what you’re doing), the CertificationDefinition part isn’t particularly relevant (it is still necessary as the Certification has a reference to the CertificationDefinition that supposedly created it). But once the Certification object is created, all the elements of the Certification are held inside that object (like owner, reminders, phase change rules, etc.), and the CertificationDefinition is no longer used.
If using a Certification Event really isn’t a viable option, the way to go would be to have a template CertificationDefinition, make a clone of that Definition and update any scope/target/naming/etc needed in your rule, and leverage the CertificationSchedule object to schedule the certification. Once the schedule launch activates, IIQ should handle generating the Certification from the template used in the CertificationSchedule.
Hi Brian,
I’m trying to add a CertificaitonEntityCustomization rule to add permitted roles as separate certification items, but when I add them to the entity, I get a java.util.ConcurrentModificationException error. Any idea how I might go about adding a new object to the entity?
So the ConcurrentModificationException is occurring because you’re trying to modify a collection that you’re in the process of iterating (attempting to add a new CertificationItem to a CertificationEntity whose item’s are being iterated).
// Note the 'entity.getItems()' here...
List items = entity.getItems();
logger.debug("Permitted Roles: The number of items is " + items.size());
// Loop through items
Iterator itemIt = items.iterator();
while (itemIt.hasNext()) {
// Much looking up and other stuff omitted
//
// Picking up with where the CME is occurring...
while (permittedRoleAssignIt.hasNext()) {
RoleAssignment permittedRoleAssign = permittedRoleAssignIt.next();
logger.debug("Permitted Roles: Certification Item being added for " + permittedRoleAssign.toXml());
CertificationItem certItem = new CertificationItem();
certItem.setType(CertificationItem.Type.Bundle);
certItem.setRoleAssignment(permittedRoleAssign);
// Here is where the CME actually occurs...
// You're adding a new item to the same entity whose items List you're still iterating
entity.add(certItem);
}
}
To resolve this, you could try using a ListIterator, which should support concurrent modification of the list (https://docs.oracle.com/javase/10/docs/api/java/util/List.html#listIterator()), or take a different approach by creating a new List object to store CertificationItems that you want to add to the Entity while iterating, then iterating that List and add them to the entity when you’re done iterating the entity itself (similar to this…)
List items = entity.getItems();
logger.debug("Permitted Roles: The number of items is " + items.size());
List itemsToAdd = new ArrayList();
// Loop through items
Iterator itemIt = items.iterator();
while (itemIt.hasNext()) {
// Much looking up and other stuff omitted
//
// Picking up with where the CME is occurring...
while (permittedRoleAssignIt.hasNext()) {
RoleAssignment permittedRoleAssign = permittedRoleAssignIt.next();
logger.debug("Permitted Roles: Certification Item being added for " + permittedRoleAssign.toXml());
CertificationItem certItem = new CertificationItem();
certItem.setType(CertificationItem.Type.Bundle);
certItem.setRoleAssignment(permittedRoleAssign);
// Instead of adding the new CertificationItem to the entity, add it to a separate List that is acting as a queue
itemsToAdd.add(certItem);
}
}
// Check for items to add to the entity, and add them if any are found...
if (itemsToAdd instanceof java.util.List && !itemsToAdd.isEmpty()) {
logger.debug("Have queued CertificationItems to add");
// Iterate our itemsToAdd list and add them to the Entity outside the Entity iteration
// This avoids concurrent modification
for (CertificationItem ci : itemsToAdd) {
entity.add(ci);
}
}
Thanks for explaining. I should have realized that is what was going on.
Do you see any reason adding this item would cause a problem. Or is there other information I would need to add? I’m getting a null pointer after returning from this rule. I tried adding the new CertificationItems to the context, but that didn’t make a difference.
After adding the items to the entity, you probably need to do a context.saveObject(entity); call to update the persisted entity with your changes.
As to the NPE, turning trace logging on for your rule should help narrow down where in the rule that might be occurring (and whether or not the NPE is from your rule or some other process). Sharing the NPE stacktrace should help as well.
I added the save on the entity as well and that didn’t make a difference. I have a print statement as the last line in my rule and that is getting logged. The exception is in creating the certification.
2023-05-11T17:14:33,563 ERROR QuartzScheduler_Worker-4 sailpoint.task.IdentityRefreshExecutor:1441 - RefreshWorker 1 exception: NullPointerException sailpoint.tools.GeneralException: NullPointerException at sailpoint.api.CertificationTriggerHandler.createCertification(CertificationTriggerHandler.java:192) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.CertificationTriggerHandler.handleEventInternal(CertificationTriggerHandler.java:74) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.AbstractIdentityTriggerHandler.handleEvent(AbstractIdentityTriggerHandler.java:89) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.Identitizer.processEvents(Identitizer.java:3209) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.Identitizer.processTriggers(Identitizer.java:3172) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.Identitizer.processTriggers(Identitizer.java:3158) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.Identitizer.finishRefresh(Identitizer.java:3005) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.Identitizer.refresh(Identitizer.java:2425) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.task.IdentityRefreshExecutor$RefreshWorker.process(IdentityRefreshExecutor.java:1382) [identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.task.IdentityRefreshExecutor$RefreshWorkerPool.queue(IdentityRefreshExecutor.java:1713) [identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.task.IdentityRefreshExecutor.refresh(IdentityRefreshExecutor.java:1040) [identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.task.IdentityRefreshExecutor.execute(IdentityRefreshExecutor.java:754) [identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.TaskManager.runSync(TaskManager.java:903) [identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.TaskManager.runSync(TaskManager.java:723) [identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.scheduler.JobAdapter.execute(JobAdapter.java:128) [identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [quartz-2.2.3.jar:?] at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [quartz-2.2.3.jar:?] Caused by: java.lang.NullPointerException at sailpoint.api.CertificationPhaser.getPhaseHandler(CertificationPhaser.java:804) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.CertificationPhaser.handleRollingPhaseTransitions(CertificationPhaser.java:666) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.Certificationer.refresh(Certificationer.java:1779) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.Certificationer.refresh(Certificationer.java:1719) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.Certificationer.start(Certificationer.java:1392) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.Certificationer.start(Certificationer.java:1349) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.Certificationer.start(Certificationer.java:1432) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.Certificationer.start(Certificationer.java:1309) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] at sailpoint.api.CertificationTriggerHandler.createCertification(CertificationTriggerHandler.java:171) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010] … 16 more
That’s going to be a fun one to troubleshoot… I’d start off reproducing the error after enabling trace logging for sailpoint.api.CertificationTriggerHandler and sailpoint.api.Certificationer to see if that helps shed some light.
After digging in the logs. It looks like the CertificationItems that I created don’t have entitlements and the others do. I’m assuming that is the issue and I just need to figure out how to get the EntitlementSnapshot to add.
Back on this topic, I now have the certification created, but when I try to make a decision on the review, I’m getting another NPE, which rolls up to a locking exception.
Uncaught JAX-RS exception.
sailpoint.tools.GeneralException: Exception while locking
at sailpoint.api.ObjectUtil.doWithCertLock(ObjectUtil.java:3479) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010]
at sailpoint.api.certification.CertificationDecisioner.decide(CertificationDecisioner.java:228) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010]
at sailpoint.rest.ui.certifications.CertificationResource.saveDecisions(CertificationResource.java:177) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010]
.
.
.
Caused by: java.lang.NullPointerException
at sailpoint.api.CertificationPhaser.getPhaseHandler(CertificationPhaser.java:804) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010]
at sailpoint.api.CertificationPhaser.handleRollingPhaseTransitions(CertificationPhaser.java:666) ~[identityiq.jar:8.1p5 Build cb180202551-20220607-090010]