Walking through the IDM 4 Google Apps Driver - Part 6


With the release of NetIQ Identity Manager 4.0 Support Pack 1 (aka IDM 4.01) some bug fixes, and a few new features were added.

Specifically three partner provided drivers were included, an RSA Driver, a Blackboard driver, and a Google Apps driver.

I discussed some of the new features and bugs fixed in IDM 4.01 in this series of articles:

I have been working on a series of articles walking through driver configurations, policy by policy to try and understand what the driver is doing, under the covers.

You can see more of these walk throughs on a Wiki page I maintain to keep them all together: Detailed driver walk through collection.

For this series of articles I would like to start looking at the Google Apps driver in IDM 4.01, that is provided by Consensus Consulting.

In the first article in the series I worked my way through the Subscriber Event transform and half way through the Matching Policy set.

In the second article in the series I worked my way through the rest of the Matching Policy set.

In the third article in the series I worked my way through the Creation policy set.

In the fourth article in the series I started working through the Placement policy set.

In the fifth article I did not quite finish the Subscriber Command Transform, so in the fifth article I continued to the end of the Command Transform and part way through the Schema Mapping policies.

In this article I would like to finish the Schema mapping policy set and begin to work through the Output Transform policy set.


There are two rules in here, for converting telephone numbers. One for fax numbers and for work phone (which is mapped from Telephone Number).

What is interesting is that they chose to do this conversion here in the Schema Map policy set, instead of in the Output transform where it normally would be done. I wonder why they chose that? Since there is no Query into Google, and really no Publisher channel, so no real need to handle it in the normal fashion which is designed to properly handle query events which will usually run through the output transform but not other policy sets.

Fax numbers in eDirectory are tricky since they have three components. The schema syntax is designed to be useful in the days when it mattered what speed and bit depth your fax machine was. In the end, it did not matter since fax machines all got better in time, but the schema syntax is still there. The difficulty is that the faxBitDepth component defaults to 0, and if you treat a structured attribute value as a string, (Technically treating a nodeset as a string) you get the string values from the nodeset concatenated together. Thus you end up with a trailing 0 on your fax number if you are not careful to strip it out properly.

The way this rule does it, is with an XPATH expression of:
.//*[@attr-name='FaxPhoneNumber' and last()]//value[last()]/component[@name='faxNumber']/text()

That means the current context (period), find any (the //) node (the asterisk, (Asterix is a Gaul, asterisk is shift with the predicate (aka Where, which is indicated by the [] brackets) the XML attribute (the @ sign) attr-name is FaxPhoneNumber, (note we are after the schema map has executed, so in the application name space) and its the last() instance, since this attribute can be multi valued. This is an interesting decision to only take the last one that might be in the document, and then underneath that, find any (the // again) value node whose the last() one, and then inside the value node, we need to get the faxNumber component, and then its text() value.

I suspect this could have been done more simply with just a reformat operational attribute token, assuming you wanted to fix all the values. There is a special local variable the engine makes available for this token, current-value, so that you get an implicit for-each loop through all the values. It's actually a bit XSLT'ish in that way. Anyway you could do something like:
<do-reformat-op-attr name="FaxNumber">
<arg-value type="string">
<token-xpath expression="$current-value/component[@name='faxNumber']/text()"/>

By doing it this way, you rely on the magic of the reformat operational attribute token which knows how to handle all the various types of event documents (modify, add, instance, etc) and present just the attribute value nodes. Where this would not accomplish their task is that it would do this for every value in the document, so if three fax numbers were coming three it would fix each value up.

The only reason it is worth commenting is that classical XPATH folk will shudder when they see the use of double backslashes as they are considered expensive in terms of computing power. Thus my way does not need two instances of double backslash. But if FaxNumber is single valued in Google, then you might have an issue.

Usually in cases like this we would additionally flatten the data into perhaps a comma delimited form, and then just build a string of all the fax numbers, one after another. I do wonder at this design choice.

Work number is handled pretty much the same, but with a much simpler XPATH since it is not structured, and thus the only need is to take the last value, instead of doing both flattening and taking the last value.
.//*[@attr-name='WorkPhoneNumber' and last()]//value[last()]/text()

Same basic approach, find the node with the right attr-name, and then select the last instance of that attribute in the event document, since you could have two semantically identical XDS (IDM's XML DTD language name) documents, with one <modify-attr> node for FaxNumber with three <value> nodes, or you could have three <modify-attr> nodes, each with one <value> node each. The end result is the same, but an important distinction to watch out for, which this XPATH does. That is why the first instance of the last() function is used. That covers the case of three <modify-attr> nodes with one <value> node each. Then the second time last() is used, is for the case of one <modify-attr> node with three <value> nodes underneath it, in which case we get the last of the set.

Other than that, not much else to see here in the Schema Map policy set.

Next up is the Output Transformation policy set. There are four policy objects in the Policy set.

  • NOVLATKBASE-otp-Subscribe

  • NOVLGGLEGRPS-otp-EntitlementsImpl

  • NOVLGGLEUSER-otp-EntitlementsImpl

  • NOVLGGLEUSER-sub-otp_HandleDeleteEvents

Let's start working through these Policy objects then, shall we.


First up is a policy from a Novell provided package, Account Tracking. It has its own GCV's to control it, and they are in their own GCV object. Account tracking is used to provide the DirXML-Accounts object that Sentinel uses for Identity Injection when enabled.

There are several policy objects involved, this one in the Output transform, and two more in the Input Transformation.

For the most part the Output transform is used to add operation property data to the event and upon success in whatever the operation is, used to then update the DirXML-Accounts attribute as makes sense.

AccountTracking - Initialize Realm Mapping

There are three conditions, the first two based on GCV's as to whether we are using Account Tracking and if it is in fanout mode. The third condition tests for a variable realmMappingInitilaized not equal to true, so that this only ever has to fire once to initialize the Realm mapping. This is a reasonably hefty initialization rule, so it makes sense to only run it once. One of the last operations in this rule will be to set the realmMappingInitilaized variable to true, as a driver scoped variable. Thus it should only ever run once, per driver start up.

The first thing it does is a test to see if the driver has finished starting up. I ran into this before, where the driver startup events (init-driver) can cause a rule to fire, the problem is that the driver is not finished starting but the rule will process and everything will basically fail as the queries won't work and you end up in a goofy state. This is especially important when you are working with a run once style rule, since at the end, you will have nonsense data set, and the variable that stops it from running again will be set yet. So it won't fire again.

This is easily tested by reading an obvious attribute from an obvious object. I usually do the same as this driver does and read Object Class from the driver object. There is an auto GCV with the driver object DN, so that is obvious, and every object in eDirectory has to have an Object Class (even an Unknown object is actually of object class Unknown) so you know you should always get a response with some value, if the driver is ready.

Then there is a IF test for a regular expression match of . in the objectClass variable. I probably would have had done a break if there is no match, but six of one, half a dozen of the other, Makes no real difference.

Assuming the driver is ready, we go into the if then logic, and trace out a message that Account Tracking: initializing realm mapping.

Another driver scoped variable is checked, shimConfigInfo, again with the intent of checking so that we only do the work once, if needed, since this is reading back a reasonably large bit of data.

Now the way they read back the configuration settings into this variable, looks very familiar. I am not sure which came first, but I saw this in the SAP HR driver, in the CMP (Compliance Management Platform) version (which is also now the base IDM 4 version). There they read back the entire DirXML-ConfigInfo attribute that holds all the GCV settings for the driver, using a similar approach. You can read more in that article at: SAP HR CMP Integration Driver Walkthrough - Part 5

What they do here is use the Document token, which needs a DN in a specific format, and can read back the information.
<do-set-local-variable name="shimConfigInfo" scope="driver">
<token-text xml:space="preserve">vnd.nds.stream:</token-text>
<token-text xml:space="preserve">/</token-text>
<token-parse-dn dest-dn-delims="00,/ =*\" dest-dn-format="custom" src-dn-format="slash">
<token-global-variable name="dirxml.auto.driverdn"/>
<token-text xml:space="preserve">#</token-text>
<token-text xml:space="preserve">DirXML-ShimConfigInfo</token-text>

They build the DN using the ParseDN token with a custom delimiter, which you can read more about understanding in this article: Parse DN Custom DN Delimiter Example

In this case, DirXML-ShimConfigInfo holds the configuration data for the specific driver, that is not available as GCV's, the stuff you would see on the driver configuration tab in iiManageror Designer. This is one of the ways to get access to the information that is normally provided to the driver at startup time. You know, that is an interesting idea. I wonder if you could grab the <init-driver> event into a driver scoped variable at startup to try and catch this stuff earlier. I will have to try that, could be interesting.

I do know from trying to modify the response to the <init-driver> event in my Salesforce.com driver, that you are not able to get at it, via Policy. (I needed to tell the driver to use query-ex, so the engine would behave like the shim supported it. I intended to generate the right events to actually make query-ex work, but if the engine decides at shim startup, based on the data in the response to the init-driver query which says whether the shim supports query-ex. Anyway, I was able to get a build from engineering that tells the shim to lie and say it supports query-ex, knowing that the configuration has to actually make it work).

Anyway, instead of the Document token, you could just as easily read the attribute back from the driver object (Use the same auto GCV used in the sample above) and then I think you just have to base 64 decode and XML Parse it into a nodeset variable. So I guess if you consider building the DN easier then go this way, if you consider nesting tokens easier go that way.

Now that shimConfigInfo is sure to be available, a couple of values are picked out of it using some convoluted XPATH, since the configuration information is stored as XML on the object.
<do-set-local-variable name="primSystemID" scope="policy">
<token-xpath expression="$shimConfigInfo/driver-config/driver-options/configuration-values/definitions/definition[@name='nsap-system-id']/value"/>
<do-set-local-variable name="primSystemNumber" scope="policy">
<token-xpath expression="$shimConfigInfo/driver-config/driver-options/configuration-values/definitions/definition[@name='nsap-cs-type']/value"/>
<do-set-local-variable name="primClientNumber" scope="policy">
<token-xpath expression="$shimConfigInfo/driver-config/driver-options/configuration-values/definitions/definition[@name='nsap-user-client']/value"/>
<do-set-local-variable name="key" scope="policy">
<token-xpath expression="$shimConfigInfo/driver-config/driver-options/configuration-values/definitions/definition[@name='nsap-ls-name']/value"/>
<do-set-local-variable name="realm" scope="policy">
<token-text xml:space="preserve">\</token-text>
<token-local-variable name="primSystemID"/>
<token-text xml:space="preserve">\</token-text>
<token-local-variable name="primSystemNumber"/>
<token-text xml:space="preserve">\</token-text>
<token-local-variable name="primClientNumber"/>

An example of the XPATH used to get the primSystemID value is:

That is, looking at the context of the shimConfigInfo variable ($ means variable), walking the XML tree down until you find a definition node whose XML attribute 'name' is nsap-system-id. The problem is, I cannot find where that info is coming from in the driver configuration. This is very familiar looking to me, but I cannot figure out where in this driver they think this data is set. It should be in the configuration XML but I cannot find it there. I know I have seen these before, and after some searching I found them! These are settings found in the SAP driver only! Novell SAP, nsap, should have seen that one sooner. Anyway, I wonder if the default Account Tracking package has some SAP specific references that are just empty if the system in not SAP.

But basically the rest of this policy does not make sense in this context since none of the configuration values it is looking for are present in this driver.

At the very end the realmMappingInitialized variable is set to true so that this will only fire once per driver startup.

Anyway, that looks a good breaking point. Stay tuned for more in the coming articles in this series.


How To-Best Practice
Comment List