Talking about Entitlements – Part 4

geoffc

By: geoffc

September 14, 2011 12:15 pm

Reads: 548

Comments:0

Rating:5.0

This entry is part 4 of 4 in the series Talking about Entitlements

Novell Identity Manager introduced the concept of Entitlements several versions back. In fact, I am not sure when they got added, I imagine around the Identity Manager 3 or 3.5 time frame.

There are three entitlement granting agents available from Novell right now.

  • Roles Based Entitlements (RBE) driver,
  • Roles and Resources driver (part of RBPM 3.7)
  • Workflow (via Grant Entitlement or Role activity)

In Part 1 of this series Talking about Entitlements – Part 1 I talked about Entitlements in general, and the first of those agents, the Roles Based Entitlements driver.

In Part 2 of this series Talking about Entitlements – Part 2 I talked about the Roles and Resources driver, which is part of the Roles Based Provisioning Module (3.7 and 4).

In Part 3 of this series Talking about Entitlements – Part 3 I talked about what it means for a driver to support entitlements.

In this article let’s start digging into what needs to be done and getting an Entitlement built.

Now I usually rail against the doc writers using this soapbox as my forum, and to be fair, I don’t just complain I submit lots and lots of comments on the documentation via the Comment links at the bottom of every page.

However, I also want to be clear that often the docs have good stuff in them and I like to call it out when I find it. This is one of those cases, where there is a bunch of good stuff in the Entitlement documentation, but I still want more (lots more!). Definitely recommend reading it if you are interested in entitlements. It is a pretty short document all things considered.

Entitlement Docs:

What I would like more discussion about would be some inner workings to better understand how the various entitlement agents discussed in the past set of articles interact and work. That is, as discussed in the second article, a bit of detail on how the nrfRequest object is used. How does this differ from using the Entitlements Service driver, and from using a User Application workflow to work on it? The sort of thing I am interested in, and once you get beyond basic usage, the sort of thing needed to take it to that next level.

They even nicely include the DTD for Entitlements which is quite helpful. However, they include it in the raw DTD format:

Whereas I am getting spoiled and am enjoying reading it in a slightly easier format, in the overall DTD documentation:

which has a section specifically about entitlements:

For some reason the 401 version of that doc has a dead link, but the 4.0 version works fine, if you just minorly edit the URL. (By the time this gets published I imagine the 4.01 link should be working, you can just change the 40 in the URL to 401 if you want to check). I am quite happy to see the Global Configuration Variable DTD is there as well, as I nagged quite a bit and commented a lot for these docs.

In addition, which turns out to be really helpful is a section in the Entitlement docs discussing, at least perfunctorily what the Active Directory does to handle Entitlement processing. Now this is really a first in my experience for Novell documentation for Identity Manager, so I am very happy to see it as well. But as usual, I am very hard to satisfy, and I would have preferred more detail, as that would have obviated the need for me to write this series. But I guess it keeps me going, so lemons and lemonade.

Docs on how the AD driver Entitlement policies do their magic:

These are great to have, but as I said I would like more, so let me use this series to try and provide more info. Yet let me waffle one more time and note, that they do provide something I was not aware of, in terms of the example regarding the Group membership handling. This is not something I discussed previously, as I was focusing on the account style entitlement, but that is a good point. The docs note that you have to process the Group Entitlement checking, after the user is created. I.e. During the <add> event, you cannot start adding Group entitlements, if they have them. You need an object created first.

To do this is tricky, if you are not aware of a subtle engine and driver behavior. If you were just looking at <status> events, it is hard to tell when it was a successful user creation, as compared with a successful user modify case.

The subtle thing you need to know is that on a successful object create (Not just User’s, but any object in the filter) the engine and driver will generate an <add-association> event that you can work with in the Input Transform. For a reason I am not entirely clear about it seems to not be processed by the Publisher channel event transform, nor its Command transform policy set. Thus you need to manage it in the Input Transform.

You can see an example of this in later driver configurations for the Active Directory driver, as the example, where the DirXML-ADContext should only be written back to the user in eDirectory after a successful add event. This is handled in the Input transform, in a Policy object called something like:
itp-SubscriberUserAdd (in the Package NOVLADCFG in IDM 4, so the full name there would be NOVLADCFG-itp-SubscriberUserAdd)

This rule watches for if operation is add-association, then it does its work.

Well the entitlement example from the Active Directory driver (NOVLADENTEX-itp-EntitlementsImpl) has a couple of rules to handle it. One for Group entitlements and one for Exchange Mailbox entitlements. The Group membership one has a bit of a twist, as the Group is not named in the entitlement, rather it is identified by its association value, which is really an Active Directory GUID, (aka the Group in AD’s DirXML-Association value.)

The rule calls an ECMA function, getEntParamField() asking for the ID node from the current event, and the ECMA looks something like this, it is actually four functions that are part of the lib-AJC library. If you use Packages in IDM 4 then you will have this, as it is part of common content that most drivers require. If you are running IDM 3.6.1 I suspect you will have an older copy of this ECMA Library object and may not have these four functions.

getEntParamField(param, field)
getEntParamFieldBackwardsCompatible(param, idm4field, legacyfield)
getIDM4EntParamField(jsonstr, field)
getParamValue(paramString, paramName)

Which are defined below:

/** 
 * Get named field form an entitlement parameter string
 * The function tries to determine the parameter format automatically and parse the parameter accordingly
 *
 * @param param      parameter string ( e.g. '{"ID":"b53b65bb0b921548b8830cc698666dc2","ID2":"CN=PamelaGroup,CN=Users,DC=carpathia,DC=qalab,DC=com"}' or 'AG=Group1|LSNAME=S7ECLNT315|FROM=|TO=')
 * @param field      field name
 * @return value     field value
 */
function getEntParamField(param, field)
{
	return getEntParamFieldBackwardsCompatible(param, field, field);
}

/** 
 * Get named field form an entitlement parameter string.
 * The function tries to determine the parameter format automatically and parse the parameter accordingly. 
 * If the field names have changed from the legacy format to the idm4 format, this method allows both field names to be specified.
 *
 * @param param        parameter string ( e.g. '{"ID":"b53b65bb0b921548b8830cc698666dc2","ID2":"CN=PamelaGroup,CN=Users,DC=carpathia,DC=qalab,DC=com"}' or 'AG=Group1|LSNAME=S7ECLNT315|FROM=|TO=')
 * @param idm4field    idm4 format field name
 * @param legacyfield  legacy format field name
 * @return value       field value
 */
function getEntParamFieldBackwardsCompatible(param, idm4field, legacyfield)
{
	param = String((new JString(param)).trim());
	if (param.charAt(0)=='{' && param.charAt(param.length-1)=='}')
	{
		try {
			return getIDM4EntParamField(param, idm4field);
		}
		catch (e) {
			return getParamValue(param, legacyfield);
		}
	}
	else {
		return getParamValue(param, legacyfield);
	}
}

/** 
 * Get named field form a JSON-formatted IDM4 entitlement parameter string
 *
 * @param jsonstr    JSON string ( e.g. {"ID":"b53b65bb0b921548b8830cc698666dc2","ID2":"CN=PamelaGroup,CN=Users,DC=carpathia,DC=qalab,DC=com"} )
 * @param field      field name
 * @return value     field value
 */
function getIDM4EntParamField(jsonstr, field)
{
	var jsonobj = eval('(' + jsonstr + ')');
	return eval("jsonobj." + field);
}

/** 
 * Get named parameter value form a pipe-delimited (|) string
 *
 * @param paramString   parameter string string ( e.g. AG=Group1|LSNAME=S7ECLNT315|FROM=|TO= )
 * @param paramName     parameter name
 * @return value     	parameter value
 */
function getParamValue(paramString, paramName)
{
	try {
		var pairs = String(paramString).split('|');
		for (i in pairs) {
			pair = pairs[i].split('=');
   			if (pair[0].equals(paramName)) {
				return pair[1];
			}
		}
	} catch (e) {
		return String(paramString);
	}
	return paramString;
}

If I understand these correctly, the old function call, getEntParamField() has been replaced since there are now at least two different methods of storing the parameter information in the entitlements path.xml component, which I like to call the payload.

Previously, it looks like the values inside the payload part of the DirXML-EntitlementRef were pipe (|) separated. That is, if there was more than one value (imagine the SAP UM Fan out driver, where you might wish to entitle a user to access to many systems). You might apply one entitlement per system, but it also supports one entitlement with 1 or more values, so that the entitlement could handle multiple values.

There also is a new format, which I am not sure when it was introduced. I am looking at an IDM 4.0 set of rules, but I know (not from reading the IDM 4.01 docs since after scouring them, I have yet to find this directly mentioned, instead I found out in one of the Novell support forums) that the entitlement format changed in IDM 4.01 to allow multiple values. I am still trying to find out what this actually means. Regardless, I infer that at least one aspect of it, is handled by this ECMA function.

Thus when you call getEntParamField() to keep older policies working, it just chains and calls getEntParamFieldBackwardsCompatible which then tries two different approached. If the first and last character of the value passed in are { and } (open and close curly braces) then it will use the JSON version of parsing this data. That is, it will first wrap it in open and close round brackets, and then takes that value and appends a .fieldname and executes (eval()’s) that statement to get the field name out of the JSON

In the other case, it uses the older approach in getParamValue() of splitting it by the pipe sign. Then when it finds the parameter you passed in, it splits it on the equal sign, and returns the value from after the equal sign.

This way both approaches are supported, using the same ECMA function call in the policy, which by updating the underlying ECMA function definition requires no changes for the upgraded system. That is a nice approach to backwards compatibility.

Anyway, once it has pulled out the ID parameter from the <path.xml> node which holds the payload for the entitlement, it will then add a value to the member attribute of the group referenced (The group is referenced by its association value, which is what is stored in the payload in the ID field) by the entitlement.

Then it does an interesting XPATH trick, using this DirXML Script token:

<do-set-xml-attr expression="../modify[last()]/modify-attr[last()]/add-value[last()]/value[last()]" name="association-ref">
	<arg-string>
		<token-xpath expression="./text()"/>
	</arg-string>
</do-set-xml-attr>

This says, add an XML attribute (what we would select with @name or @attr-name in XPATH, something like <add-attr attr-name=”Something”> where attr-name is the XML attribute there), called association-ref. We need to do this, since the rule is in the Input Transform and will not have the DN of the current object resolved in Active Directory by the engine if we turn around and right back to Active Directory. Thus the policy needs to add it in itself. This appears on DN attributes being sent to AD, as an XML attribute named association-ref. Now this is not really the tricky part. It is the XPATH used to select the association value and the XPATH used to specify where to add it.

This requires you to remember what an <add-association> event looks like in XML. The proof is in the pudding, so lets take a look at a sample of such an event document:

<nds ndsversion="8.7" dtdversion="1.1">
 	<source>
 		<product version="3.5.10" asn1id="" build="20100709_120000" instance="\ACME-IDV\Acme\Drivers\IDM\AD-Driver">AD</product>
 		<contact>Novell, Inc.</contact>
 	</source>
 	<output>
 		<add-association dest-dn="\ACME-IDV\Acme\Users\davej" dest-entry-id="39267" event-id="server123#20101119141455#3#1">b003d12492ed61418701277118949f34</add-association>
 		<status level="success" event-id="osg900lnx#20101119141455#3#1"/>
 	</output>
</nds>

Thus to get the association value, you want the text value of the <add-association> node. Now if you are just beginning with XPATH let me remind you that the single most important thing about XPATH in IDM to know is that the context node (like the current directory, if you were browsing the file system from a command line) is the event node. That is usually an <add>, <modify>, <instance>, or the like node. Well <add-association> counts as well, so period (.) in XPATH means the current node, and the text() value of it, gets what you need, thus the XPATH of ./text() is all it takes to get the correct value. Normally in an <add> or <modify> or whatever event you would select the association with an XPATH of association, since under the event node there will be an <association> node which you want to select.

Also, less clear but important to notice that in fact this event document has two events in it. The <add-association> and the <status> event.

The other interesting part of that token is the XPATH that tells it where to add the association-ref XML attribute.

../modify[last()]/modify-attr[last()]/add-value[last()]/value[last()]

This looks confusing, since it keeps using a predicate with last() as the function for everything. But this is necessary. There is a word that describes how XPATH works, but danged if I know what it is, but if you were to specify to append XML attribute to:

../modify/modify-attr

without any of those predicates, you would get the association-ref XML attribute added to every single node that matches modify/modify-attr. This would be way too many.

When you start building XML documents using the append XML token, append XML node, set XML text, etc set of tokens, invariable you run into this issue. You will have a loop that works for a single attribute/node, and everything looks good. Then you feed a longer example document through it and then suddenly you start getting multiple copies of nodes you are trying to add increasing with each run through the loop.

Thus what you really want is to always work with the last node added. While there is a last() function in XPATH, there is alas no first() which might be really helpful. There is a positional notation you could use if it made sense, [position()=2] which can be shortened to [2]. Do be careful using that notation, as it must be a number, so if you use a variable, like a counter in a WHILE loop, remember to use the number() function to make sure it is an integer. Thus you might use modify[1]/modify-attr[number($COUNTER)] but I recommend sticking with the predicate test using last().

As you can see there is lots of interesting stuff going on in the rules that are needed to handle entitlements. More to come in later articles in this series.

VN:D [1.9.22_1171]
Rating: 5.0/5 (1 vote cast)
Talking about Entitlements - Part 4, 5.0 out of 5 based on 1 rating
Series Navigation<< Talking about Entitlements – Part 3

Tags:
Categories: Identity Manager, Technical Solutions

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.

Comment