In the introduction to this series, SAP HR CMP Integration Driver I discussed the plan of working through the SAP HR driver that comes with the Compliance Management Platform (CMP) to give a better understanding of its inner workings.
You can read more about the new family of SAP drivers that were released with Identity Manager 3.6 and the Compliance Management Platform in this article on the SAP family of drivers:
The SAP Driver Family for IDM
As this series develops, I will update the links to the rest of the series here:
The plan is to try and get more articles of this nature, which walk through a default driver configuration, explaining WHAT is going on, and when possible WHY it is done that way, in order to make troubleshooting and modifications easier and safer. If you do not know WHY something is being done, it is often hard to work with it.
There are all sorts of interesting little tidbits scattered throughout the various driver configurations that are of interest, and it would be great to have them all in one location as a reference.
Thus I started this Wiki page: Detailed driver walk through collection to try and pull it all together.
If you have the time, consider looking at a driver configuration you are very familiar with and try writing up a channel (Publisher or Subscriber), a policy set (Say the Publisher Event Transform, or the Subscriber Command Transform, or whatever tickles your fancy), or if you can, the entire driver.
The more we get written, the better it is for everyone. This is also of interest for the older and newer driver configurations, as they change from version to version, and it is important to be able to notice the differences between the two, if we are to ever have a hope of doing a meaningful upgrade.
The hope is to get as much content, even duplicate content, as different perspectives are of interest, together to make it better for everyone.
A quick recap of the SAP HR driver then. The current shipping driver handles the relationships between Organizations, Positions, Jobs, and Persons in SAP's HR module in a somewhat simple fashion, and if your SAP OM (Organizational Management) module is used in any a somewhat complex fashion, then the driver may have problems with it.
This was recognized, and for backwards compatibility reasons, the Novell Identity Manager 3.6 product comes with two different versions of the SAP HR driver. There is the previous version, updated for Identity Manager 3.6 and there is the version called the CMP SAP HR driver.
This second driver is the one under discussion, and it requires a second driver to work hand in hand with, the SAP Business Logic driver.
Lets work through the new CMP SAP HR driver first, then on to the SAP Business Logic.
In the first article ( SAP HR CMP Integration Driver Walkthrough - Part 1) I discussed the driver configuration settings. In the second article (SAP HR CMP Integration Driver Walkthrough - Part 2) I discussed the Global Configuration Values. In the third article (SAP HR CMP Integration Driver Walkthrough - Part 3) I started on the Input transform. and got barely into the Publisher, Event Transform rule set. So lets continue from where I left off.
This is the rule that stores the future events in a Work Order object for later processing. This is controlled by a GCV as to whether future dated events are to be processed at all or not.
This rule loops through each <future-event > value that the previous rule built up. Traces it in bright green to the Dstrace output so you can see it happening. If you are not familiar with reading Dstrace, you really must stop now and read the best article on the topic I have ever seen:
Capturing and Reading Novell Identity Manager Traces
I initially thought this next step was to find out if there is already a Work Order pending for this user, and is so, reuse it. If not, create a new one. However, as I read further, I realized rather the driver is checking for the user object itself existing, in order to decide if it is an add or modify event, before we get to the matching rule.
In looking for an existing user, the driver uses a Java query call of:
query:readObject($destQueryProcessor, substring-before(substring-after($current-node, 'TARGET='), '#'), '', substring-before(substring-after($current-node, 'CLASS='), '#'), '')/@src-dn
Now here is where I am slightly critical of the designers of this rule. This is an example of doing it a 'programming' way, that is not necessarily more efficient, or at least not in a major fashion, than in using the more clearer methods, of DirXML Script.
This could have been done using a Query token, just as easily, and been much more readable to us. It probably would have required using a few local variables which is slightly less efficient and more than one line to accomplish.
This is a an interesting discussion to have. Should the default driver configurations be written for simplicity, or readability. I would argue the latter. In my opinion (which is worth about as much as the electrons it is written in at the moment) I would say it is ok to use the less readable version, so long as there is an explanation in the <comment > node given. This way the same goal is accomplished.
Anyway, this is ultimately saying lets get the -dn from the object returned by the query made for the objects whose association is the part after TARGET= (the substring-after) but before the next # sign (the subtring-before that it is wrapped in). Then an empty value, which would be the DN (you can send EITHER the DN or the association in this query call) and finally of the object class in that is after the CLASS= but before the next # sign.
Then they test to see if they got a value. This test uses a regular expression (you can tell since it says "match" instead of "equal" in the display) compare of . (period then a plus sign) which means any character (.) of any length ( ).
If there is no returned DN then it sets the variable target to the current objects name. Usually that is CN, but it would be based on whatever the naming attribute it.
Set up the attributes needed for a work order from the <future-event > node by picking it apart, as it was designed to be used.
validfrom is the time value from the #FROM= segment.
attName from the #NAME= of segment
assoc from validfrom then # then the Association value which is the PERNUM, then a # sign, then the attr name, which would look something like: 12345678#00001234#Given Name
Set dn to the driver DN followed by the above Assoc value.
Finally add a destination object, a DirXML-WorkOrder, with the DN from the dn value from above.
Now to add some of the needed attributes:
DirXML-woType, SAPFEN (I guess to mean SAP Future EveNt ? As good as any other value).
DirXML-nwoCreationDate with the current time in CTIME format.
DirXML-CreatorName as this drivers DN
DirXML-Other1 as the value of target from above.
DirXML-DueDate is set to the validfrom value (previously converted to CTIME)
DirXML-nwoDeleteDueDate also to the validfrom
DirXML-nwoDeleteOnError to true
CN to the assoc value
DirXML-nwoContent to the entire string of the current value of <future-event > node that we are looping through.
DirXML-nwoStatus to pending
This will make a work order object right under the SAP HR driver (ick. Would have been nicer to make a holding container for it, I would think) that will handle future events, when they are due.
This rule does something I was not expecting, after having just made a DirXML-WorkOrder object, with a destination DN specified, it strips the destination DN off. The rules name seems to explain why, that it is being done so that the add event will go through the matching rule, so as to match an existing object if it already exists. Very interesting approach. I guess, add destination object requires you to specify a DN, so they do it then, but then remove it. Skipping ahead and checking the Match and Placement rules, you can see that the driver looks in the SAP Business Logic drivers sub tree for an object, and if needed, creates it there. If it already exists, will match it and use that existing object.
Except that as I think about it, I wonder... I was thinking this should catch a second change to a future event, in the non-CMP SAP HR driver, this was a real problem, since each future dated event gets stored in an iDOC on the remote loader (or engine) iDOC directory and the driver shim does not really check to see if the same attribute is changing a second time, or has been recalled/canceled.
But since the DN of the object includes the time stamp, that will not work, where just the object and the attribute name might have. The future date is still stored in the DirXML-DueDate attribute, so the information would not be lost.
Or else I am missing some step here. This is where it would be nice to have better docs!
Next step is to remove any empty nodes and the <future-event > nodes to clean the document back up, if this is NOT a DirXML-WorkOrder object.
This is another XSLT style sheet, mostly because the query for RELATIONSHIPS values in the SAP HR needs an extra node that is awkward to do in DirXML Script, but not impossible.
There is some setup XSLT, and then the guts of the style sheet is to match add and modify events for all object types except DirXML-WorkOrder (which is the easy way of saying User, DirXML-sapO, DirXML-sapC, or DirXML-sapS).
As in all the rest of the rules, check the GCV that controls whether relationships stuff is on or not.
Set some variables, class-name and operation, and query for RELATIONSHIPS based on the association value.
Now the driver has to parse the results. If you look in the docs, it offers what a RELATIONSHIP query should return, something like:
<nds dtdversion="1.0" ndsversion="8.5" >
<product build="20080318_1405 " instance="SAP-HR351" version="3.5.2" > DirXML Driver for SAP/HR</product >
<contact > Novell, Inc.</contact >
<instance class-name="RELATIONSHIPS" timestamp="20100108" xmlns:sapshim="http://www.novell.com/dirxml/drivers/SAPShim" >
<association xmlns:query="http://www.novell.com/nxsl/java/com.novell.nds.dirxml.driver.XdsQueryProcessor" > RELATIONSHIPS00004474</association >
<sapshim:policyAttr attr-name="RELATIONSHIPS" >
<value type="structured" >
<component name="ITXNR" > 00000000</component >
<component name="BEGDA" > 20070901</component >
<component name="INFTY" > 1001</component >
<component name="SEQNR" > 000</component >
<component name="PROZT" > 100.00</component >
<component name="ISTAT" > 1</component >
<component name="OTYPE" > P</component >
<component name="RELAT" > 008</component >
<component name="ENDDA" > 20100108</component >
<component name="SCLAS" > S</component >
<component name="PLVAR" > 01</component >
<component name="MANDT" > 400</component >
<component name="UNAME" > KXB9094</component >
<component name="RSIGN" > B</component >
<component name="SOBID" > 50026639</component >
<component name="OBJID" > 00004474</component >
<component name="VARYF" > S 50026639</component >
<component name="AEDTM" > 20100107</component >
Now the style sheet dissects this returned document (the result is in the variable $result) and gets the current time ($now in yyyyMMddhhmmss format) then for each <value > node (since for an Organization object usually are three or more (an RSIGN of A to its parent, a RSIGN of B to its children (one per each child Organization object, and an RSIGN of A, for the Position that is the manager of the Organization), sets some values:
rel-type is the RSIGN which is either A or B.
rel-subtype is the RELAT, which is 0002 (supervisory-class), 0008 (position holder), 0012 (manager) and possibly others.
rel-class is SCLAS which is the class of the referenced object. (S = position, P = person, O=Organization, C = job)
rel-pointer is the SOBID, which is an OBJID value of the referenced object.
begin set to BEGDA, converted to CTIME seconds.
end set to ENDDA converted to CTIME seconds
rel-attr-name which is a string of $class-name-R-$rel-type-$rel-class-$rel-subtype so for this example that would be:
"User-R-B-S-008" since this is a user, with a B008 relationship, telling us the position it is the role occupant of.
If logging is enabled, this rel-attr-name is logged. (Can you imagine how much logging this must generate if enabled? Every attribute, every event, every relationship! Wow!)
Then we do a test to be sure that the relationship is current, that NOW is after BEGIN, and before END, then log a message to trace of a couple of spaces, the rel-attr-name = rel-pointer values then the string (current- > add)
Now we test if this is an add or modify event.
For an add event, ($operation='add') we add an attribute, (<add-attr > node for an attribute named rel-attr-name with the value of the rel-pointer.
If it is a modify we do it with a <modify-attr > node instead.
If the event is in the past ($now is greater than $end) then on adds we do ignore it, but on modify events we do a remove value for it.
For future events ($begin is greater than $now) we just trace it to the screen and do nothing.
This explains some of the schema attributes in use. The naming pattern for the schema name is that used for rel-attr-name as we discussed just above:
rel-attr-name which is a string of $class-name-R-$rel-type-$rel-class-$rel-subtype so for this example that would be: "User-R-B-S-008"
Now I was wondering how this would work, as I do not see any schema attributes named User-R-B-S-008 in my Designer project, but for the other classes, I do see how this works. I am used to working with the pre-CMP SAP HR driver, where the O, C and S objects are Organizational Unit, CommExec, and Organizational Roles. But in this driver, I have to remember that the object classes are actually DirXML-sapO, DirXML-sapC, and DirXML-sapS respectively.
This means a more typical rel-attr-name might look like:
DirXML-sapO-R-A-O-003 for which there is such an attribute in schema.
Now to puzzle out what is going on with the User case... Because there is no such attribute as User-R-B-S-0008 that I can see.
In principle you only need half of the A-B relationship to calculate relationships, but it makes me feel warm and fuzzier approaching it from both directions.
Well that's about it for this segment, stay tuned for part 5!