Novell Identity Manager has a number of prebuilt drivers available. There are two parts to any particular driver. There is the driver shim, the code that executes the actual commands on the target system, and then there is the driver configuration, which defines how Identity Manager will handle the data. There is very interesting divide between these two components.
Driver shims are usually compiled code and we do not get much influence upon them, with some notable exceptions. The Omnibond drivers for Mainframes, Midrange, Linux/Unix, and Scripting actually run a shim as a series of native scripts that are modifiable. However these are the exceptions not the rule.
The driver configuration is where the data from the shim (or Identity Vault) is processed and changes made as needed.
The shim can be the same between two configurations but the driver config can totally change the behavior and approach. The SAP Human Resources (HR) driver is a great example. There are two versions of this driver currently shipping. There is the standard version that is pretty much the same since Identity Manager 3.0 and it works at a pretty basic level. Then there is the SAP HR driver that is part of the Compliance Management Platform (CMP). You would not expect too great a difference between the two right? Well you could not be more wrong! The SAP HR driver in the CMP is a total re-write with a new approach and some really interesting ideas.
I walked through the SAP HR CMP driver in this series of articles:
- The SAP Driver Family for IDM
- SAP HR CMP Integration Driver
- SAP HR CMP Integration Driver Walkthrough – Part 1
- SAP HR CMP Integration Driver Walkthrough – Part 2
- SAP HR CMP Integration Driver Walkthrough – Part 3
- SAP HR CMP Integration Driver Walkthrough – Part 4
- SAP HR CMP Integration Driver Walkthrough – Part 5
- SAP HR CMP Integration Driver Walkthrough – Part 6
- SAP HR CMP Integration Driver Walkthrough – Part 7
- SAP HR CMP Integration Driver Walkthrough – Part 8
- SAP HR CMP Integration Driver Walkthrough – Part 9
With the SAP HR driver there are two primary flaws in the simple configuration that the CMP version is trying to resolve. They are future dated events, and the SAP Organizational structure.
The SAP HR driver in the CMP model is really broken into two parts, the HR driver itself, and the SAP Business Logic driver.
In part one of this series (
SAP Business Logic IDM Driver Walkthrough – Part 1) I started discussing the two flaws, and what the purpose of the SAP Business Logic driver was.
In part two of this series (SAP Business Logic IDM Driver Walkthrough – Part 2), I discussed the configuration values that control this driver.
In part three of this series, I will start to walk through the actual policy in the Subscriber Event Transform.
First up the Subscriber Event Transform set of rules:
Lets walk through each Policy object one at a time. This works best if you open up Designer, and follow along in the policy objects.
First the GCV that controls logging, drv.proclog.enabled is checked, if not move on. Then the lib-LogFileSpec-V1 policy object is included.
This policy basically sets up some variables like lvToday with todays date, if the GCV that controls the use of a single or daily log file is set (drv.proclog.daily.logfile).
The log file path is then calculated with the XPATH of concat(‘~drv.proclog.logdirectory~’,$lvToday,es:getSuffix(‘~dirxml.auto.driverdn~’,'\’),’-~drv.proclog.logfile~’)
That basically takes the drv.proclog.logdirectory value, appends the driver name (es:GetSuffix function) then a dash followed by the drv.proclog.logfile GCV value.
Thus you get a dated logfile, in the right directory, with the driver name in it. This could have been done with tokens, instead of ECMA functions. The ECMA functions are ports of the Advanced Java Class that Novell Consulting uses and are available in the lib-AJC ECMA object in the Library at the root of the driver set.
For example, ParseDN would have been fine to get the driver name, but that would have meant storing it as a local variable to use in the single line XPATH statement. Just as the Time token could have been used to set lvToday.
This is set as a driver scoped variable so it is available to other policies within the driver.
Then lvTmp is used to write to the log file, first I think a blank value (concat(”,”) ) then next the XPATH of:
Which should be the time and date, a comma, what channel we are in (the fromNds variable is true if in the Subscriber channel and false in the Publisher channel), an open bracket, the event-id from the current operation, a close bracket, the src-dn of the operation, and then a space.
This is a basic logging rule that these drivers use all over the place. The Display Incoming events rule then builds up a string that says:
SAP Business Operation (operation name) detected.
Object Class: (class name)
ObjectID: (Objects source name)
then for each add-attr node in the document it will log out the attribute and the value. Then another loop in case it is a modify event, since the nodes in the document looks different on an add event versus a modify event.
I really want to get my hands on the full output from one these drivers to do a separate article explaining why it is valuable. If you are running any drivers using this, I would love a sample log output for a couple of events!
This checks to see if operation was not caused by a Job event (that is the event type of trigger) and there is no association value, add an association value as the object GUID on the Source object. That is write it back into eDirectory. Also, since some of the objects that will flow through this driver may not make it all the way through the driver, for it to add an association in the normal fashion, it adds it here.
Same conditions as previous rule, but this time if we made it this far, cause a Status event with level = “retry” so that we basically sit and wait (retying every X seconds until we get past here. The X value is set in the Engine Control Variables).
First rule uses a GCV drv.bl.hr.enable to decide if this should fire or not. We are going to see this rule repeat across many of these rules, so I will probably not report on it anymore.
Next if it is a user, and does not yet have the DirXML-EmployeeAux class added, then add the auxiliary class.
This rule is triggered by a Job, (operation of trigger) whose source name is dm-maintenence. If that event comes through set the local variable potRootOrgs to the query for all objects of class DirXML-sapO (Organizations from SAP) in the container they are stored, which is the sap\om\o container under the SAP HR driver. This is why we needed that GCV in the Business Logic driver for the DN of the SAP HR driver, and the converge is needed so the SAP HR driver knows the DN of the driver to store the Work Order objects underneath.
We are looking for any objects in the sap\om\o container that has the attribute: DirXML-sapO-R-B-O-002 populated. (With value of *). This is a RELATIONSHIP value from the SAP HR system, stored as of the last event on that object, where the Org has a B0002 relationship to the value in the attribute.
Quick review of how to parse these object classes. DirXML-sap is the base. The next character, O, in this case is the SAP object class (Organization in this case), -R means it is a relationship related attribute, then -A or -B to know if it is a forward or reverse relationship. -A is forward I think, and means that this object refers to that object. A -B means that object is referencing this object. If that makes any sense.
Then there are a stack of relationship types. The ones we usually care about are:
RELAT A relation B relation
002 – Parent Organization / Child Organization
003 – Position Belongs to Organization / Organization has these positions
008 – Holder of a Position / Position is held by
012 – Manager of positions in this Organization / Organizations managed by
209 – Links to the Central Person number
The last one, 209 I only recently found out about, and is generally not used in the driver, but is interesting.
So in this case, we are looking for the O to O relationship, backwards link (B) for relationship type 002.
Then the query pulls back the DirXML-sapO-R-B-O-002 and DirXML-sapO-R-A-O-002 values. So this should be the references on a organization object above it in the tree hierarchy, and an organization below it in the tree hierarchy.
In the next rule we will use this variable.
Next in “Migrate root Organizations” if the local variable potRootOrgs is available, aka set in the previous rule then proceed.
Now they do something cute. In theory this nodeset has thousands of values for Organization units, that have Organizations reporting to them. But some (not sure if there can be more than one) will not have any links pointing up the tree. Thus the rule loops through the nodeset of:
which is all values where the number of nodes who attr-name = DirXML-sapO-R-A-O-002 (the link up the reporting tree structure) is zero. Aka top nodes in the tree (trees I suppose if there are really more than one possible).
Very cute. The next test inside the loop is an XPATH way of saying if it is associated. So the XPATH of:
string-length($current-node/association/text())>0 and $current-node/association/@state=’associated’
basically says there should be text in the association value (string-length > 0) and that the XML attribute “state” (@state) = associated. In other words, the object is associated correctly.
This is probably more efficient than the way I might have done it by reading back the DirXML-Associations value for the object in the query and checking that the state is 1, for the SAP HR driver dn (which we have in a GCV already) and you could do in XPATH as well (Using XPATH to examine Association values).
If it is associated, then first, add a destination attribute migrate, on the DirXML-sapO object class, for the object referenced by the Association (GUID is the association value) with the value of the objects DN.
At first glance this seems odd, as the way they add this association doesn’t seem to do much by itself, and they send it into the destination direction, which seems odd, and the attribute is migrate, which again seems odd.
Thus we need to jump ahead and look at the Publisher Event Transform to see if maybe something happens there. Looking ahead I see a policy object called pub-etp-DMMaintMigrate that sees this event, and changes the DirXML-Association state from 1 to 4 (stored in the nameSpace component of the DirXML-Association attribute. Structured attribute syntax are great, but a little tricky to deal with).
Then more logging stuff which is traced out, and if the GCV allows it, logged to the log file.
Rinse and repeat in the next two rules for Positions. Almost identical process, the main difference is the attribute on the S (Position) objects is DirXML-sapS-R-A-S-002 since it is an S object not an O object.
Finally, in the Veto Trigger event rule, the specific Job trigger is vetoed, so it ends here.
As always, “Break if feature not turned on”.
Previously we found all the root Organization nodes, now we are looking for children nodes.
In “Find all the child Organizations” on sync events, for the class DirXML-sapO, which is Organizations from SAP, set a local variable from a similar query in the parent case, for DirXML-sapO in the sap\om\o context under the SAP HR driver object but this time look for one with the DirXML-sapO-R-A-O-002 has this objects name and return the GUID. In other words, on any sync event (which would be triggered by the migrate of the parent object in the previous rule perhaps?) of Organization objects from SAP.
In “Migrate child organizations” if the local variable just set is available, then basically do the same as for the Parent organizations rule, and add a migrate attribute so the rule in the Publisher Event transform can force the engine to migrate (and thus generate the <sync> events) the objects.
Then rinse and repeat to “Find all the child Positions”, and then “Migrate child positions”.
Well thats it for this episode. Stay tuned for part 4 as we work through the Subscriber Event Transform rules. There are 26 (!) policy objects to get through. Six down, twenty to go!