Did you know that last month Kenna Security released a new feature, multiple roles per user? What does this mean for Kenna’s User APIs? Per Katie Kolon’s article, “Understanding Multiple Roles per User”, the following User APIs have been modified:
As stated in the article, the changes have impacts on your current code that uses the above User APIs. Basically, the role field has been changed to roles. By the way, Delete User has not changed.
Now that that is out of the way, let’s look at a use case. As the administrator, you might want to modify your users’ roles to have more granularity. With multiple roles per user, the user has multiple roles, so that you don’t have to give more privileges than are necessary. This use case updates users with the specified roles. A CSV file that contains the user’s email address and the roles to be assigned to the user.
The example code, update_roles.py, obtains a list of users and a list of roles. Each line on the CSV file is read, and the user email is mapped to a user ID and the role name is mapped to a role ID. With the user ID and the role IDs, the user’s roles are updated.
Looking at the code, you’ll notice that there is a Python class, User.
This is the first time I’ve used a class. I like using a class to contain information about an object and then hash to the objection with a dictionary. The User class stores the user’s email address, ID, and sorted role IDs. It is straightforward to add more information to the class if required.
Obtaining the roles and users
The function get_user_ids() obtains a dictionary of users hashed by the user’s email address.
The function invokes the List Users API. For each user, a User object is created with the desired information. The newly created object is pointed to by a dictionary with the user’s email address as the key.. As you can see I have left some debugging print statements in the code.
The function get_role_ids() is very similar to get_user_ids(), except that in get_role_ids() the List Roles API is invoked and the dictionary maps to the role ID, not a User object.
Our next function, map_role_names_to_ids(), takes the role name to ID dictionary and a list of role names as input parameters to return a list of valid role IDs.
Note the function verifies if the role name exists and sorts the role IDs. The reason for sorting the role IDs is for comparison purposes later on. If the role name is invalid, a warning message is printed and the function carries on.
Finally, we get to update_user_() which updates one user specified by user ID and the role IDs to update by invoking the Update User API. Updating the role IDs completely obliterates the current role IDs. If you want to add role IDs to the current roles ID(s), then you’ll have to obtain the current role IDs and concatenate them with the new role IDs.
You could update the user with role names, but I prefer role IDs, because role IDs are used in API URLs. IDs are the coin of the API realm.
Tying it all together
Note that the default CSV file name is user_roles.csv, and that it can be modified by the first command input parameter.
The first thing to do is get the user and role dictionaries.
The CSV file is read and each user is processed:
The code above uses reader from the Python CSV library. For each CSV row:
- Skip comments.
- Assign the first column to the email address.
- Validate the user’s email address.
- Assign the rest of columns to role names.
- Map role names to role IDs.
- If the role IDs from the CSV file are the same as the user’s current role IDs, print an informative message; else update the user with the new role IDs.
Notice that the code can be used to verify the role IDs are correct as well as updating.
Multiple roles per user allow you control user permissions with more granularity; and this script makes it straightforward to update each user.
If you’re interested in playing with these samples, they’re located in the Kenna Security blog_samples repo in the python/user_roles directory. By the way, I left the list_user_roles.py script that lists the role IDs for each user that I used for verification. Also in Kenna’s All_Samples repository, there is an add_user sample in Ruby.