Abstract
There’s limited resources describing an issue with jsonPath attribute mapping and correlating ACCOUNT attributes to IDENTITY attributes.
The ideal accountObject contains explicit keys for the desired output values, but what if the attribute value you’re looking for is nested in a list or list of lists? jsonPath, with conditional statements, will be able to find the value, but depending on where the key lies, the output value may either be “foo” or [“foo”].
This is the case with ADP’s workersV2 API where various attributes are nested under a list (workerAssignments) such as “title”, “department”, and most notably “manager”. Generally this is a non issue for mapping Identity Attributes to identities, but when the ACCOUNT attribute has a value of [“foo”] and is comparing against and identity attribute value “foo” correlation fails.
interpreting the rawResponseObject
ADP nests title
, department
, manager
, and other ephemeral worker data under workAssignemts.
[
{
"id": "0123456",
"name": "john",
"workAssignments": [
{
"title": "Engineer",
"department": "IT",
"manager": "654321",
"primary": false
},
{
"title": "SysAdmin",
"department": "Finance",
"manager": "654321",
"primary": true
}
]
},
...
]
Fortunately in the list of workAssignments, there’s a key \ value indicator that we can search with JSON path to get the most recent employee data.
{ ...
"connectorAttributes": {...
"connectionParameters": [...
"refMappingObj"
"id": "id",
"name": "name",
"title": "workAssignments[?(@.primary==true)].positionID",
"department": "workAssignments[?(@.primary==true)].department",
"manager": "workAssignments[?(@.primary==true)].manager"
]
}
}
}
processedResponseObject
The jsonPath for any of the values nested in the list are represented [value], whereas the direct attribute are strings.
{
"id":"0123456",
"name":"john",
"title":[Engineer],
"department":[IT],
"manager":[654321]
}
Correlation and Conflicts
Source - Attribute manager
has a type set as String
- Aggregation HTTP Get Operations
- attributeMapping for
manager
= workAssignments[?(@.primaryIndicator==true)].manager - Sailpoint interprets the manager string value as [654321] despite it being a string
- The connector does not attempt to normalize it as a string, similar to that of setting the attribute type to “multi-valued” however, multi-valued account attributes are not supported (hidden) from the manager correlation.
- attributeMapping for
Source - Attribute name
has a type set as String
- Aggregation HTTP Get Operations
- attributeMapping for AttributeB jsonPath
- attributeMapping for
name
= name
- attributeMapping for
- Sailpoint interprets the
name
string value as “john”
- attributeMapping for AttributeB jsonPath
Identity Profile Mapping
manager
mapped to IdentityAttribute- Identity Attribute is represented as a string “654321”
name
mapped to IdentityAttribute- Identity Attribute is represented as a string “john”
Let’s review the comparitive operators for a Manager Correlation on the source: ADP
.
IdentityAttribute: id == AccountAttribute: manager
Or
654321 == [654321] # Which is False
.
The discrepancy largely impacts correlation as the Identity Attributes vs. Account Attributes greatly differ in type. Identity Attributes appear to be normalized, however the Account Attributes are still represented as a jsonPath output enclosed with []
.
Workaround \ Solution
Disclaimer: As always approach with caution
For our case, every attribute mapped is intended to be a string (no multi-valued account attributes), so we took a more dynamic approach (converting all values of “list” to string), however the example below is explicitly for the managerID (example) Account Attribute.
import connector.common.JsonUtil;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import sailpoint.tools.GeneralException;
for (int i = 0; i < processedResponseObject.size(); i++) {
Map responseMap = (Map) processedResponseObject.get(i);
if (responseMap != null) {
Object value = responseMap.get("managerID");
if (value instanceof List) {
List listValue = (List) value;
if (listValue.size() > 0) {
responseMap.put("managerID", listValue.get(0).toString());
} else {
responseMap.put("managerID", "");
}
} else {
if (value != null) {
responseMap.put("managerID", value.toString());
} else {
responseMap.put("managerID", "");
}
}
}
}
return processedResponseObject;
Applying this as an AfterRule to the AggregateUsers and GetObject HTTP operations normalized the enclosed jsonPath results so that all account attribute values are strings and allowed both the Identity and Manager correlation to function as expected.