Walking through the ServiceNow Driver - Part 4

This series is walking through the ServiceNow IDM driver configuration. In general, the docs never discuss this level of detail and while I concede this is as boring as eating paint as it dries, nonetheless I like to think there is value when you run into an issue in a policy and can find a discussion of what it is doing. Knowing what is supposed to happen helps understand what is happening, and when it is not working correctly.

Last we left off, in the Subscriber channel, Command Transform, in the policy object:


There are eight rules in this policy.

     Login Disabled attribute change in IDVault
     Remove Group Membership changes if using Group Entitlement
     Veto if event not relevant
     User Account entitlement change (Disable Option)
     User Account entitlement change (Delete Option)
     Group entitlement change
     Role entitlement change
     Department entitlement change

Got through the first five and now on to the sixth.

Group entitlement change

Here we check for the GCV flag that Group Entitlements are in use. Then test for a user, a modify, and the Group entitlement changing in the current document.

As discussed back in the UserAccount entitlement, disable option rule, the loop is over Removed Entitlement for Group, because this gives us the variable, current-node, as the payload of the Entitlement with the only part that matters.

This is important since the way Entitlements work is complicated, more so than you would otherwise expect. I have spent a fair amount of time, talking about Entitlements in previous arguments and I do not want to recapitulate it all here. Thus you can go read more about it in these articles:

Series: Talking about Entitlements

The key take away as a fast summary is that DirXML-EntitlementRef is a Path syntax attribute that uses the normally text 'path' component, is actually 'path.xml' which requires valid XML in the component. Inside the XML, the &ltr;param&gtr; node contains the actual payload of interest.

Here is an example of a &ltr;modify&gtr; event that has the attribute changing.

      &ltr;modify-attr attr-name="DirXML-EntitlementRef"&gtr;
&ltr;value timestamp="1352147831#8" type="structured"&gtr;
&ltr;component name="nameSpace"&gtr;0&ltr;/component&gtr;
&ltr;component name="volume"&gtr;\ACME\services\IDM\DriverSet\Google Apps 2\NOVLGGLEUSER-Account&ltr;/component&gtr;
&ltr;component name="path.xml"&gtr;

In the above example you can see that the attribute in play is DirXML-EntitlementRef (for fun, to select that in XPATH, you could use XPATH of modify-attr/attr[@attr-name="DirXML-EntitlementRef]/add-value to select the node set of information.

Inside the path.xml component, you can see XML that looks like this:


The for each loop, nicely delivers just the contents of &ltr;param&gtr; into the variable current-node. This payload started as a single value, which in your EntitlementConfiguration object is known as 'legacy' format. Then IDM 3.61 and the SAP UM drivers, needed a way to represent a set of key value pairs since one entitlement could provide a couple of data values and they pipe separated the values. Thus CNAME=PRDCLNT100|LSNAME=200 was a possible value. With IDM 4 and higher they moved to a JSON representation of the values, like {"ID":"cn of the group","ID2":"GUID of the group"} which can be parsed in ECMA natively or using some functions in the LibAJC ECMA library. This is known as 'idm4' format in the EntitlementConfiguration object.

This is great, since the first step we take inside the for each loop is to get the groupId variable set with the value of the ECMA function: es:getEntParamField($current-node,'ID')

This function is fun if you go look at its definition in the LibAJC ECMA object. Basically it looks to see if there is JSON and then it tries to get the value of second parameter (ID) from the JSON of the payload.

But it is also designed to try the old way first, just in case, so you can use the same function on legacy version

So why is there more than one value in the entitlement payload? Well it turns out there are really two useful pieces of information often in defining a target object. A name that a human can read, often a DN value, and then the actual value the driver uses under the covers, which is almost always the Association value for that object. In many cases the Association value is a GUID which is very non-human readable. I note the association 'for that object' means that if it is a Group entitlement, it uses the GUID of the Group in the target system.

Next it grabs the Association value (of the current User) into a variable userId, because sometimes you want it easily available for use in XPATH (Though the XPATH is pretty simply, association/text() which can be simplified to just 'association' since the text() is implied.)

Then it does a Query into a node set local variable, groupMemberObj, that is looking at the sys_user_grmember object (aka table. In ServiceNow, everything is a table, and the exposure over SOAP is the same, they are all tables, exposed the same way. What it all means is much more complicated.). The match criteria is that the 'group' attribute (aka field/column) should match the GUID from the entitlement and the 'user' attribute that matches the GUID from the current user objects association value. It is important that this is stored into a node set variable, so the next step works.

Then it loops over the returned values which should really be just the one or none, but by looping over it, if there are none, nothing happens, if one, you do it once. This is the old way of implementing an IF-THEN style block in IDM before the token IF was added to DirXML Script.

Inside the loop it deletes the object (aka row) using the association of that row that came in the query results.

I think what is happening here is that you need to understand ServiceNow under the covers. Earlier I said everything is a table. Here is an example. There is a table of groups. There is a table of Users. How do you make the user a member of a group? In ServiceNow, you add an entry in the sys_user_grmember that links the two. But since this driver maps tables as object classes, and rows as objects, with columns as attributes, to remove them you delete the row (object) in the destination.

You can see this in IDM a little bit, when you look at how a Role and Resource are linked. The nrfResource and nrfRole objects are stored in eDirectory. There are a couple of models for linking them.

The traditional eDirectory way might be to have an nrfParentRole attribute on the Resource, with the DN of the Role, stored on the nrfResource object. Then a reciprocal attribute nrfChildResource on the nrfRole object pointing back at the Resource object. This is how Group Membership/Security Equals works.

But ServiceNow does not do it this way.

You could do it the way that User App does the Level 10, 20, 30 parent/child relationship mapping, where there is an nrfChildRoles and nrfParentRoles and a Level 10 can only have a nrfParentsRoles, and Level 30 can only have nrfChildRoles, but Level 20 could have either. This is an attempt to force you into the three level model and not expand it on your own to more levels (I think), since the more obvious approach would have been to use the Group membership model and it could be turtles all the way down, if you so desired.

But ServiceNow does not do it this way either.

The way User App actually does Role to Resource mapping is by creating a third object, the nrfResourceAssociation object, and it has an nrfResource and nrfRole attribute that holds the linkage between the two objects.

This is how ServiceNow does it.

Now I admit, I am lightly disparaging this approach, since I like directory services and how it handles this sort of thing in general and dislike somewhat how databases often do this sort of thing.

To be fair, using the third object to link them has an additional value, which is to add additional information about the connection. For example, in the case of role and resource linkages, you could add time related information, or who requested the link and why. You see this in the use of the nrfRequest object that requests adding a Role to a user.

Thus the first loop removes the group membership in the target system based on the removed entitlement value. If there are no removed entitlements in the event document then it loops zero times and nothing happens.

The second loop is over the Added Entitlement. Inside the loop, it again gets the groupId from the current-node, the XML of the &ltr;param&gtr; node inside the path.xml component node. (See above example). It calls the same ECMA to get the ID value. Then it sets the current users association into the variable userId.

The next step is clearly a ServiceNow specific thingy. they build a variable dest-dn as:

which would look like /GroupGUID$UserGuid a very messy string, but it looks like how ServiceNow represents a group membership in the sys_user_grpmember table.

Then three writes are done.

  1. sys_user_grpmember table adds an object with the dest-dn calculated.

  • The group value is set on the object just created with the groupId value.

  • The user value is set on the object just created with the userId value.

The object is named by the /GroupID$userId value, then there is a group and user column with the values again.

Role entitlement change

Roles are handled almost exactly the same, with the core distinction being that the table that holds the User to Role assignment is named sys_user_has_role instead of the sys_user_grpmember from the previous example.

There are two loops, first over Removed Entitlement and the second over the Added Entitlement. In both loops they behave exactly the same as in the Group case, just some variable names change.

Department entitlement change

This starts the same, checking conditions of the GCV enabling IDM Roles for Departments, a User, a modify event, and the entitlement Department changing.

Then it loops twice, like in the previous examples. But the actions are simpler. For the Removed Entitlement, it writes a destination attribute value of department to the User, but with a specific value of NULL.

I guess that is how you clear the value, and there is really only one Department you can have at one time. (If you look at the Entitlement object for Department you will see that the flag "Allow the entitlement to be assigned multiple times" is NOT checked. Whereas in the Role and Group case it is checked.

The second loop, over Added Entitlement, gets the payload of the entitlement via ECMA from the ID element of the JSON payload, and writes that to department on the User in the User table.

This is interesting, since it is a bit odd to define an attribute value as a Role. You of course can, but the question is why? If so, why not have a Title entitlement as well? Why not any other attribute? If it were stored in a different table, like Roles/Groups that would make sense. But Department seems a bit strange. Unless of course the value of the department value is a GUID or not a simple readable string, and it is a foreign key into some Department table elsewhere in ServiceNow.

That would make sense, as a Code Map Refresh would be able to query for the department values, and when you look at the Department entitlement object, you see that the query is against a table, cmn_department, and returns the association value. Thus suggesting that the answer to my question is that the association value (probably a GUID like value) is needed in the department attribute value.

Ok, that is all for now in this article. Stay tuned for more as we finish working through the Subscriber channel.


How To-Best Practice
Comment List