Sure,
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule name="DistinguishedNameGenerator" type="AttributeGenerator">
<Description>
The "DistinguishedNameGenerator" rule generates a unique Distinguished Name (DN) for a user based on their first name, last name, and organizational unit (OU) path. It retrieves the base DN from application settings, considering regional variations. The rule ensures uniqueness by checking for existing records and retrying if necessary. If an account is disabled, it places the user in the "DISABLED ACCOUNTS" OU. The DN is constructed dynamically, with retries using a numerical suffix if conflicts occur. The rule also checks for DN uniqueness and retries if a match is found, up to a maximum number of attempts, before returning the final unique DN.
</Description>
<Source><![CDATA[
import sailpoint.object.Application;
import sailpoint.object.Field;
import sailpoint.object.Identity;
import sailpoint.server.IdnRuleUtil;
import sailpoint.tools.GeneralException;
import javax.naming.NamingException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
private int MAX_ATTEMPTS = 10;
/**
* Retrieves the "forestName" from the application settings.
* This method looks for the "forestSettings" attribute within the application object.
* If the attribute is found and contains a list of maps, it extracts the "forestName"
* from the first map in the list. If no such attribute or value is found, it returns null.
* @return the forest name if found, or null if not found or the attribute is in an unexpected format
*/
private String getBaseDNFromSource() {
// Retrieve the attribute value for "forestSettings"
Object attributeValue = application.getAttributeValue("forestSettings");
// Check if the attribute is a List
if (attributeValue instanceof List) {
List forestSettingsList = (List) attributeValue;
// Ensure the list is not empty
if (!forestSettingsList.isEmpty()) {
Object firstElement = forestSettingsList.get(0);
// Check if the first element is a Map and contains "forestName"
if (firstElement instanceof Map) {
Map forestSettingsMap = (Map) firstElement;
Object forestName = forestSettingsMap.get("forestName");
// Return the "forestName" if found
if (forestName != null) {
return forestName.toString();
}
}
}
}
// Return null if "forestName" is not found
return null;
}
/**
* Retrieves the base DN based on the given organizational unit path.
* This method determines the base DN by checking the organizational unit path for known regions.
* It supports regions like Europe, America, Africa, Asia, and Oceania. If no region is matched,
* the method returns the default base DN.
* @param adOrganizationalUnitPath the target OU path
* @return the appropriate base DN
* @throws IllegalArgumentException if the organizational unit path is null or empty
*/
public String getLdapBaseDnFromOrganizationalUnitPath(String adOrganizationalUnitPath) {
if (adOrganizationalUnitPath == null || adOrganizationalUnitPath.isEmpty()) {
throw new IllegalArgumentException("getLdapBaseDnFromOrganizationalUnitPath - adOrganizationalUnitPath cannot be null or empty");
}
// Normalize the input to uppercase to ensure case-insensitive comparison
String normalizedDn = adOrganizationalUnitPath.toUpperCase();
// Map region to their respective base DN prefix
HashMap regionPrefixMap = new HashMap();
regionPrefixMap.put("DC=EUROPE", "DC=EUROPE");
regionPrefixMap.put("DC=AMERICA", "DC=AMERICA");
regionPrefixMap.put("DC=AFRICA", "DC=AFRICA");
regionPrefixMap.put("DC=ASIA", "DC=ASIA");
regionPrefixMap.put("DC=OCEANIA", "DC=OCEANIA");
// Loop through the map to find a matching region
for (Object entryObj : regionPrefixMap.entrySet()) {
Map.Entry entry = (Map.Entry) entryObj; // Cast to Map.Entry
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if (normalizedDn.contains(key)) {
return value + "," + getBaseDNFromSource();
}
}
// Return the default base DN if no region is matched
return getBaseDNFromSource();
}
/**
* Calculates a unique distinguishedName (DN) for a user, potentially placing them in a "DISABLED ACCOUNTS" OU if needed.
* @param givenName the user's given name
* @param sn the user's surname
* @param adOrganizationalUnitPath the target OU path
* @param isDisabled whether the account should be placed in a disabled OU
* @return the calculated distinguishedName
* @throws NamingException if a naming error occurs during the process
*/
public String calculateDistinguishedName(String givenName, String sn, String adOrganizationalUnitPath, String isDisabled) throws NamingException, GeneralException {
// Validate input
validateInput(givenName, sn, adOrganizationalUnitPath);
// Convert isDisabled to boolean
boolean disabled = Boolean.parseBoolean(isDisabled);
// Calculate basePath
String basePath = buildBasePath(adOrganizationalUnitPath, disabled);
String distinguishedName = buildBaseDistinguishedName(givenName, sn, basePath);
int attempt = 0;
// Retry loop for uniqueness
while (attempt < MAX_ATTEMPTS) {
if (attempt > 0) {
distinguishedName = appendSuffixToGivenName(distinguishedName, attempt);
log.error("calculateDistinguishedName - Attempt " + attempt + ": Trying distinguishedName: " + distinguishedName);
}
if (isUnique(distinguishedName)) {
log.error("calculateDistinguishedName - distinguishedName " + distinguishedName + " is unique.");
return distinguishedName;
} else {
attempt++;
log.error("calculateDistinguishedName - distinguishedName " + distinguishedName + " already exists. Retrying...");
}
}
throw new NamingException("calculateDistinguishedName - Cannot generate unique distinguishedName for " + givenName + " " + sn + " after " + MAX_ATTEMPTS + " attempts");
}
/**
* Validates the input parameters.
* @param givenName the user's given name
* @param sn the user's surname
* @param adOrganizationalUnitPath the target OU path
* @throws IllegalArgumentException if any of the input parameters are null or empty
*/
private void validateInput(String givenName, String sn, String adOrganizationalUnitPath) {
if (givenName == null || givenName.isEmpty() || sn == null || sn.isEmpty() || adOrganizationalUnitPath == null || adOrganizationalUnitPath.isEmpty()) {
throw new IllegalArgumentException("validateInput - None of the required parameters can be null or empty");
}
}
/**
* Builds the base distinguishedName based on the user's given name, surname, and OU path.
* @param givenName the user's given name
* @param sn the user's surname
* @param basePath the organizational unit path
* @return the base distinguishedName
*/
private String buildBaseDistinguishedName(String givenName, String sn, String basePath) {
return "CN=" + sn + " " + givenName + "," + basePath;
}
/**
* Builds the full path to the target organizational unit.
* @param adOrganizationalUnitPath the target OU path
* @param isDisabled whether the account should be placed in a disabled OU
* @return the full base path for DN construction
*/
private String buildBasePath(String adOrganizationalUnitPath, boolean isDisabled) {
if (isDisabled) {
return "OU=DISABLED ACCOUNTS," + getLdapBaseDnFromOrganizationalUnitPath(adOrganizationalUnitPath);
} else {
return adOrganizationalUnitPath;
}
}
/**
* Appends a numerical suffix to the distinguishedName.
* @param distinguishedName the current distinguishedName
* @param attempt the retry attempt number
* @return the distinguishedName with the appended suffix
*/
private String appendSuffixToGivenName(String distinguishedName, int attempt) {
String incrementSuffix = String.valueOf(attempt);
// Find the position of the first comma (which separates CN part from the rest of the DN)
int firstCommaIndex = distinguishedName.indexOf(',');
// Extract the CN part and append the suffix
String cnPart = distinguishedName.substring(0, firstCommaIndex); // Get CN=NAPOLEONI Nicolas
String restOfDn = distinguishedName.substring(firstCommaIndex); // Get the rest of the DN after the first comma
// Append the suffix to the CN part
cnPart = cnPart + incrementSuffix; // CN=NAPOLEONI Nicolas1
// Return the full distinguishedName with the modified CN part
return cnPart + restOfDn; // CN=NAPOLEONI Nicolas1,ou=USERS,ou=ACG,ou=MRS,...
}
/**
* Checks if the generated distinguishedName is unique.
* @param distinguishedName The distinguishedName to check.
* @return true if the distinguishedName is unique, false otherwise.
*/
public boolean isUnique(String distinguishedName) {
List sourcesId = Collections.singletonList(application.getId());
List searchValues = Collections.singletonList(distinguishedName); // Use typed List
int count = idn.attrSearchCountAccounts(sourcesId, "distinguishedName", "Equals", searchValues);
return count == 0; // Return true if no existing accounts found
}
return calculateDistinguishedName(identity.getFirstname(), identity.getLastname(), identity.getAttribute("adOrganizationalUnitPath"), identity.getAttribute("cloudLifecycleState"));
]]></Source>
</Rule>
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule name="SamAccountNameGenerator" type="AttributeGenerator">
<Description>
This rule generates a unique SAM account name using input parameters like AD trigram, first name, and last name, ensuring it’s no longer than 20 characters. If a forced SAM account name is provided, it uses that directly. The rule checks for existing accounts and attempts up to 10 retries with an incrementing number if a conflict occurs. For contractors, it sets the AD trigram to “EXT”; for employees, it uses the provided trigram. The last name is sanitized by removing spaces and non-alphanumeric characters. If a unique name can’t be generated within the retry attempts, a NamingException is thrown. </Description>
<Source><![CDATA[
import sailpoint.object.Application;
import sailpoint.object.Field;
import sailpoint.object.Identity;
import sailpoint.server.IdnRuleUtil;
import sailpoint.tools.GeneralException;
import javax.naming.NamingException;
private int MAX_USERNAME_LENGTH = 20;
private int MAX_ATTEMPTS = 10;
/**
* Generates a sAMAccountName based on the input parameters, ensuring uniqueness and proper formatting.
*
* @param adTrigram The AD trigram to use.
* @param personType The type of person (employee or contractor).
* @param firstName The first name of the person.
* @param lastName The last name of the person.
* @param forcedSamAccountName Forced sAMAccountName if provided.
* @return The generated sAMAccountName.
* @throws GeneralException If there are errors checking uniqueness.
* @throws NamingException If a unique name cannot be generated after multiple attempts.
*/
public String calculateSamAccountName(String adTrigram, String personType, String firstName, String lastName, String forcedSamAccountName) throws GeneralException, NamingException {
// Validate input
if (personType == null || personType.isEmpty() || lastName == null || lastName.isEmpty() || firstName == null || firstName.isEmpty() || adTrigram == null || adTrigram.isEmpty()) {
log.error("calculateSamAccountName - One or more required parameters are null or empty");
throw new IllegalArgumentException("calculateSamAccountName - None of the required parameters can be null or empty");
}
// Return forced sAMAccountName if provided
if (forcedSamAccountName != null && !forcedSamAccountName.isEmpty()) {
log.error("calculateSamAccountName - Using forced sAMAccountName: " + forcedSamAccountName);
return forcedSamAccountName;
}
// Determine trigram
String trigram = personType.equalsIgnoreCase("contractor") ? "EXT" : adTrigram;
log.error("calculateSamAccountName - Trigram determined as: " + trigram);
// Sanitize last name
String sanitizedLastName = sanitizeName(lastName);
log.error("calculateSamAccountName - Sanitized last name: " + sanitizedLastName);
// Process first name parts
String[] firstNameParts = firstName.trim().split(" ");
String firstNamePart = firstNameParts[0].substring(0, 1).toUpperCase() + (firstNameParts.length > 1 ? firstNameParts[1].substring(0, 1).toUpperCase() : "");
log.error("calculateSamAccountName - Processed first name part: " + firstNamePart);
// Construct base sAMAccountName
String baseSamAccountName = (trigram + "." + firstNamePart + sanitizedLastName).toUpperCase();
log.error("calculateSamAccountName - Base sAMAccountName generated: " + baseSamAccountName);
// Truncate to 20 characters if needed
if (baseSamAccountName.length() > MAX_USERNAME_LENGTH) {
baseSamAccountName = baseSamAccountName.substring(0, MAX_USERNAME_LENGTH);
log.error("calculateSamAccountName - Base sAMAccountName truncated to: " + baseSamAccountName);
}
// Try generating a unique sAMAccountName with retry mechanism
String sAMAccountName = baseSamAccountName;
boolean isNameConflict = true;
int attempt = 0;
while (isNameConflict && attempt < MAX_ATTEMPTS) {
if (attempt > 0) {
String incrementSuffix = String.valueOf(attempt);
sAMAccountName = baseSamAccountName.substring(0, Math.min(MAX_USERNAME_LENGTH - incrementSuffix.length(), baseSamAccountName.length())) + incrementSuffix;
log.error("calculateSamAccountName - Attempt " + attempt + ": Trying sAMAccountName: " + sAMAccountName);
}
// Check if the username is unique
boolean isUnique = isUnique(sAMAccountName);
if (isUnique) {
isNameConflict = false;
log.error("calculateSamAccountName - sAMAccountName " + sAMAccountName + " is unique.");
} else {
attempt++;
log.error("calculateSamAccountName - sAMAccountName " + sAMAccountName + " already exists. Retrying...");
}
}
// If the name is still not unique after MAX_ATTEMPTS, throw an exception
if (isNameConflict) {
throw new NamingException("calculateSamAccountName - Cannot generate unique sAMAccountName for " + firstName + " " + lastName + " after " + MAX_ATTEMPTS + " attempts");
}
log.error("calculateSamAccountName - Successfully generated sAMAccountName: " + sAMAccountName);
return sAMAccountName;
}
/**
* Sanitizes a name by removing unwanted characters.
*
* @param name The name to be sanitized.
* @return The sanitized name.
*/
protected String sanitizeName(String name) {
String sanitized = name.replaceAll("\\s+", "").replaceAll("[^a-zA-Z0-9]", "");
log.error("sanitizeName - Sanitized name: " + sanitized);
return sanitized;
}
/**
* Checks if the generated distinguished name is unique.
* @param sAMAccountName The sAMAccountName to check.
* @return true if the distinguished name is unique, false otherwise.
*/
public boolean isUnique(String sAMAccountName) {
List sourcesId = Collections.singletonList(application.getId());
List searchValues = Collections.singletonList(sAMAccountName); // Use typed List
int count = idn.attrSearchCountAccounts(sourcesId, "sAMAccountName", "Equals", searchValues);
return count == 0; // Return true if no existing accounts found
}
return calculateSamAccountName(identity.getAttribute("adTrigram"), identity.getAttribute("personType"), identity.getFirstname(), identity.getLastname(), identity.getAttribute("forcedSamaccountname"));
]]></Source>
</Rule>
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule name="UserPrincipalNameGenerator" type="AttributeGenerator">
<Description>
This rule generates a unique SAM account name using input parameters like AD trigram, first name, and last name, ensuring it’s no longer than 20 characters. If a forced SAM account name is provided, it uses that directly. The rule checks for existing accounts and attempts up to 10 retries with an incrementing number if a conflict occurs. For contractors, it sets the AD trigram to “EXT”; for employees, it uses the provided trigram. The last name is sanitized by removing spaces and non-alphanumeric characters. If a unique name can’t be generated within the retry attempts, a NamingException is thrown. </Description>
<Source><![CDATA[
import sailpoint.object.Application;
import sailpoint.object.Field;
import sailpoint.object.Identity;
import sailpoint.server.IdnRuleUtil;
import sailpoint.tools.GeneralException;
import javax.naming.NamingException;
private int MAX_ATTEMPTS = 10;
public String calculateUserPrincipalName(String sAMAccountName, String email, String upnAlignedWithMail) throws NamingException {
// Validate input
if (sAMAccountName == null || sAMAccountName.isEmpty() || email == null || email.isEmpty() || upnAlignedWithMail == null || upnAlignedWithMail.isEmpty()) {
throw new IllegalArgumentException("calculateUserPrincipalName - None of the required parameters can be null or empty");
}
String baseUpn = null;
if (sAMAccountName != null && !sAMAccountName.isEmpty()) {
baseUpn = sAMAccountName + "@XXXXX.COM";
log.error("calculateUserPrincipalName - Base UPN from sAMAccountName: " + baseUpn);
}
if ("yes".equalsIgnoreCase(upnAlignedWithMail)) {
if (email == null || email.isEmpty()) {
throw new IllegalArgumentException("calculateUserPrincipalName - Can't calculate userPrincipalName for " + identity.getFirstname() + " " + identity.getLastname() + " because it must equal the email, but the email is empty.");
}
if (!email.equalsIgnoreCase("[email protected]")) {
baseUpn = email;
log.error("calculateUserPrincipalName - Base userPrincipalName aligned with email: " + baseUpn);
}
}
if (baseUpn == null) {
throw new IllegalArgumentException("calculateUserPrincipalName - Unable to determine baseUpn");
}
// Try generating a unique UPN name with retry mechanism
boolean isNameConflict = true;
String upn = baseUpn;
int attempt = 0;
while (isNameConflict && attempt < MAX_ATTEMPTS) {
if (attempt > 0) {
String incrementSuffix = String.valueOf(attempt);
String[] parts = baseUpn.split("@");
String upnPrefix = parts[0];
String domain = parts[1];
if (upnPrefix.length() + incrementSuffix.length() > 64) {
upnPrefix = upnPrefix.substring(0, 64 - incrementSuffix.length());
}
upn = upnPrefix + incrementSuffix + "@" + domain;
log.error("calculateUserPrincipalName - Conflict found, attempting new userPrincipalName: " + upn);
}
// Check if the username is unique
boolean isUnique = isUnique(upn);
if (isUnique) {
isNameConflict = false;
log.error("calculateUserPrincipalName - userPrincipalName " + upn + " is unique.");
} else {
attempt++;
log.error("calculateUserPrincipalName - userPrincipalName " + upn + " already exists. Retrying...");
}
}
// If the name is still not unique after MAX_ATTEMPTS, throw an exception
if (isNameConflict) {
throw new NamingException("calculateUserPrincipalName - Cannot generate unique userPrincipalName for " + identity.getFirstname() + " " + identity.getLastname() + " after " + MAX_ATTEMPTS + " attempts");
}
log.error("calculateUserPrincipalName - Successfully generated userPrincipalName: " + upn);
return upn;
}
/**
* Checks if the generated userPrincipalName name is unique.
* @param userPrincipalName The userPrincipalName to check.
* @return true if the userPrincipalName name is unique, false otherwise.
*/
public boolean isUnique(String userPrincipalName) {
List sourcesId = Collections.singletonList(application.getId());
List searchValues = Collections.singletonList(userPrincipalName); // Use typed List
int count = idn.attrSearchCountAccounts(sourcesId, "userPrincipalName", "Equals", searchValues);
return count == 0; // Return true if no existing accounts found
}
return calculateUserPrincipalName(identity.getAttribute("sAMAccountName"), identity.getAttribute("email"), identity.getAttribute("upnAlignedWithMail"));
]]></Source>
</Rule>