Managing multiple Active Directory domains in one IDM system - Part 2

The Active Directory driver in all versions of Identity Manager from 2.0 and up, when we started using DirXML Script instead of XSLT for policies have had a pair of attributes in common. DirXML-ADContext and DirXML-ADAliasName.

These are used to handle the strange circumstance of a rename or move event in Active Directory. The shim cannot tell the difference between a rename or move event. Both cases are actually a modify of the distinguishedName (DN) attribute. For example: cn=geoffc, ou=Users, dc=acme, dc=com

If this were to change, it would depend where the change occurred to know if this was a move or rename event. If the cn=geoffc part changed, then that is a rename event. If any of the ou=Users, dc=acme, dc=com part changed, then this was a move event, since the parent container for the object is now different, in other words a move event.

You can find the policies that manage this in the Publisher Event Transformation rule set. There have been a number of different shipping default configurations for Identity Manager, so the name of the rule and its exact workings have changed over time. With the advent of IDM 4 and Packages, we should see this stabilize and become more manageable.

The problem with using single valued, simple string attributes for this task is how do you handle a second AD driver in a different domain? Well if the user is only ever in a single domain, that is fine, but if you have worked with IDM then you know that one of the cool things about IDM is how easy it would be to manage users in multiple AD domains, eDirectory, and many other connected services.

If you add a second Active Directory domain to your IDM world, then the values of DirXML-ADAliasName and DirXML-ADContext will be incompatible between drivers and domains.

In the first article in this series Managing multiple Active Directory domains in one IDM system - Part 1 I discussed an approach in general of a plural version of the attributes, using Path syntax.

In this article I plan on discussing more of the specific implementation details of making this work.

With the use of plural attributes DirXML-ADAliasNames and DirXML-ADContexts, and Path syntax, it means that we should not be using the Set Destination or Source attribute tokens, since we might have multiple valid values.

What that means is that every time you would have the set destination attribute value or set source attribute value, first you need to remove the old value for this driver, and then finally add back the correct value.

You can tell when this code has failed, because you will see users with multiple entries for the same driver.

In general, I use the follow code snippet to manage this issue. Lets look at the snippet, and then dissect it to better understand why this works. Lets use the destination case (Publisher channel event):
<token-dest-attr name="DirXML-ADAliasNames"/>
<if-xpath op="true">$current-node/component[@name='volume']=""</if-xpath>
<do-remove-dest-attr-value name="DirXML-ADAliasNames">
<arg-value type="structured">
<arg-component name="nameSpace">
<token-xpath expression='$current-node/component[@name="nameSpace"]'/>
<arg-component name="volume">
<token-xpath expression='$current-node/component[@name="volume"]'/>
<arg-component name="path">
<token-xpath expression='$current-node/component[@name="path"]'/>
<do-add-dest-attr-value name="DirXML-ADAliasNames">
<arg-value type="structured">
<arg-component name="nameSpace">
<token-time format="!CTIME" tz="UTC"/>
<arg-component name="volume">
<token-global-variable name=""/>
<arg-component name="path">
<token-op-attr name="DirXML-ADAliasName"/>

First we startup a loop, over the destination attribute values of DirXML-ADAliasNames (our plural attribute). This will be much easier if you know what the returned value looks like in trace, so let me help out by pasting a sample right here with the output for a user with three such values.
<nds dtdversion="3.5" ndsversion="8.x">
<product version="">DirXML</product>
<contact>Novell, Inc.</contact>
<instance class-name="User" qualified-src-dn="O=idv\OU=Users\CN=23test" src-dn="\ACMEIDV\idv\Users\23test" src-entry-id="230365">
<association state="associated">2co8LIUufEE/p9nKPCyFLg==</association>
<attr attr-name="DirXML-ADAliasNames">
<value timestamp="1282761541#40" type="structured">
<component name="nameSpace">1282761557</component>
<component name="volume">\ACMEIDV\idv\servers\IDMDriverSet\IDV-AD1</component>
<component name="path"></component>
<value timestamp="1282761557#1" type="structured">
<component name="nameSpace">1282761557</component>
<component name="volume">\ACMEIDV\idv\servers\IDMDriverSet\IDV-AD2</component>
<component name="path"></component>
<value timestamp="1282761557#6" type="structured">
<component name="nameSpace">1282761557</component>
<component name="volume">\ACMEIDV\idv\servers\IDMDriverSet\IDV-AD3</component>
<component name="path"></component>
<status level="success"></status>

This is a multivalued attribute, so we get one <instance> node, containing one <attr attr-name="DirXML-ADAliasNames"> node, that has three <value> nodes, that are structured, each with three <component> nodes.

As discussed in the first article, Path syntax has three components as you can see in the above trace quite clearly. We will store a timestamp in the nameSpace node, using CTIME formatted time. (Count of seconds since Jan 1, 1970 at midnight). The volume component will store the DN of the driver object, and the actual string value, which in this contrived example is different for all three domains will be stored in the path component.

Do be careful, in XPATH most things are case sensitive, so note the nameSpace with a capital S in there.

So our for each loop is working through the values returned. Now, we need to only deal with our drivers values, so we use an if XPATH is true test, for the XPATH statement:

$current-node references the current node we are looping through, which when using a destination attribute (or source attribute, operational attribute, or just plain attribute) token is the current value returned in the nodeset.

So we are looping through the <value> nodes, and the $current-node is one of the three <value> nodes, as a nodeset. Since it is a node set that means we can walk through it in XPATH so lets look for the <component name='volume'> node. That is the $current-node/component[@name='volume'] part of the XPATH.

Then lets test to see if the string stored there is the same as the current driver DN. We are using a Global Configuration Value (GCV) called which always returns the current drivers DN in backslash format. This is really convenient since the volume node is storing the driver DN to which it is relevant in backslash format, so the compare will work, if it matches.

Note a subtle point. I used the tilde notation to use the GCV, ( instead of the dollar sign notation ($ while in XPATH. There is a very subtle difference between the two. Well several subtle differences, but the one that rears its head here is the need to enclose it in quotation marks. That is because when using the dollar sign notation, you compare against the variable value, and when you use the tilde notation, the driver does a string search and replace at startup time for any such references. Therefore the driver at startup is replacing the with the literal string \ACMEIDV\idv\servers\IDMDriverSet\IDV-AD3 during startup. and THAT needs to be quote enclosed. Subtle eh?

Why tilde instead of dollar sign? In this case, no real reason. There happen to be some other subtle differences that make it matter in other cases. For example, there is a bug in IDM up to V4 (it is now fixed in V4, which is another funny story) when using the Replace All verb, and passing a regular expression via such a variable. That is, you might want to store some complex matching pattern in a GCV or a local variable and use it in a Replace All token. Well turns out, any 'funny' characters (not sure which exactly count, but usually the special characters that regular expressions would recognize) are replaced with some goofy value, and it just doesn't work.

Turns out they fixed this in IDM 4, which hit me in a very funny fashion as I was working on a IDM 3.6.1 project but wanted to use Designer 4. Well all was going well, till I built a rule doing this, and tested in Simulator and it worked fine. Then I deployed it, and it failed. I recognized the error, and realized that the Simulator in Designer, uses the same JAR files that IDM uses. That is, Designer 4 is using the IDM 4 Java files. Thus testing in Designer 4 showed it working, but failing in the IDM 3.6.1 engine. How annoying!

Upside is, you can validate if a bug of this type was fixed in IDM 4, just by running your test case through Designer.

The other major consequence of how Simulator works is that there was a patch in the IDM engine (idm361engineir2a) for the nxsl.jar file, which is used in parsing XML, usually in the SOAP driver. There was a very common case where hitting a specific XML value would send the JVM into 100% CPU forever and seemingly never return (I only waited an hour or so, I never actually let it go to see if it really was an infinite loop). Well Simulator did the same thing! Same code, same bug. This too is fixed in Designer 4, but is not documented to be a needed fix to Designer 3.5.1 alas. The engineer at Novell told me where to place the new JAR file in Designer to fix it in Designer 3.5.1 but I did not see that information appear in the release notes for the engine patch.

Anyway, this XPATH test, of $current-node/component[@name='volume']="" will be true, when the DirXML-ADAliasNames value we hit in the loop belongs to this driver.

Since we want to update the value, first we need to remove the old one, so when true, we do a remove value, but since this is a structured attribute we need to specify exactly which value, including all three components. Well again, XPATH to the rescue, and the sample used in the test is all you need to do the trick.

We specify the three components by name, and the XPATH is basically something like this:

where NAME is either nameSpace, volume, or path, depending on which of the three you are specifying. Thus you can see in the code sample above, the three repetitions of that XPATH for each component name. This way, we say remove the specific attribute with the values of the current one we selected, because it has the correct driver DN value in the volume component.

If you have never worked with structured attributes before, then you will see that Designer displays it as a curly brace enclosed set of values, to indicate this specific case.

What is nice about this approach is that if you had more than one value for a specific driver, this would loop through, find all of them and clean it up.

Next we actually add a value that we really want in the add destination attribute token. We again use the structured option, in the type of attribute we are setting. (There are of course string, integer, state (boolean), and other types, structured is just not so commonly used).

Set the volume component to the current driver DN of course, using the same GCV as above. The nameSpace as discussed with be the current time in CTIME, using the time token, and the path will be the string that would otherwise have been stored in DirXML-ADAliasName (the singular version).

Basically, you need to use this sample code block, modified appropriately, based on where it is used in each case where a DirXML-ADAliasName or DirXML-ADContext is SET in the driver configuration. Obviously, if you are on the Subscriber channel, these would be source attribute tokens instead of destination attribute tokens. I personally think a "Channel Changer" option for the source and destination attribute tokens would be cool, if Designer could add it to the user interface. I very often find I copy and paste content and borrowed it from the wrong channel.

This is usually easier to fix in the XML view in Designer, since I usually just change the <do-set-src-attr> node to <do-set-dest-attr> and of course the close node as well </do-set-src-attr> to </do-set-dest-attr> or else it is invalid XML. But I think this is a reasonable use case to provide a UI widget to do it for us. Submitted it as an enhancement request via the RMS portal, lets see if one day it gets added. I have about 35 such enhancements requested, only 2 so far have been implemented. But it takes time for stuff to get added, alas.

If you know that the add attribute value (destination or source) is going to be the first time, you can skip the for each loop part of the code, however, I would probably still leave it in, just to clean up in case you get bad data in there somehow. It adds a little bit of overhead, but is probably worth it just in case.

Stay tuned for Part 3 of this series, when I will discuss some of the other twists that will need to be dealt with!


How To-Best Practice
Comment List