Converting Entitlements to Resources, more details


Volker Schueber and John DaSilva wrote this very nice article about how you would convert your Entitlement based drivers to being Resource based drivers.

Behind the scenes the key point is that with Novell's Role Based Provisioning Module (RBPM) 3.7 the model was changed from the previous versions.

Prior to RBPM 3.7 drivers had Entitlements that are really just flags to tell the Identity Manager engine to grant access to something.

Then you would build up Roles that consisted of a bunch of Entitlements. Thus the Financials Role might consist an Active Directory Account Entitlement, an Oracle driver Account Entitlement, a Lotus Notes Account entitlement, and then a Group entitlement in each of those systems as well. So by granting the single Role to a User (Group or Container as well of course) they would get a 3 accounts and a bunch of group memberships.

This is a nice model, but there are some short comings. Or so they tell me. I understand a couple of them, but more than anything else it seems that Novell has plans to use this more in the future for interesting things. The one shortcoming I understand is that the naming of the Entitlement value is not pretty.

With RBPM 3.7 they added another abstraction layer in between Roles and Entitlements, and these are called Resources.

With Resources you get the interesting possibility to have a Resource for a thing not managed by Identity Management system.

A friend commented (Thanks Mike W.) that Entitlements are for computers, and Resources are for people, which sums it up nicely.

Taking a driver that is using Entitlements, and using it with Resources, which is needed for better use with RBPM, is what Volker and John were talking about in their article.

Not all drivers support these out of the box, so this is a real issue for people to deal with.

As usual, the article does a nice job of HOW you convert driver Entitlements to use the Resource model, but they do not tell you HOW it works! I am more interested in how it work at the low level.

I figured there would be real value to pick apart the policy they suggest you add, and explain what it is doing, and if I can divine it, the reason why the policy is doing it.

First off, there is a new object under drivers that is used to store the Resource and Entitlement data. It is called EntitlementConfiguration, right under the driver object.

This is an XML resource, which holds an XML document to define the available entitlements/resources this driver supports.

With the newer driver configurations, the policies John and Volker tell us how to add manage to create this object on every driver restart. Which makes it very confusing, as you will not see the object in your Designer project, if you have never started the driver and imported it back in. I.e. If you are playing in a sandbox, where you are working offline in Designer you will not have this object.

Once we know that the rest is pretty easy.

Basically the instructions are add a couple of Global Configuration Values (GCV), add a policy object in the Input Transform rule set, and a Mapping table to handle multi language support.

The GCVs you need to add are basically control attributes to allow Novell to include the same basic rule in every driver configuration and let the end user control it as a driver level setting. If you have read some of my other articles you will know I am a fan of GCV's and this is one of the better uses.

You can read more of my thoughts on GCV's in these articles specifically, and in lots of my other articles buried in the content:

If the first condition test is if global configuration value, then that is a very fast test and the performance cost is very low. Thus I think this is really nice approach. My polydactyl cat says 4 thumbs up! (Cats with thumbs are one of my personal nightmares! The only thing keeping cats from ruling the world is lack of opposable thumbs and cat nip!)

Then you need a localization table since RBPM is a multi language product approach, so you could have 7 languages supported, so you need a way to provide that.

Finally you need to include a policy to be used in the Input Transform of your driver to delete and recreate the data in the EntitlementConfiguration object that sits underneath the driver.

Lets walk through that set of rules, since it will show us much of what we need to understand.

There are 6 rules, and the names help understand the purpose a little bit. I still wish we had more comments in the configuration, and if you feel like doing this sort of exercise, please let me know, or update the Wiki yourself where I am trying to track these sort of articles:

The 6 rules are:

  1. Make sure we only run once and when we're ready

Make sure we only run once and when we're ready:

They use Driver scoped local variables, which is objectClass does not have a value (match . is a regular expression test which is true with any values) and if entConfigInitialized equals true. If both are true, no need to do any work so break. This way the rule only fires once. The last rule, Ran once, set flag so we don't run again is where the entConfigInitialized is set true.

Check whether the driver had been fully initialized:

This is important, since this will fire during driver startup as events go through the driver shim as part of the startup process, and they use the objectClass local variable (Driver scoped, so it hangs around till the driver is restarted) to test if the driver is ready to roll, by setting it to a Destination attribute, Object Class of the driver object. This query will get queued up, after the rest of driver startup events get processed, and will not complete until the driver starts up.

Delete Entitlement Configuration resource object if both role and resource mapping are disabled:

Here we check the two GCV's:



If both are not true, then we do not have any entitlements, so lets get rid of the object entirely.

Get some info from the EntitlementConfiguration object, into a nodeset variable entConfigResObj by Querying for the DriverDN\EntitlementConfiguration object, where the DirXML-ContentType equals "text/vnd.novell.idm.entitlementConfiguration xml". I am not sure what the significance of this match attribute is specifically, since the next step is to test that the nodeset returned -dn value exists. I would normally have pulled back Object Class since every object has Object Class defined, if all you want to do is verify its existence. Maybe they want to be sure to only delete their version of this file, and a normal hand created version will not have the DirXML-ContentType set exactly to this value?

Then via XPATH test if "string-length($entConfigResObj/@src-dn)>0" which is an easy way to test that -dn of that returned result has more than 0 characters (aka, got a value back). Of course, a node test of $entConfigResObj/@src-dn should have worked as well, I wonder why they chose this specific test.

If they get a good DN, then trace a message and delete the object specified.

If not, the trace a message that the object does not exist.


Now for the real heart of this policy. Lets build the XML for the EntitlementsConfiguration object into a variable called xml. Thus you will see lots of $xml references coming up.

First thing to do when building a nodeset variable that is XML text is to XML Parse() the opening node. Which is:

<?xml version="1.0" encoding="UTF-8"?><entitlement-configuration><entitlements/></entitlement-configuration>

What this does is initialize the variable. You get an error if you do not do this.

Next is to add an XML attribute modified to the entitlement-configuration node with the current timestamp value, in yyyMMddhhmmss format.

Next we need to read in all the Entitlement objects (Object Class = DirXML-Entitlement) into entitlements variable to loop through.

For each value in the entitlements nodeset, we loop.

First we get the entName from the XPATH of: $current-node/attr[@attr-name='CN']/value[1]/text()
That is get the first value of CN (in case it is multivalued) from the current node of the loop.

Then set the locTables variable as a nodeset of objects under the driver set, whose CN begins with L10N* (aka any L10N objects since they are named as L10N_XX where XX is the language code).

I am not sure why this is done inside the loop, since it ought not to change between iterations of the loop.

Next up is an interesting approach to allow the driver to enable Entitlements, one by one. Now I happen to think there is a better way, using either a List GCV or a Structured GCV but lets see how they do it.

Using the fact we can use variable interpolation even in the name of the GCV (!!) they build the GCV name to test if it is true with this string: "drv.rolemapping.$entName$" which means replace the $entName$ with the value we figured out in a previous attribute. So this way we test if each entitlement was enabled by creating a standalone GCV for it by name.

Same test for Resource Mapping.

Seems like this would be easier to add a pair of List GCV's and just enter the names of the Entitlements into the List GCV's. This would be much easier to manage from a GCV perspective.

If we have either an enabled Role or Resource mapping GCV for this entitlement then we trace a message saying we found one, then append an XML node <entitlement>, into the $xml variable, under the <entitlements> node.

On the <entitlement> node we add an XML attr dn, with the LDAP DN, then set role-mapping and resource-mapping XML attrs to the values of the GCV's, using the same trick as above. Which may be why they did it instead of a List GCV. Though you could easily store the state in local variables and use that.

Next we need a <type> node, then a <display-name> node, then looping through the locTables variable (which is the variable of L10N* objects) we are going to build the <value> nodes based on the language.

Inside this loop we get the DN of the current-node of the locTables into locTableDN, the langCode by substring-after L10N_ to get the two letter code. The value in the table by using the Map token against locTableDN we just got, for the source value of ent$entName$DisplayName using the variable replacement trick again then trace those values out for debugging.

Then we add a <value> node with a langCode XML attribute with the langCode value, and then stick some text into the <value langCode="EN"></value> of the variable "value".

Then we are basically done this loop. Repeat for each L10N_* object found.

Had we not had the GCV's both set to true (aka both set to false) then we add an <entitlement dn="cn=ldap,ou=acme,o=com" resource-mapping="false role-mapping="false"> node.

At the end of that you end up with XML document that looks at least something like below:

<entitlement dn="cn=ldap,o=something,dc=com resource-mapping="true" role-mapping="false">
<value langCode="EN">Account</value>
<value langCode="DE">AccountWithAGermanAccent</value>
<entitlement dn="cn=ldap,o=something,dc=com resource-mapping="false" role-mapping="false">

Create or update Entitlement Configuration resource object

In this rule if either role mapping or resource mapping in general are enabled, then we need to write the value in the "xml" attribute back to eDirectory. They get the entConfigResObj the same was as when it was time to delete it 3 rules before. Test again for length is greater than 0 of the DN, then they do something cool!

They read back the current values via the Document token and the Custom ParseDN (Parse DN Custom DN Delimiter Example, Structured Global Configuration Values in IDM) into currentXml variable. Then currentXmlStr is the string version (via XML-Serialize token) but with all whitespaces removed. (Replace all of "\s" removes all whitespaces), and xmlStr is the xml attr with whitespace removed as well.

Then compare the two variables, since they should be identical now if the same. If they are, trace a message no update needed. If they differ, trace a message, then update the attribute with the Base64 XML-Serialized value of the "xml" attribute. Finally trace "xml" to screen for debugging.

Ran once, set flag so we don't run again

Finally set entConfigInitialized to true, so we do not run again till driver start.

Well that's how it works. Hope this insight is helpful to people! I think you could more effectively do this with a List GCV, which would make it much easier to manage many entitlements on a driver. For example if you had 10 entitlements on a driver (not a totally ridiculous possibility) you would need 22 GCV's!

Two to enable role mapping and resource mapping in general, then 2 for each Entitlement by name.

Whereas you could have defined the two GCV's as List and if there are no values, same as the general enabler being false. If there are 1 or more values, you can test the current value in the looping through the Entitlement list against the GCV to see if it is enabled or not.

Might be fun to re-write it that way, but that's for another day



How To-Best Practice
Comment List