Introduction
A common use case when developing for provisioning is the need to provide information to a connector rule that is not available on the virtual appliance (VA). This can occur because you need identity information to perform additional logic, or because a request payload requires information that is not actively changing in the account.
The first case can often occur in Active Directory and Azure Native Rules, where a developer must take additional actions based on Identity attributes that are not part of the provisioning plan. They may not be stored in the target application at all.
The second case is a common occurrence for applications such as WebServices and JDBC applications which will often require all the account information be passed in with each query or request. This information generally already exists on the target account, and could include attributes like first name, last name, employeeId, etc.
This presents a problem. While the Before Provisioning Rule can retrieve these values from the Identity and add them to the provisioning plan as AttributeRequests, IDN will filter these AttributeRequests from the provisioning plan before it reaches the Connector Rule if their values are already detected on the target account.
Both cases are easily solved using AccountRequest Arguments.
This blog assumes that you have an understanding of connector rules and how to deploy them. For more information about rules, please read the rules documentation. You can also check out the rule development kit (RDK), which is a tool that helps with the building and testing of rules.
AccountRequest Arguments
Every AccountRequest has a set of arguments in the form of a sailpoint.object.Atributes
object. This object extends java.util.HashMap
, and can be accessed through as follows:
// setting the value for attribute named 'foo'
Attributes attributes = accountRequest.getArguments()
if (null == attributes) attributes = new Attributes();
attributes.putClean("foo","value of foo");
accountRequest.setArguments(attributes);
// alternate approach
Object value = accountRequest.addArgument("foo","value of foo");
---
// retrieving the value for attribute named 'foo'
Attributes attributes = accountRequest.getArguments()
Object value = attributes.get("foo");
// alternate approach
Object value = accountRequest.getArgument("foo");
These arguments will not be filtered from the provisioning plan, and can be used to store additional attributes and information from the Before Provisioning Rule to any one of the connector rules (Webservices Before Operation Rule, JDBC Provision rule, etc) which may not have access to the context.
Scenario 1: Active Directory
Example Scenario
The customer has two new hire scenarios: accounts created before hire date and accounts created after hire date. The AD After Create rule must also create an exchange account, and hide it from the GAL for prehire, but not posthire users.
Solution
For this solution, we will pass the lifecycle state of the user as an argument that is not provisioned to Active Directory
BeforeProvision Rule
note this code below uses IDN Java objects in the BeforePovision rule, but can easily be modified to fit IIQ available objects.
If (null != plan){
Identity identity = plan.getIdentity();
String lcs = identity.getAttribute(âcloudLifecycleStateâ);
List accountRequests = plan.getAccountRequests();
if (null != accountRequests) {
for (AccountRequest acctReq : accountRequests) {
//set the attributes on the Account Request with the updated values
acctReq.addArgument("lcs", lcs);
}
}
}
AfterCreate Powershell
Add-type -path utils.dll;
# Create SailPoint objects
$sReader = New-Object System.IO.StringReader([System.String]$env:Request);
$xmlReader = [System.xml.XmlTextReader]([sailpoint.utils.xml.XmlUtil]::getReader($sReader));
$requestObject = New-Object Sailpoint.Utils.objects.AccountRequest($xmlReader);
# retrieve argument from AccountRequest
# treat the $requestObject.Attributes object as a Map,
# and retrieve the attributes by the key
$lcs = $requestObject.Attributes.lcs
if($lcs -eq 'active'){
# create mailbox
} elseif ($lcs -eq 'prehire'){
#create mailbox and hide from GAL
}
Scenario 2: JDBC
Example Scenario:
JDBC Provisioning rule uses stored procedure that requires firstName, lastName, and manager for every update
Solution
For this solution, we will set arguments individually
BeforeProvision Rule
if (null != plan) {
Identity identity = plan.getIdentity();
//Get required attributes from identity that may not be part of the plan
String firstName = identity.getFirstname();
String lastName = identity.getLastname();
String managerName = null;
if (null != identity.getManager()) {
managerName = identity.getManager().getDisplayName();
}
//check account/attribute requests. If values are present in provisioning plan, use those values to prevent potential conflicting information
List accountRequests = plan.getAccountRequests();
if (null != accountRequests) {
for (AccountRequest acctReq : accountRequests) {
List attributeRequests = acctReq.getAttributeRequests();
if (null != attributeRequests) {
for (AttributeRequest req :attributeRequests) {
if (null != req.getName() && req.getName().equals("firstName")) {
firstName = req.getValue();
} else if (null != req.getName() && req.getName().equals("lastName")) {
lastName = req.getValue();
} else if (null != req.getName() && req.getName().equals("managerName")){
managerName = req.getValue();
}
}
}
/*
* set the attributes on the Account Request
* with the updated values
*/
acctReq.addArgument("firstName", firstName);
acctReq.addArgument("lastName", lastName);
acctReq.addArgument("managerName", managerName);
}
}
}
Scenario 3: WebServices
Example Scenario
Websevices Add and Remove Entitlement payloads function as a âPostâ operation, and require payload to include final list of entitlements on the updated account (after add/remove takes place).
Solution
For this Solution, we will use retrieve and set arguments via the Attributes object. note: The objects availble to the Before Provision rule are different between IDN and IIQ. The code below represents IDN implementation. For IIQ, update the code accordingly
Before Provision Rule
if (null != plan) {
List accountRequests = plan.getAccountRequests();
if (null != accountRequests) {
for (AccountRequest acctReq : accountRequests) {
if(AccountRequest.Operation.Modify.equals(acctReq.getOp())) {
// Get currently assigned entitlements
String nativeIdentity = acctReq.getNativeIdentity();
Account account = idn.getAccountByNativeIdentity(application.getName(), nativeIdentity);
Map accountAttributes = account.getAttributes();
List currentRoles = new ArrayList();
if(null != accountAttributes.get("role")) {
//add all roles on the account to the currentRoles list
currentRoles.addAll(getValues(accountAttributes.get("role")));
}
//Get current attribute requests
List attributeRequests = acctReq.getAttributeRequests("role");
for (AttributeRequest attributeRequest : attributeRequests) {
if (attributeRequest.getOperation().equals(ProvisioningPlan.Operation.Add)){
//if operation is 'add' add role(s) to currentRoles
attributeRequestsToRemove.add(attributeRequest);
} else if (attributeRequest.getOperation().equals(ProvisioningPlan.Operation.Remove)){
//if operation is 'remove' remove role(s) to currentRoles
currentRoles.removeAll(getValues(attributeRequest.getValue()));
}
}
/*
* Get arguments from Account Request to make sure we
* do not overwrite existing arguments
*/
Attributes attributes = acctReq.getArguments();
if (null == attribtues) {
attributes = new Attributes();
}
/**
* Add ALL roles as a list to the Attributes (arguments)
*/
attributes.putClean("currentRoles",currentRoles);
acctReq.setArguments(attributes);
}
}
}
}
Before Operation Rule
Then we just have to retrieve the list of roles from the AccountRequest.Attributes
List currentRoles = null;
if (null != provisioningPlan) {
List accountRequests = provisioningPlan.getAccountRequests();
if (null != accountRequests) {
for (AccountRequest acctReq : accountRequests) {
Attributes attributes = acctReq.getArguments();
if (null != attributes) {
currentRoles = attributes.get("currentRoles");
}
}
}
// update and format the requestEndPoint with the required data here
}
Conclusion
This approach will allow you to pass specific attribute information, along with metadata you may need for logic in your connector rule (a tag to determine if this operation represents specific type of user, for example).
This is not limited to IdentityNow. IdentityIQ faces the same issue. While an IIQ developer has access to the context, and can work around these limitations, it is worth considering a future where the product and code may be migrated to an IDN instance, where that is not a viable option.