In SailPoint IdentityIQ (IIQ), scalability depends on how effectively you decouple business logic from manual processes. The IdentitySelector and Matchmaker are the core components that enable this decoupling, turning static identity data into dynamic, policy-driven automation.
1. The IdentitySelector: The “Definition”
An IdentitySelector is a configuration object that defines a subset of Identities. It is the “source of truth” for criteria used in Roles, Policies, and Lifecycle Events. There are four ways to define a selector:
-
Match List: Simple attribute-value pairs (e.g.,
department == "Finance"). Best for basic logic. -
Filter: A complex
sailpoint.object.Filterstring. Best for nestedAND/ORlogic. -
Script/Rule: BeanShell logic returning a boolean. Use sparingly; it is the most flexible but slowest to evaluate.
-
Population: A reference to a
GroupDefinition. This allows you to manage a group in one place and reuse it across multiple roles or policies.
2. The Matchmaker: The “Engine”
The sailpoint.api.Matchmaker is the service class that evaluates an Identity against a Selector. It serves two primary functions:
-
Boolean Evaluation: Determining if a specific
Identityobject satisfies theIdentitySelector. -
Filter Translation: Converting a Selector into a
sailpoint.object.Filter. This is critical for database-level performance, allowing you to query the database for all matching users rather than iterating through every identity in memory.
3. Core Use Cases
Automated Birthright Provisioning
During an Identity Refresh, the engine uses the Matchmaker to evaluate “Assignment Rules” on Roles. If a user matches the selector, the role is queued for provisioning.
Impact Analysis (The “What-If” Scenario)
Before deploying a new Role or Policy, you must calculate the impact. Using the Matchmaker to translate a selector into a QueryOptions object allows you to perform a count against the database in milliseconds, even in environments with 100k+ users.
4. Best Practices
-
Avoid “Rule” Selectors: Using a BeanShell script inside a selector prevents the Provisioning Engine from using optimized database queries. It forces a “Full Scan” of identities, significantly increasing Identity Refresh times.
-
Memory Management: When you must iterate, always use
context.decache(). Failing to do so will lead toOutOfMemoryErrorduring bulk processing. -
Type Safety: Always check
selector.getType()before processing. Treating a Script selector like a Filter will return a null pointer or an empty result set.
5. Sample Code
Here is a sample code that takes one or more Bundle names and one or more Identity names, retrieves their selectors, and uses Matchmaker to determine if the identities match the criteria for each role. Returns a Map of results.
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule language="beanshell" name="BundleFilterMatcherRule">
<Description>
This rule takes one or more Bundle names and one or more Identity names,
retrieves their selectors, and uses Matchmaker to determine if the identities
match the criteria for each role. Returns a Map of results.
</Description>
<Source><![CDATA[
import sailpoint.api.Matchmaker;
import sailpoint.object.Bundle;
import sailpoint.object.Identity;
import sailpoint.object.IdentitySelector;
import sailpoint.tools.Util;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.*;
/**
* Bulk evaluates matches between identities and roles.
*
* @param roleNames Object (String or List<String>) The names of the bundles to check.
* @param identityNames Object (String or List<String>) The names of the identities to evaluate.
* @return Map<String, Map<String, Boolean>> Results map structured as Map<IdentityName, Map<RoleName, IsMatch>>.
*/
public Map evaluateRoleMatches(Object roleNames, Object identityNames) {
Log log = LogFactory.getLog("rule.BundleFilterMatcher");
Map results = new HashMap();
List roles = Util.toList(roleNames);
List identities = Util.toList(identityNames);
if (Util.isEmpty(roles) || Util.isEmpty(identities)) {
log.warn("Roles or Identities list is empty. Nothing to evaluate.");
return results;
}
Matchmaker matchMaker = new Matchmaker(context);
for (String identityName : identities) {
Identity identity = context.getObjectByName(Identity.class, identityName);
Map identityResults = new HashMap();
if (identity == null) {
log.error("Identity not found: " + identityName);
results.put(identityName, "Identity Not Found");
continue;
}
for (String roleName : roles) {
Bundle bundle = context.getObjectByName(Bundle.class, roleName);
if (bundle == null) {
log.error("Bundle not found: " + roleName);
identityResults.put(roleName, false);
continue;
}
IdentitySelector selector = bundle.getSelector();
if (selector == null) {
identityResults.put(roleName, false);
continue;
}
try {
boolean isMatch = matchMaker.isMatch(selector, identity);
identityResults.put(roleName, isMatch);
} catch (Exception e) {
log.error("Error matching " + identityName + " against " + roleName + ": " + e.getMessage());
identityResults.put(roleName, false);
}
}
results.put(identityName, identityResults);
context.decache(identity);
}
return results;
}
// Entry point for Rule Runner or external calls
// Expected inputs: 'roleNames' (String or List) and 'identityNames' (String or List)
return evaluateRoleMatches(roleNames, identityNames);
]]></Source>
</Rule>