As part of a RBAC implementation, we needed to bulk import roles from a CSV file for a Client demo.
The file would contain data in the below format:
Role Name |
Role Type |
Sub Roles/IT Roles |
---|---|---|
IT A | IT | |
IT B |
IT | |
IT C | IT |
|
BG A | Business | IT A |
BG B | Business | "IT B" | "IT C" |
BG C | Business | "IT C" | "IT A" |
I am breaking down the steps to bulk upload Roles from a CSV file into IIQ.
Here we go...
For brevity, I have taken only 3 Business Roles and 3 IT Roles. Business Role BG A should have IT A as required IT Role. Similarly refer to the table above for other Business Role to IT Role mapping.
Now write a Custom rule as below to open the file and read data. Create a map out of the incoming data. Cells having multiple data will have to passed as List to the Bundle. (Bundle is the name of role object in Sailpoint). Code credits is mostly to tina.timmerman. I have changed the code as per the given use case.
<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd"> <Rule language="beanshell" modified="1527706764848" name="Standalone - Import Roles" type="Correlation"> <Description> Import template-defined Roles from a CSV file. </Description> <Signature returnType="Map"> <Inputs> <Argument name="log"> <Description> The log object associated with the SailPointContext. </Description> </Argument> <Argument name="context"> <Description> A sailpoint.api.SailPointContext object that can be used to query the database if necessary. </Description> </Argument> </Inputs> </Signature> <Source> // Sample Tool to import Roles. import java.io.File; import java.util.List; import java.util.HashMap; import sailpoint.object.Application; import sailpoint.object.Bundle; import sailpoint.object.Identity; import sailpoint.object.IdentitySelector; import sailpoint.object.IdentitySelector.MatchExpression; import sailpoint.object.IdentitySelector.MatchTerm; import sailpoint.object.Link; import sailpoint.object.Profile; import sailpoint.object.Filter; import sailpoint.object.QueryOptions; import sailpoint.object.Schema; import sailpoint.tools.RFC4180LineIterator; import sailpoint.tools.RFC4180LineParser; // Routine that takes a key/value hash of role data and builds it into roles // in IdentityIQ. public void buildRole (HashMap roleHash) { String roleName = (String) roleHash.get("Role Name"); String roleType = (String) roleHash.get("Role Type"); String subRoles = (String) roleHash.get("Sub Roles/IT Roles"); System.out.println("Role: '" + roleName + "' type: [" + roleType + "]" + "Sub ROles: " + subRoles); Bundle role = context.getObject(Bundle.class, roleName); if (null == role) { System.out.println(" - role does not exist, creating new: " + roleName); role = new sailpoint.object.Bundle(); } role.setName(roleName); role.setDescription("Testing..."); if (roleType.toLowerCase().startsWith("it")) { role.setType("it"); } else { role.setType("business"); } Identity ownerId = context.getObject(Identity.class, "spadmin"); role.setOwner(ownerId); System.out.println(" Debug --- Mandatory values set for : " + roleName); // Convert the sub-roles list into a java.util.List object. RFC4180LineParser subRolePaser = new RFC4180LineParser("|"); List subRolesList = subRolePaser.parseLine(subRoles); System.out.println(" Debug ---- Reading sub roles : "); // Handle Business-role specific build out options here. if (role.getType().contains("business")) { // Clear the previous list of requirements for this Role. if (null != role.getRequirements()) { role.getRequirements().clear(); } // Add the required IT roles for this business role. for (String subRoleName : subRolesList) { Bundle requiredRole = context.getObjectByName(Bundle.class, subRoleName); if (null == requiredRole) { System.out.println(" - ERROR: Required role not found: " + subRoleName); } else { role.addRequirement(requiredRole); } } } System.out.println("role: " + role.toXml()); context.saveObject(role); context.commitTransaction(); return; } // What delimiter do we want to use in the CSV file. String dlm = ","; String csvFileName = "/home/spadmin/ImplementerTraining/config/POC/Bulk-Roles-Mapping.csv"; File testFile = new File(csvFileName); if ((!testFile.exists()) || (testFile.isDirectory())) { System.out.println("Unable to find file: " + csvFileName); return; } System.out.println("Importing Roles from: '" + csvFileName + "' ..."); // Open the CSV file for reading. BufferedReader fileIn = new BufferedReader(new FileReader(csvFileName)); RFC4180LineParser parser = new RFC4180LineParser(dlm); int lineCounter = 0; ArrayList headerStrings = new ArrayList(); String thisLine = null; while (null != (thisLine = fileIn.readLine())) { List tokens = parser.parseLine(thisLine); // System.out.println(" tokens: " + tokens); if (lineCounter == 0) { // Header Line. // Assign indexes to each position. for (int i=0; i < tokens.size(); i++) { headerStrings.add(tokens.get(i).trim()); } } else { // Data Line. Read the data elements out. HashMap lineHash = new HashMap(); for (int i=0; i < tokens.size(); i++) { String headerString = headerStrings.get(i); String valueString = tokens.get(i); if (null != valueString) { valueString = valueString.trim(); } System.out.println ("header " + headerString + " == " + valueString); lineHash.put(headerString, valueString); } buildRole (lineHash); } lineCounter++; // Don't let objects accrue in memory, flush them out every so often. if ((lineCounter % 20) == 0) { context.decache(); } } fileIn.close(); System.out.println("Role import Complete."); return; </Source> </Rule>
Now run the rule from debug. Do not forget to specify the file name before running the rule.
Open BG C Business role, Go to Edit. You will find IT C and IT A as required IT roles to it.
Special thanks to you guys for giving me direction. Here is hoping to learn more from you guys:
kalyankumar.saha
reshu
suchismita.pal.aig
Hi there,
I implemented this rule in our 7.3p2 instance but am getting an error when trying to run it.
"Exception running rule: The application script threw an exception: sailpoint.tools.GeneralException: not-null property references a null or transient value: sailpoint.object.Bundle.name BSF info: Import Roles working at line: 0 column: columnNo"
I updated the file path and can see that the file is being read. The error appears to be in the buildRole method, any suggestions?
Try to change csv file encoding for the exception
As the error suggests the property cannot be null for "sailpoint.object.Bundle.name". Print the Role before committing/saving the transaction and validate if the "name" for the Bundle is set or not.
Hi,
When removing entitlements to an IT role, after running the Propagate Role Changes task (followed by an identity refresh), the entitlement still appears in the identity.
How have you overcome this?
Thanks.
Can you please add code to also choose entitlements for IT roles.
Hello,
I am implemented this rule in 8.3p2
I tried using your script logic to apply on my bulk roles. From the Role Management, it is able to populate the first row of data from the csv. However, when it reached the second row of data, it detected duplicate IT roles and gave me error "function .setName ( subRoleName ) BSF info: Rule - Role to Function PAM"
The first row of business and IT roles is populated in Role Management and add requirement works. But it does not work for the second row through script. Hope there is a solution
Thank you @cerguler for the suggestion regarding change of encoding type.