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: http://wiki.novell.com/index.php/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 of a somewhat complex fashion, then the driver may (will) 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. In the fourth article (SAP HR CMP Integration Driver Walkthrough – Part 4) I got part way through the Publisher Event transform.

So lets continue from where I left off in the Publisher Event Transform.

pub-etp-pp-Relationships

This rule acts like a filter on relationship values, since we have to let them through the regular Identity Manager filter to get them this far, and notify won’t help, because some we want to let through, and others not so we cannot use the regular filter part of the engine to handle this.

First off, load the configValues driver scoped variable if it is not set. This is a largish attribute, and would be wasteful to reload on every operation, so by loading it once per driver restart we optimize quite nicely. (Driver scoped variables hand around for the duration of the drivers running time. Thus load it once, and it hangs about till you stop the driver).

Now they use an interesting token, I only recently heard about, the document token. You can reference an entire document from an object in eDirectory if you want. In fact this entire set local variable is quite interesting:

<do-set-local-variable name="configValues" scope="driver">
	<arg-node-set>
		<token-document>
			<arg-string>
				<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-parse-dn>
				<token-text xml:space="preserve">#</token-text>
				<token-text xml:space="preserve">DirXML-ConfigValues</token-text>
			</arg-string>
		</token-document>
	</arg-node-set>
</do-set-local-variable>

This uses the ParseDN token with a custom DN delimiter. Which is defined in the DTD but I have never actually used myself. ParseDN is shockingly powerful, and is actually exposed via a Java class if you wanted to use it elsewhere (DNConverter I believe is the name).

It is quite a tricky task to be able to convert DN formats, from various different possibilities to all the other possibilities. Then throw in the ability to define a custom delimiter pattern, and it is pretty hard to beat. Regardless, it needs to get the reference into the format that looks something like:
vnd.nds.stream://ACME-TREE/acme/driverset/CMP-SAPHR#DirXML-ConfigValues

The parse DN uses the GCV for the current drivers DN, and taking the value of \ACME-TREE\acme\driverset\CMP-SAPHR and converting the backslashes (\) to forward slashes (/).

Once that is done the document token is requesting the specified attribute (DirXML-ConfigValues) as an entire XML node set which we will later parse with XPATH.

Now all this work is actually so that we can read in the configurations settings that are defined in the driver configuration.

The particular GCV we need is called drv.relationship.filter. which we discussed in part 2 (SAP HR CMP Integration Driver Walkthrough – Part 2) of this series of articles. This is known more commonly as: Filter, under the Object Relationships header. (Remember to set Show Object Relationships Options to “show” instead of “hide” if you are not seeing this value).

The next step is to loop through the values, in a for each loop. The node set is XPATH of:
$configValues//definition[@name=’drv.relationships.filter’]/value/instance

That means the configValues variable ($ means the string following is a variable), // means any occurrence of a definition node, (which is an expensive way, since it has to search the entire document), with the predicate (I like to read that as “where”) where the XML attribute name is drv.relationships.filter, and then find the value node under it, and return the instance node under that.

Now the reason they use the //definition notation, which I commented above is a bit expensive from a pure performance view, is that the XML in the GCV is somewhat more complex than your usual XML. Let me paste in how the particular GCV of interest is formatted. I stripped it down to just the Object Relationships set, to make the example clearer.

<?xml version="1.0" encoding="UTF-8"?>
<configuration-values> 
	<definitions>
		<header display-name="Object Relationships"/>
		<group>
			<definition display-name="Show Object Relationships Options" name="drv.relationships.options.display" type="enum">
				<description>Show future event options?</description>
				<enum-choice display-name="show">show</enum-choice>
				<enum-choice display-name="hide">hide</enum-choice>
				<value>show</value>
			</definition>
			<subordinates active-value="show">
				<group>
					<definition display-name="Discover Relationships" name="drv.relationships.enable" type="enum">
						<description>Discover relationships between objects in the SAP HR data model?</description>
						<enum-choice display-name="Yes">true</enum-choice>
						<enum-choice display-name="No">false</enum-choice>
						<value>true</value>
					</definition>
					<subordinates active-value="true">
						<definition display-name="Filter" name="drv.relationships.filter" type="structured">
							<template>
								<definition display-name="Object Class" name="objectClass" type="string">
									<description>Specify the object class you want to discover relationships for. Class names must be in the identity vault name space.</description>
									<value/>
								</definition>
								<definition display-name="Attributes" item-separator="|" name="attrNames" type="list">
									<description>Add all the relationship attributes you want to be populated. Attribute names must be in the identity vault name space.</description>
									<value/>
								</definition>
							</template>
							<value>
								<instance>
									<definition display-name="Object Class" name="objectClass" type="string">
										<description>Specify the object class you want to discover relationships for. Class names must be in the identity vault name space.</description>
										<value>DirXML-sapO</value>
									</definition>
									<definition display-name="Attributes" item-separator="|" name="attrNames" type="list">
										<description>Add all the relationship attributes you want to be populated. Attribute names must be in the identity vault name space.</description>
										<value>
											<item>DirXML-sapO-R-A-K-011</item>
											<item>DirXML-sapO-R-A-O-002</item>
											<item>DirXML-sapO-R-B-O-002</item>
											<item>DirXML-sapO-R-B-S-003</item>
											<item>DirXML-sapO-R-B-S-012</item>
										</value>
									</definition>
								</instance>
								<instance>
									<definition display-name="Object Class" name="objectClass" type="string">
										<description>Specify the object class you want to discover relationships for. Class names must be in the identity vault name space.</description>
										<value>DirXML-sapS</value>
									</definition>
									<definition display-name="Attributes" item-separator="|" name="attrNames" type="list">
										<description>Add all the relationship attributes you want to be populated. Attribute names must be in the identity vault name space.</description>
										<value>
											<item>DirXML-sapS-R-A-O-003</item>
											<item>DirXML-sapS-R-A-O-012</item>
											<item>DirXML-sapS-R-A-P-008</item>
											<item>DirXML-sapS-R-A-S-002</item>
											<item>DirXML-sapS-R-B-C-007</item>
											<item>DirXML-sapS-R-B-S-002</item>
										</value>
									</definition>
								</instance>
								<instance>
									<definition display-name="Object Class" name="objectClass" type="string">
										<description>Specify the object class you want to discover relationships for. Class names must be in the identity vault name space.</description>
										<value>DirXML-sapC</value>
									</definition>
									<definition display-name="Attributes" item-separator="|" name="attrNames" type="list">
										<description>Add all the relationship attributes you want to be populated. Attribute names must be in the identity vault name space.</description>
										<value>
											<item>DirXML-sapC-R-A-S-007</item>
										</value>
									</definition>
								</instance>
							</value>
						</definition>
					</subordinates>
				</group>
			</subordinates>
		</group>
	</definitions>
</configuration-values>

You can see a number of things. First off, the use of a <header> node is always nice to pretty up and organize your GCV’s. I probably should read through the DTD to see what is valid in the GCV’s because there are all sorts of nice tricks you can play in there and it would be nice to learn more. One day, in my spare time, sure…

Then they <group> a couple of values together, again helps keep it clean. Then the first GCV is the show or hide value, and with the use of <subordinates active-value=”show”> it allows you to show or hide a group of values. Very nice way of folding stuff up out of sight to make things cleaner.

Then we have another sub grouping with subordinates for Discover Relationships. Anyway, I think you get the point. Our GCV selected via explicit XPATH would actually look more like:

$configValues/definitions/group/subordinates/group/subordinates/definition[@name='drv.relationships.filter']/value/instance

I think that would probably select the node we wanted, but I would have to spend more time testing to be sure than it is worth. Thus you can see why in this specific case, the use of double forward slashes (//) makes sense.

Well the node set we are working with, as current-node, in our for each loop, would look something like this:

<instance>
	<definition display-name="Object Class" name="objectClass" type="string">
		<description>Specify the object class you want to discover relationships for. Class names must be in the identity vault name space.</description>
		<value>DirXML-sapO</value>
	</definition>
	<definition display-name="Attributes" item-separator="|" name="attrNames" type="list">
		<description>Add all the relationship attributes you want to be populated. Attribute names must be in the identity vault name space.</description>
		<value>
			<item>DirXML-sapO-R-A-K-011</item>
			<item>DirXML-sapO-R-A-O-002</item>
			<item>DirXML-sapO-R-B-O-002</item>
			<item>DirXML-sapO-R-B-S-003</item>
			<item>DirXML-sapO-R-B-S-012</item>
		</value>
	</definition>
</instance>

Thus the next line in the rule gets the filterInstance variable from the current-node variable. Now this is an interesting choice, as they could just use current-node for this value, right? Well if you look a little further down in the rule, inside this for each loop, there is yet another nested for each loop, which will reset current-node locally, but they need to use this value inside that nested loop, so we need to have stored it before we get into that loop.

Set the variable className for the particular instance of the loop with the XPATH of $filterInstance/definition[@name=’objectClass’]/value which means, in the example code above, would select the definition node, where the definition node has the XML attribute name set to objectClass. Then grab the data in the value node, which in this case is DirXML-sapO.

Then set a flagging attribute, allowClass to false. On each loop, if it is allowed, we will change that to true to work with.

If the current objects class is the same as the object class in the current instance of the filter list, then it is time to process them, otherwise, we can probably move on, as it does not really apply.

If it is the correct object class, then set the allowClass to true, which means we will let it through.

The next IF THEN block tests to see if lvTHeader is true, and it is set to true at the very beginning of this rule, and at the end of this test, is set to false, basically to help us only run through this once, since each object class should only be in the filter a single time.

The THEN block sets up some headers to add to the log file. Using some variables set up earlier for logging.

Next is a for each through the node set of XPATH of node()[starts-with(@attr-name, concat($className, ‘-R-‘))] which looks for any node in the original input document (node() ) where the attr-name XML attribute inside the node starts with the className value and a -R- (i.e. DirXML-sapO-R-). This way you can select the nodes of interest to the filter, since all the relationship attributes are named className-R- by design.

Inside this loop, we set the attrName value with the XPATH $current-node/@attr-name, since our current loop is through a bunch of nodes with attributes. As noted above, this inner loop has reset the local value of current-node, so after we set the allowAttr to false, we are going to loop through the filterInstance node set, which was of course, the previous current-node value.

This loop will run through $filterInstance/definition[@name=’attrName’]/value/item/text() which would be the list of attribute names we pulled from the GCV above. (The one with the <item> modes).

Store the filterAttrName from the current-node value, and then if the current filterAttrName is the same as the current attrName from the original XDS document that we are looping through, set allowAttr to true.

Now we should have allowClass and allowAttr set to either true or false each, so that we know if we should let it through this filter process or not.

If both are true, then we log some more stuff and do nothing else. I.e. This lets it flow through to the next part of the process.

The else case, where either allowClass or allowAttr are not both true, then log the fact an attr was rejected, and use the Strip XPATH verb to delete node()[@attr-name=$attName] which means the current XDS doc, the attributes that have am XML attribute attr-name, and using variable interpolation to replace $attrName with the value of it. Note you do not need quotes around the $attrName in that case.

Finally we finish the Publisher channel Event Transformation rule. Boy that was a lot of work! It will all pay off, once we get a better overview of the entire process though!

That’s about it for this article, stay tuned for part 6 where we start working through the Match, Create, and Placement rules. Oy, is this ever going to end?

0 votes, average: 0.00 out of 50 votes, average: 0.00 out of 50 votes, average: 0.00 out of 50 votes, average: 0.00 out of 50 votes, average: 0.00 out of 5 (0 votes, average: 0.00 out of 5)
You need to be a registered member to rate this post.
Loading...Loading...
Categories: Uncategorized

Disclaimer: As with everything else at NetIQ Cool Solutions, this content is definitely not supported by NetIQ, so Customer Support will not be able to help you if it has any adverse effect on your environment.  It just worked for at least one person, and perhaps it will be useful for you too.  Be sure to test in a non-production environment.

Leave a Reply

No Comments
geoffc
By: geoffc
Apr 15, 2010
11:34 am
Reads:
1,134
Score:
Unrated