I have built out a rule that I have working with the rule development kit. I have tried to update the rule based on feed back I am getting from the rule validator. I am still really new to building out rules and I am not sure how I can get this to pass so that I can deploy this to sandbox. All suggestions are welcome.
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule name="DetermineUID IdentityAttribute Rule" type="IdentityAttribute">
<Description>Determine lifecycle state.</Description>
<Source><![CDATA[
import sailpoint.object.Identity;
import sailpoint.object.Link;
import java.util.List;
import java.util.Date;
import org.apache.commons.lang.StringUtils;
import sailpoint.object.Application;
import sailpoint.object.Field;
import sailpoint.server.IdnRuleUtil;
import sailpoint.tools.GeneralException;
public String calculateSecZettaUserId(String seczettaFirstname, String seczettaLastname) throws GeneralException {
log.debug("calculateSecZettaUserId:: Check if first name is null");
if(seczettaFirstname != null) {
log.debug("calculateSecZettaUserId:: FristName is not null");
log.debug("calculateSecZettaUserId::Remove and special char");
seczettaFirstname = seczettaFirstname.replaceAll("[^a-zA-Z0-9]", "");
}
log.debug("calculateSecZettaUserId:: Set empty var for lastFirst5");
String lastFirst5 = "";
log.debug("calculateSecZettaUserId:: Check is last name is null");
if(seczettaLastname != null) {
log.debug("calculateSecZettaUserId:: LastName is not null");
log.debug("calculateSecZettaUserId:: Remove and special char");
seczettaLastname = seczettaLastname.replaceAll("[^a-zA-Z0-9]", "");
}
log.debug("calculateSecZettaUserId:: Set firstIntial to substring of 1 for firstName");
String firstIntial = seczettaFirstname.substring(0, 1);
log.debug("calculateSecZettaUserId:: Set lastNameLength to number of char in lastname");
int lastNameLength = seczettaLastname.length();
log.debug("calculateSecZettaUserId:: seczettaLastname.length: " + lastNameLength);
log.debug("calculateSecZettaUserId:: Check if length is <= 5");
if(lastNameLength <= 5){
log.debug("calculateSecZettaUserId:: count is less then or equal to 5");
log.debug("calculateSecZettaUserId:: Set lastFrist5 to seczettaLastname");
lastFirst5 = seczettaLastname.substring(0);
}
else{
log.debug("calculateSecZettaUserId:: count is greater then 5");
log.debug("calculateSecZettaUserId:: Substring of 5 for seczettaLastname");
lastFirst5 = seczettaLastname.substring(0, 5);
}
log.debug("calculateSecZettaUserId:: LastFirst5: " + lastFirst5);
log.debug("calculateSecZettaUserId::Set userName");
String userName = firstIntial + lastFirst5;
log.debug("calculateSecZettaUserId:: userName: " + userName);
log.debug("calculateSecZettaUserId:: Set counter to 1");
int counter = 1;
String userNameFull;
log.debug("calculateSecZettaUserId:: Step into while loop");
while(!isUnique(userName.toLowerCase() + "9" + Integer.toString(counter)) && counter <= 99)
{
log.debug("calculateSecZettaUserId:: inside Value for Unique: " + isUnique(userName.toLowerCase() + "9" + Integer.toString(counter)));
log.debug("calculateSecZettaUserId::increase counter by 1");
counter++;
log.debug("calculateSecZettaUserId:: after counter is set to: " + counter);
}
log.debug("calculateSecZettaUserId:: Set userNameFull");
String userNameFull = userName.toLowerCase() + "9" + Integer.toString(counter);
log.debug("calculateSecZettaUserId:: full userName is set to: " + userName);
log.debug("calculateSecZettaUserId:: Return userNameFull");
return userNameFull;
}
public boolean isUnique(String username) throws GeneralException {
log.debug("isUnique:: Stepping into isUnique()");
log.debug("isUnique:: username is: " + username);
log.debug("isUnique:: getName is set to: " + application.getName());
return !idn.accountExistsByDisplayName(application.getName(), username);
}
public String calculateUid() throws GeneralException{
//Initialize
List<Link> linksList;
log.debug("calculateUid:: Set uid to null");
String uid = null;
log.debug("calculateUid:: Check if identity is not null");
if (identity != null) {
log.debug("calculateUid:: Get a list of links");
linksList = identity.getLinks();
log.debug("calculateUid:: List<Link> linksList: " + linksList);
if (linksList != null && !linksList.isEmpty()) {
log.debug("calculateUid:: Set isWorkdayAccountPresent to false");
boolean isWorkdayAccountPresent = false;
log.debug("calculateUid:: Set isSecZettaAccountPresent to false");
boolean isSecZettaAccountPresent = false;
log.debug("calculateUid:: Set workdayUserid to null");
String workdayUserid = null;
log.debug("calculateUid:: Set seczettaFirstname to null");
String seczettaFirstname = null;
log.debug("calculateUid:: Set seczettaLastname to null");
String seczettaLastname = null;
log.debug("calculateUid:: Set seczettaUserid to null");
String seczettaUserid = null;
log.debug("calculateUid:: Set workdayAccountCreatedDate to null");
Date workdayAccountCreatedDate = null;
log.debug("calculateUid:: Set seczettaAccountCreatedDate to null");
Date seczettaAccountCreatedDate = null;
log.debug("calculateUid:: Stepping into for loop");
for (Link link : linksList) {
log.debug("calculateUid:: Check if appliaction name is AD");
if ("Active Directory [source]".equals(link.getApplicationName())) {
log.debug("calculateUid:: Grabbing UID from AD");
uid = (String)link.getAttribute("sAMAccountName");
break;
}
else {
log.debug("calculateUid:: Check is application name is Workday");
if ("Workday Production Sandbox [source]".equals(link.getApplicationName()) || "Workday Production [source]".equals(link.getApplicationName())) {
log.debug("calculateUid:: Workday source account found");
isWorkdayAccountPresent = true;
log.debug("calculateUid:: Get USERID");
workdayUserid = (String)link.getAttribute("USERID");
log.debug("calculateUid:: Get Created Date");
workdayAccountCreatedDate = link.getCreated();
}
log.debug("calculateUid:: Check if application name is SecZetta");
if ("SecZetta [source]".equals(link.getApplicationName())) {
log.debug("calculateUid:: SecZetta source Account Found");
isSecZettaAccountPresent = true;
log.debug("calculateUid:: Get personal_first_name");
seczettaFirstname = (String)link.getAttribute("personal_first_name");
log.debug("calculateUid:: Get personal_last_name");
seczettaLastname = (String)link.getAttribute("personal_last_name");
log.debug("calculateUid:: SecZettaLastNAame: " + seczettaLastname);
if((String)link.getAttribute("sailpoint_username_ne_attribute") != null){
log.debug("calculateUid:: SailPoint UserName is not null");
seczettaUserid = (String)link.getAttribute("sailpoint_username_ne_attribute");
log.debug("calculateUid:: seczettaUserid was set to: " + seczettaUserid);
}
else{
log.debug("calculateUid:: Calcuatle the UID for seczetta user");
seczettaUserid = calculateSecZettaUserId(seczettaFirstname, seczettaLastname);
}
log.debug("calculateUid:: Get created date");
seczettaAccountCreatedDate = link.getCreated();
}
}
}
log.debug("calculateUid:: Check if uid is set to null");
if (uid == null) {
log.debug("calculateUid:: uid was set to null");
log.debug("calculateUid:: check if workday account and seczetta account is present");
if (isWorkdayAccountPresent && isSecZettaAccountPresent) {
log.debug("calculateUid:: Workday and SecZetta accounts were present");
if (workdayAccountCreatedDate.before(seczettaAccountCreatedDate) || workdayUserid == seczettaUserid) {
log.debug("calculateUid:: Workday was created before SecZetta account");
log.debug("calculateUid:: setting uid to " + workdayUserid);
uid = workdayUserid;
}
else {
log.debug("calculateUid:: SecZetta was created before Workday");
log.debug("calculateUid:: Putting identity into an Error state");
uid = null;
}
}
else {
log.debug("calculateUid:: UID was not null");
if (isWorkdayAccountPresent) {
log.debug("calculateUid:: Workday Account Present is true");
log.debug("calculateUid:: uid is set to " + workdayUserid);
uid = workdayUserid;
}
if (isSecZettaAccountPresent) {
log.debug("calculateUid:: SecZetta Account Present is true");
log.debug("calculateUid:: uid is set to " + seczettaUserid);
uid = seczettaUserid;
}
}
}
}
}
log.error("calculateUid:: DetermineUID IdentityAttribute Rule: " + uid);
log.debug("calculateUid:: Retruning the calcualted UID");
return uid;
}
return calculateUid();
]]></Source>
</Rule>
The application variable issue is due to the rule’s signature not having the application input variable defined or it not being a known input variable to this type of rule The rule examples on the developer site do not have the full signature stub for their example rules (and they really should). It explains the arguments in a table, but does not show how to define in the rule signature. Even examples on Compass are missing, but now the rule validator expects you to randomly know about these, which you won’t unless you came from IIQ development. When you define a rule in the IIQ UI, the signature map is automatically generated based off template objects. Here is an example rule from IIQ which has the full signature the validator is expecting to validate:
<Rule name="Example Application Identity Attribute Rule"
type="IdentityAttribute">
<Description>
Example rule that can be used to generate an identity attribute value.
This is an application specific attribute promotion rule and it will be
called for each link found on the application specified along with
this rule.
</Description>
<Signature returnType='String'>
<Inputs>
<Argument name='context'>
<Description>
A SailPointContext object used if its necessary
to query objects from the database.
</Description>
</Argument>
<Argument name='environment' type='Map'>
<Description>
Arguments passed to the aggregation or refresh task.
</Description>
</Argument>
<Argument name='identity'>
<Description>
The Identity of the object where the account is
assigned.
</Description>
</Argument>
<Argument name='link'>
<Description>
The Link object that represents the account that is
being processed.
</Description>
</Argument>
<Argument name='attributeDefininition'>
<Description>
The AttributeDefinition object for this attribute.
</Description>
</Argument>
<Argument name='attributeSource'>
<Description>
The AttributeSource object from the AttributeDefinition.
</Description>
</Argument>
</Inputs>
<Returns>
<Argument name='attributeValue'>
<Description>
The value of the attribute that should be populated. The rule
should return this value.
</Description>
</Argument>
</Returns>
</Signature>
<Source>
<![CDATA[
import sailpoint.object.Link;
import sailpoint.object.Attributes;
String isContractor = "false";
Attributes attrs = link.getAttributes();
if ( attrs != null ) {
int userCode = attrs.getInt("userCode");
if ( userCode == 6501 ) {
isContractor = "true";
}
}
return isContractor;
]]>
</Source>
</Rule>
As you can see, there is no application variable that gets passed into the rules runtime, so you would need to use link.getApplicationName() like you did in other cases.
The linksList thing seems bogus to me. I don’t know why it’s yelling about that honestly. @JackSparrow is right about removing the Generics.
Here is a version that validates fine:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule name="DetermineUID IdentityAttribute Rule" type="IdentityAttribute">
<Signature returnType='String'>
<Inputs>
<Argument name='context'>
<Description>
A SailPointContext object used if its necessary
to query objects from the database.
</Description>
</Argument>
<Argument name='environment' type='Map'>
<Description>
Arguments passed to the aggregation or refresh task.
</Description>
</Argument>
<Argument name='identity' type='Identity'>
<Description>
The Identity of the object where the account is
assigned.
</Description>
</Argument>
<Argument name='link' type='Link'>
<Description>
The Link object that represents the account that is
being processed.
</Description>
</Argument>
<Argument name='attributeDefininition'>
<Description>
The AttributeDefinition object for this attribute.
</Description>
</Argument>
<Argument name='attributeSource'>
<Description>
The AttributeSource object from the AttributeDefinition.
</Description>
</Argument>
</Inputs>
<Returns>
<Argument name='attributeValue'>
<Description>
The value of the attribute that should be populated. The rule
should return this value.
</Description>
</Argument>
</Returns>
</Signature>
<Description>Determine lifecycle state.</Description>
<Source><![CDATA[
import sailpoint.object.Identity;
import sailpoint.object.Link;
import java.util.List;
import java.util.Date;
import org.apache.commons.lang.StringUtils;
import sailpoint.object.Application;
import sailpoint.object.Field;
import sailpoint.server.IdnRuleUtil;
import sailpoint.tools.GeneralException;
public String calculateSecZettaUserId(String seczettaFirstname, String seczettaLastname) throws GeneralException {
log.debug("calculateSecZettaUserId:: Check if first name is null");
if(seczettaFirstname != null) {
log.debug("calculateSecZettaUserId:: FristName is not null");
log.debug("calculateSecZettaUserId::Remove and special char");
seczettaFirstname = seczettaFirstname.replaceAll("[^a-zA-Z0-9]", "");
}
log.debug("calculateSecZettaUserId:: Set empty var for lastFirst5");
String lastFirst5 = "";
log.debug("calculateSecZettaUserId:: Check is last name is null");
if(seczettaLastname != null) {
log.debug("calculateSecZettaUserId:: LastName is not null");
log.debug("calculateSecZettaUserId:: Remove and special char");
seczettaLastname = seczettaLastname.replaceAll("[^a-zA-Z0-9]", "");
}
log.debug("calculateSecZettaUserId:: Set firstIntial to substring of 1 for firstName");
String firstIntial = seczettaFirstname.substring(0, 1);
log.debug("calculateSecZettaUserId:: Set lastNameLength to number of char in lastname");
int lastNameLength = seczettaLastname.length();
log.debug("calculateSecZettaUserId:: seczettaLastname.length: " + lastNameLength);
log.debug("calculateSecZettaUserId:: Check if length is <= 5");
if(lastNameLength <= 5){
log.debug("calculateSecZettaUserId:: count is less then or equal to 5");
log.debug("calculateSecZettaUserId:: Set lastFrist5 to seczettaLastname");
lastFirst5 = seczettaLastname.substring(0);
}
else{
log.debug("calculateSecZettaUserId:: count is greater then 5");
log.debug("calculateSecZettaUserId:: Substring of 5 for seczettaLastname");
lastFirst5 = seczettaLastname.substring(0, 5);
}
log.debug("calculateSecZettaUserId:: LastFirst5: " + lastFirst5);
log.debug("calculateSecZettaUserId::Set userName");
String userName = firstIntial + lastFirst5;
log.debug("calculateSecZettaUserId:: userName: " + userName);
log.debug("calculateSecZettaUserId:: Set counter to 1");
int counter = 1;
String userNameFull;
log.debug("calculateSecZettaUserId:: Step into while loop");
while(!isUnique(userName.toLowerCase() + "9" + Integer.toString(counter)) && counter <= 99)
{
log.debug("calculateSecZettaUserId:: inside Value for Unique: " + isUnique(userName.toLowerCase() + "9" + Integer.toString(counter)));
log.debug("calculateSecZettaUserId::increase counter by 1");
counter++;
log.debug("calculateSecZettaUserId:: after counter is set to: " + counter);
}
log.debug("calculateSecZettaUserId:: Set userNameFull");
String userNameFull = userName.toLowerCase() + "9" + Integer.toString(counter);
log.debug("calculateSecZettaUserId:: full userName is set to: " + userName);
log.debug("calculateSecZettaUserId:: Return userNameFull");
return userNameFull;
}
public boolean isUnique(String username) throws GeneralException {
log.debug("isUnique:: Stepping into isUnique()");
log.debug("isUnique:: username is: " + username);
log.debug("isUnique:: getName is set to: " + link.getApplicationName());
return !idn.accountExistsByDisplayName(link.getApplicationName(), username);
}
public String calculateUid() throws GeneralException{
//Initialize
List linksList;
log.debug("calculateUid:: Set uid to null");
String uid = null;
log.debug("calculateUid:: Check if identity is not null");
if (identity != null) {
log.debug("calculateUid:: Get a list of links");
linksList = identity.getLinks();
log.debug("calculateUid:: List<Link> linksList: " + linksList);
if (linksList != null && !linksList.isEmpty()) {
log.debug("calculateUid:: Set isWorkdayAccountPresent to false");
boolean isWorkdayAccountPresent = false;
log.debug("calculateUid:: Set isSecZettaAccountPresent to false");
boolean isSecZettaAccountPresent = false;
log.debug("calculateUid:: Set workdayUserid to null");
String workdayUserid = null;
log.debug("calculateUid:: Set seczettaFirstname to null");
String seczettaFirstname = null;
log.debug("calculateUid:: Set seczettaLastname to null");
String seczettaLastname = null;
log.debug("calculateUid:: Set seczettaUserid to null");
String seczettaUserid = null;
log.debug("calculateUid:: Set workdayAccountCreatedDate to null");
Date workdayAccountCreatedDate = null;
log.debug("calculateUid:: Set seczettaAccountCreatedDate to null");
Date seczettaAccountCreatedDate = null;
log.debug("calculateUid:: Stepping into for loop");
for (Link link : linksList) {
log.debug("calculateUid:: Check if appliaction name is AD");
if ("Active Directory [source]".equals(link.getApplicationName())) {
log.debug("calculateUid:: Grabbing UID from AD");
uid = (String)link.getAttribute("sAMAccountName");
break;
}
else {
log.debug("calculateUid:: Check is application name is Workday");
if ("Workday Production Sandbox [source]".equals(link.getApplicationName()) || "Workday Production [source]".equals(link.getApplicationName())) {
log.debug("calculateUid:: Workday source account found");
isWorkdayAccountPresent = true;
log.debug("calculateUid:: Get USERID");
workdayUserid = (String)link.getAttribute("USERID");
log.debug("calculateUid:: Get Created Date");
workdayAccountCreatedDate = link.getCreated();
}
log.debug("calculateUid:: Check if application name is SecZetta");
if ("SecZetta [source]".equals(link.getApplicationName())) {
log.debug("calculateUid:: SecZetta source Account Found");
isSecZettaAccountPresent = true;
log.debug("calculateUid:: Get personal_first_name");
seczettaFirstname = (String)link.getAttribute("personal_first_name");
log.debug("calculateUid:: Get personal_last_name");
seczettaLastname = (String)link.getAttribute("personal_last_name");
log.debug("calculateUid:: SecZettaLastNAame: " + seczettaLastname);
if((String)link.getAttribute("sailpoint_username_ne_attribute") != null){
log.debug("calculateUid:: SailPoint UserName is not null");
seczettaUserid = (String)link.getAttribute("sailpoint_username_ne_attribute");
log.debug("calculateUid:: seczettaUserid was set to: " + seczettaUserid);
}
else{
log.debug("calculateUid:: Calcuatle the UID for seczetta user");
seczettaUserid = calculateSecZettaUserId(seczettaFirstname, seczettaLastname);
}
log.debug("calculateUid:: Get created date");
seczettaAccountCreatedDate = link.getCreated();
}
}
}
log.debug("calculateUid:: Check if uid is set to null");
if (uid == null) {
log.debug("calculateUid:: uid was set to null");
log.debug("calculateUid:: check if workday account and seczetta account is present");
if (isWorkdayAccountPresent && isSecZettaAccountPresent) {
log.debug("calculateUid:: Workday and SecZetta accounts were present");
if (workdayAccountCreatedDate.before(seczettaAccountCreatedDate) || workdayUserid == seczettaUserid) {
log.debug("calculateUid:: Workday was created before SecZetta account");
log.debug("calculateUid:: setting uid to " + workdayUserid);
uid = workdayUserid;
}
else {
log.debug("calculateUid:: SecZetta was created before Workday");
log.debug("calculateUid:: Putting identity into an Error state");
uid = null;
}
}
else {
log.debug("calculateUid:: UID was not null");
if (isWorkdayAccountPresent) {
log.debug("calculateUid:: Workday Account Present is true");
log.debug("calculateUid:: uid is set to " + workdayUserid);
uid = workdayUserid;
}
if (isSecZettaAccountPresent) {
log.debug("calculateUid:: SecZetta Account Present is true");
log.debug("calculateUid:: uid is set to " + seczettaUserid);
uid = seczettaUserid;
}
}
}
}
}
log.error("calculateUid:: DetermineUID IdentityAttribute Rule: " + uid);
log.debug("calculateUid:: Retruning the calcualted UID");
return uid;
}
return calculateUid();
]]></Source>
</Rule>
here is the output of the rule validator. I did confirm that all 11 of my testcases still passed.
________________________________________________________________________________
SailPoint SaaS Rule Validator v3.0.49
By the SaaS Acceleration Team
(c)2022-23 SailPoint Technologies Inc
Command line arguments:
--file {-f} = "C:\\Users\\mpotti\\OneDrive - SSM Health\\Documents\\Visual Studio Code Projects\\SailPoint Rule Devkit\\rule-development-kit\\src\\main\\resources\\rules\\Rule - IdentityAttribute - DetermineUID IdentityAttribute Rule.xml"
Executed from: C:\Users\mpotti\OneDrive - SSM Health\Documents\Visual Studio Code Projects\SailPoint Rule Devkit\IdenityProfileRebuild
Jar location : C:\Users\mpotti\OneDrive%20-%20SSM%20Health\Documents\Visual%20Studio%20Code%20Projects\SailPoint%20Rule%20Devkit\sailpoint-saas-rule-validator-3.0.49-distribution\sailpoint-saas-rule-validator-3.0.49
________________________________________________________________________________
File name : C:\Users\mpotti\OneDrive - SSM Health\Documents\Visual Studio Code Projects\SailPoint Rule Devkit\rule-development-kit\src\main\resources\rules\Rule - IdentityAttribute - DetermineUID IdentityAttribute Rule.xml
Rule name : DetermineUID IdentityAttribute Rule
Rule type : IdentityAttribute
Process date : Tue Jun 17 13:20:39 CDT 2025
________________________________________________________________________________
Errors: (17)
Rule init variable 'idn' of type 'sailpoint.server.IdnRuleUtil' cannot be imported into BeanShell namespace due to: Sourced file: inline evaluation of: ``sailpoint.server.IdnRuleUtil idn = null;'' : Typed variable declaration : Class: sailpoint.server.IdnRuleUtil not found in namespace
Additional jar files can be added to the Rule Validator by adding them to the ./bsh-lib folder. The ./bsh-lib folder can be found where this Rule Validator jar file is copied, or a ./bsh-lib folder can be created where the val;idator is run and additional jar files can be copied there.
Rule init variable 'log' of type 'org.apache.commons.logging.Log' cannot be imported into BeanShell namespace due to: Sourced file: inline evaluation of: ``org.apache.commons.logging.Log log = null;'' : Typed variable declaration : Class: org.apache.commons.logging.Log not found in namespace
Rule init variable 'identity' of type 'sailpoint.object.Identity' cannot be imported into BeanShell namespace due to: Sourced file: inline evaluation of: ``sailpoint.object.Identity identity = null;'' : Typed variable declaration : Class: sailpoint.object.Identity not found in namespace
Rule init variable 'context' of type 'sailpoint.api.SailPointContext' cannot be imported into BeanShell namespace due to: Sourced file: inline evaluation of: ``sailpoint.api.SailPointContext context = null;'' : Typed variable declaration : Class: sailpoint.api.SailPointContext not found in namespace
Line 15 - [LintBSHAmbiguousName(76)] Could not retrieve definition for variable name 'identity'
15: if ( identity != null ) {
Line 16 - [LintBSHMethodInvocation(92)] Could not retrieve definition for variable name 'identity'
16: List linksList = identity .getLinks ( )
Variables may be injected by IDN, these variables can be injected into the rule by modifying the Rule's XML Signature, Add an Argument to the Input Section. Example adding a variable 'academicLevel':
<Inputs>
<Argument name="academicLevel" type="java.lang.String"...
Line 27 - [LintBSHType(38)] Class: Link not found in namespace
27: for ( Link link : linksList ) {
Line 28 - [LintBSHMethodInvocation(92)] Could not retrieve definition for variable name 'link'
28: if ( "Active Directory [source]" .equals ( link .getApplicationName ( ) ) ) {
Line 29 - [LintBSHMethodInvocation(92)] Could not retrieve definition for variable name 'link'
29: uid = ( String ) link .getAttribute ( "sAMAccountName" )
Line 33 - [LintBSHMethodInvocation(92)] Could not retrieve definition for variable name 'link'
33: if ( "Workday Production Sandbox [source]" .equals ( link .getApplicationName ( ) ) || "Workday Production [source]" .equals ( link .getApplicationName ( ) ) ) {
Line 35 - [LintBSHMethodInvocation(92)] Could not retrieve definition for variable name 'link'
35: workdayUserid = ( String ) link .getAttribute ( "USERID" )
Line 36 - [LintBSHMethodInvocation(92)] Could not retrieve definition for variable name 'link'
36: workdayAccountCreatedDate = link .getCreated ( )
Line 38 - [LintBSHMethodInvocation(92)] Could not retrieve definition for variable name 'link'
38: if ( "SecZetta [source]" .equals ( link .getApplicationName ( ) ) ) {
Line 40 - [LintBSHMethodInvocation(92)] Could not retrieve definition for variable name 'link'
40: seczettaFirstname = ( String ) link .getAttribute ( "personal_first_name" )
Line 41 - [LintBSHMethodInvocation(92)] Could not retrieve definition for variable name 'link'
41: seczettaLastname = ( String ) link .getAttribute ( "personal_last_name" )
Line 43 - [LintBSHMethodInvocation(92)] Could not retrieve definition for variable name 'link'
43: seczettaAccountCreatedDate = link .getCreated ( )
Line 69 - [LintBSHMethodInvocation(92)] Could not retrieve definition for variable name 'log'
69: log .error ( "DetermineUID IdentityAttribute Rule: " + uid )
________________________________________________________________________________
Runtime stats:
Started validation at Tue Jun 17 13:20:39 CDT 2025
1 Rules Analyzed
17 Errors
0 Warnings
Finished validation at: Tue Jun 17 13:20:40 CDT 2025
Process completed in 1 second.
________________________________________________________________________________
Validation status: FAILURE
________________________________________________________________________________
I am trying to decode there error messages. It is acting like it can not access a library. Here is the update rule.
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule name="DetermineUID IdentityAttribute Rule" type="IdentityAttribute">
<Signature returnType='String'>
<Inputs>
<Argument name='context'>
<Description>
A SailPointContext object used if its necessary
to query objects from the database.
</Description>
</Argument>
<Argument name='environment' type='Map'>
<Description>
Arguments passed to the aggregation or refresh task.
</Description>
</Argument>
<Argument name='identity' type='Identity'>
<Description>
The Identity of the object where the account is
assigned.
</Description>
</Argument>
<Argument name='link' type='Link'>
<Description>
The Link object that represents the account that is
being processed.
</Description>
</Argument>
<Argument name='attributeDefininition'>
<Description>
The AttributeDefinition object for this attribute.
</Description>
</Argument>
<Argument name='attributeSource'>
<Description>
The AttributeSource object from the AttributeDefinition.
</Description>
</Argument>
</Inputs>
<Returns>
<Argument name='attributeValue'>
<Description>
The value of the attribute that should be populated. The rule
should return this value.
</Description>
</Argument>
</Returns>
</Signature>
<Description>Determine Network Id.</Description>
<Source><![CDATA[
import sailpoint.object.Identity;
import sailpoint.object.Link;
import java.util.List;
import java.util.Date;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.commons.lang.StringUtils;
import sailpoint.object.Application;
import sailpoint.object.Field;
import sailpoint.server.IdnRuleUtil;
import sailpoint.tools.GeneralException;
Logger log = LogManager.getLogger(DetermineUid.class);
public String calculateSecZettaUserId(String seczettaFirstname, String seczettaLastname) throws GeneralException {
log.debug("calculateSecZettaUserId:: Check if first name is null");
if(seczettaFirstname != null) {
log.debug("calculateSecZettaUserId:: FristName is not null");
log.debug("calculateSecZettaUserId::Remove and special char");
seczettaFirstname = seczettaFirstname.replaceAll("[^a-zA-Z0-9]", "");
}
log.debug("calculateSecZettaUserId:: Set empty var for lastFirst5");
String lastFirst5 = "";
log.debug("calculateSecZettaUserId:: Check is last name is null");
if(seczettaLastname != null) {
log.debug("calculateSecZettaUserId:: LastName is not null");
log.debug("calculateSecZettaUserId:: Remove and special char");
seczettaLastname = seczettaLastname.replaceAll("[^a-zA-Z0-9]", "");
}
log.debug("calculateSecZettaUserId:: Set firstIntial to substring of 1 for firstName");
String firstIntial = seczettaFirstname.substring(0, 1);
log.debug("calculateSecZettaUserId:: Set lastNameLength to number of char in lastname");
int lastNameLength = seczettaLastname.length();
log.debug("calculateSecZettaUserId:: seczettaLastname.length: " + lastNameLength);
log.debug("calculateSecZettaUserId:: Check if length is <= 5");
if(lastNameLength <= 5){
log.debug("calculateSecZettaUserId:: count is less then or equal to 5");
log.debug("calculateSecZettaUserId:: Set lastFrist5 to seczettaLastname");
lastFirst5 = seczettaLastname.substring(0);
}
else{
log.debug("calculateSecZettaUserId:: count is greater then 5");
log.debug("calculateSecZettaUserId:: Substring of 5 for seczettaLastname");
lastFirst5 = seczettaLastname.substring(0, 5);
}
log.debug("calculateSecZettaUserId:: LastFirst5: " + lastFirst5);
log.debug("calculateSecZettaUserId::Set userName");
String userName = firstIntial + lastFirst5;
log.debug("calculateSecZettaUserId:: userName: " + userName);
log.debug("calculateSecZettaUserId:: Set counter to 1");
int counter = 1;
log.debug("calculateSecZettaUserId:: Step into while loop");
while(!isUnique(userName.toLowerCase() + "9" + Integer.toString(counter)))
{
log.debug("calculateSecZettaUserId:: inside Value for Unique: " + isUnique(userName.toLowerCase() + "9" + Integer.toString(counter)));
log.debug("calculateSecZettaUserId::increase counter by 1");
counter++;
log.debug("calculateSecZettaUserId:: after counter is set to: " + counter);
if(counter == 99)
{
log.debug("calculateSecZettaUserId:: count gretaer then 99 return null");
String userNameFull = null;
return userNameFull;
}
}
log.debug("calculateSecZettaUserId:: Set userNameFull");
String userNameFull = userName.toLowerCase() + "9" + Integer.toString(counter);
log.debug("calculateSecZettaUserId:: full userName is set to: " + userName);
log.debug("calculateSecZettaUserId:: Return userNameFull");
return userNameFull;
}
public boolean isUnique(String username) throws GeneralException {
log.debug("isUnique:: Stepping into isUnique()");
log.debug("isUnique:: username is: " + username);
log.debug("isUnique:: getName is set to: " + application.getName());
return !idn.accountExistsByDisplayName(application.getName(), username);
}
public String calculateUid() throws GeneralException{
//Initialize
log.debug("calculateUid:: Set uid to null");
String uid = null;
log.debug("calculateUid:: Check if identity is not null");
if (identity != null) {
log.debug("calculateUid:: Get a list of links");
List linksList = identity.getLinks();
log.debug("calculateUid:: List<Link> linksList: " + linksList);
if (linksList != null && !linksList.isEmpty()) {
log.debug("calculateUid:: Set isWorkdayAccountPresent to false");
boolean isWorkdayAccountPresent = false;
log.debug("calculateUid:: Set isSecZettaAccountPresent to false");
boolean isSecZettaAccountPresent = false;
log.debug("calculateUid:: Set workdayUserid to null");
String workdayUserid = null;
log.debug("calculateUid:: Set seczettaFirstname to null");
String seczettaFirstname = null;
log.debug("calculateUid:: Set seczettaLastname to null");
String seczettaLastname = null;
log.debug("calculateUid:: Set seczettaUserid to null");
String seczettaUserid = null;
log.debug("calculateUid:: Set workdayAccountCreatedDate to null");
Date workdayAccountCreatedDate = null;
log.debug("calculateUid:: Set seczettaAccountCreatedDate to null");
Date seczettaAccountCreatedDate = null;
log.debug("calculateUid:: Stepping into for loop");
for (Link link : linksList) {
log.debug("calculateUid:: Check if appliaction name is AD");
if ("Active Directory [source]".equals(link.getApplicationName())) {
log.debug("calculateUid:: Grabbing UID from AD");
uid = (String)link.getAttribute("sAMAccountName");
break;
}
else {
log.debug("calculateUid:: Check is application name is Workday");
if ("Workday Production Sandbox [source]".equals(link.getApplicationName()) || "Workday Production [source]".equals(link.getApplicationName())) {
log.debug("calculateUid:: Workday source account found");
isWorkdayAccountPresent = true;
log.debug("calculateUid:: Get USERID");
workdayUserid = (String)link.getAttribute("USERID");
log.debug("calculateUid:: Get Created Date");
workdayAccountCreatedDate = link.getCreated();
}
log.debug("calculateUid:: Check if application name is SecZetta");
if ("SecZetta [source]".equals(link.getApplicationName())) {
log.debug("calculateUid:: SecZetta source Account Found");
isSecZettaAccountPresent = true;
log.debug("calculateUid:: Get personal_first_name");
seczettaFirstname = (String)link.getAttribute("personal_first_name");
log.debug("calculateUid:: Get personal_last_name");
seczettaLastname = (String)link.getAttribute("personal_last_name");
log.debug("calculateUid:: SecZettaLastNAame: " + seczettaLastname);
if((String)link.getAttribute("sailpoint_username_ne_attribute") != null){
log.debug("calculateUid:: SailPoint UserName is not null");
seczettaUserid = (String)link.getAttribute("sailpoint_username_ne_attribute");
log.debug("calculateUid:: seczettaUserid was set to: " + seczettaUserid);
}
else{
log.debug("calculateUid:: Calcuatle the UID for seczetta user");
seczettaUserid = calculateSecZettaUserId(seczettaFirstname, seczettaLastname);
}
log.debug("calculateUid:: Get created date");
seczettaAccountCreatedDate = link.getCreated();
}
}
}
log.debug("calculateUid:: Check if uid is set to null");
if (uid == null) {
log.debug("calculateUid:: uid was set to null");
log.debug("calculateUid:: check if workday account and seczetta account is present");
if (isWorkdayAccountPresent && isSecZettaAccountPresent) {
log.debug("calculateUid:: Workday and SecZetta accounts were present");
if (workdayAccountCreatedDate.before(seczettaAccountCreatedDate) || workdayUserid == seczettaUserid) {
log.debug("calculateUid:: Workday was created before SecZetta account");
log.debug("calculateUid:: setting uid to " + workdayUserid);
uid = workdayUserid;
}
else {
log.debug("calculateUid:: SecZetta was created before Workday");
log.debug("calculateUid:: Putting identity into an Error state");
uid = null;
}
}
else {
log.debug("calculateUid:: UID was not null");
if (isWorkdayAccountPresent) {
log.debug("calculateUid:: Workday Account Present is true");
log.debug("calculateUid:: uid is set to " + workdayUserid);
uid = workdayUserid;
}
if (isSecZettaAccountPresent) {
log.debug("calculateUid:: SecZetta Account Present is true");
log.debug("calculateUid:: uid is set to " + seczettaUserid);
uid = seczettaUserid;
}
}
}
}
}
log.error("calculateUid:: DetermineUID IdentityAttribute Rule: " + uid);
log.debug("calculateUid:: Retruning the calcualted UID");
return uid;
}
return calculateUid();
]]></Source>
</Rule>
do you have all the dependencies in bsh-lib? I think they come default when you download the validator. Seems like it cannot resolve those libraries for some reason. I’d definitely recommend not running this stuff in a OneDrive directory either as I’ve seen that cause issues.