Application Delivery Management
Application Modernization & Connectivity
CyberRes by OpenText
IT Operations Management
This is the fourth in a poorly named trilogy of articles (anyone get the Douglas Adams reference there? He started writing a trilogy that ended up being many more than 3 books long. Douglas Adams jokes are always funny darn it!), about a different approach than usual to rules in Identity Manager.
You can catch up by looking at the first three parts in these articles:
My hope is by walking through this process, it shows how I think about Identity Manager rules, and that the process of taking a simple concept (started in part 1) and adding tweaks, features, nice to haves, and so on let you see how many different things you can do and the power of the system! The final rule we came up with is attached as an XML file to Part 3 of the series. Alas, the attribute names I used are mostly nonsense so you would probably have to adapt it to match your tree anyway.
Also, I hope this will be a nice guide for a beginner just getting started to see how things can be done in Identity Manager. There are a couple of other articles that I think are good for beginners to both understand how Identity Manager works, but that also dissect functionality of default drivers, in the following articles:
The first two, how to GCV-ize a driver, takes the Active Directory driver, and modifies the default configuration that ships with IDM 3.5, and replaces all hard coded references to the container in Active Directory or eDirectory for user placement with Global Configuration Variables. Thus when you need to change the placement of users, you can change one value, instead of hunting down a dozen different instances where the string is used.
The third rule walks through the Command transform rules for Password handling that all drivers have as part of the default configuration. (As it happens I used the trace from an Identity manager 3.01 Lotus Notes driver in that one, but it is basically the same for most drivers). That is a great exercise for a beginner in Identity Manager, because it explains what the heck is going on with Password events in IDM. (Sometimes there is a <password> node, sometimes there is a <modify-attr ="nspmDistributionPassword> and yet depending where you are in the rules, one comes from the other!).
Having said that I need to also clarify the perspective from which this series of articles is approaching the task.
A normal Identity Manager rule is event driven. That is actually one of the key differentiators between Novell's product and its competitors. As something happens, we react and do something as a consequence, almost live. Other systems batch it up and wait till the evening, or some other model. While both can work, there is real power in processing things as they happen.
However, sometimes, you need to do things differently. For this series of rules, I am NOT taking an event driven approach (other than to trigger the rule) rather I am deciding to do some specific work, just using Policy Builder and DirXML Script as my toolkit.
This is like the difference between talking in the past tense or present tense in how you process the rules. In a standard IDM event, it is all present tense, where things like operational attributes, current operation, etc are available. The current event is all you are focused on. In this style of rule it is more holistic, and you are able to see what you asked for at the beginning, in the query that starts it all.
The example in use so far was a fairly simple case where we got a DirectorEID value from the HR system, but it was the Employee ID of the director, and we need to make sure every user has a DirectorDN with the Distinguished Name of the actual Director object in the tree.
Thus the action of the rule, was to query for all users, and their DirectorEID value, walk through that list, and figure out the DN of their Director by querying for the User whose EmployeeID match the Director value. Then we either report on those that are incorrect, or actually fix them.
Basically the heart of the rule is this code block:
<do-if>
<arg-conditions>
<and>
<if-xpath op="not-true">$current-node/attr[@name="DirectorDN"]/value/text()=$DIR-DN</if-xpath>
</and>
</arg-conditions>
<arg-actions>
<do-if>
<arg-conditions>
<and>
<if-op-attr mode="nocase" name="SomeTriggerAttribute" op="changing-to">43</if-op-attr>
</and>
</arg-conditions>
<arg-actions>
<do-set-src-attr-value name="DirectorDN">
<arg-dn>
<token-local-variable name="CURR-USER"/>
</arg-dn>
<arg-value>
<token-local-variable name="DIR-DN"/>
</arg-value>
</do-set-src-attr-value>
</arg-actions>
<arg-actions/>
</do-if>
<do-set-local-variable name="MESSAGE" notrace="true" scope="policy">
<arg-string>
<token-local-variable name="MESSAGE"/>
<token-text xml:space="preserve">
</token-text>
<token-text xml:space="preserve">User </token-text>
<token-local-variable name="CURR-USER"/>
<token-text xml:space="preserve"> had a DirectorDN of </token-text>
<token-xpath expression='$current-node/attr[@attr-name="DirectorDN"]/value/text()'/>
<token-text xml:space="preserve"> and we changed it to: </token-text>
<token-local-variable name="DIR-DN"/>
</arg-string>
</do-set-local-variable>
<do-set-local-variable name="DIR-WRONG-COUNT" scope="policy">
<arg-string>
<token-xpath expression="number($DIR-WRONG-COUNT) 1"/>
</arg-string>
</do-set-local-variable>
</arg-actions>
<arg-actions>
<do-set-local-variable name="DIR-CORRECT-COUNT" scope="policy">
<arg-string>
<token-xpath expression="number($DIR-CORRECT-COUNT) 1"/>
</arg-string>
</do-set-local-variable>
</arg-actions>
</do-if>
We check if the value for DirectorDN is the same as the current DirectorDN of the user, and if we are in the modify triggered case (SomeTriggerAttribute changing to 43) then we fix it. If not, we just write to a MESSAGE variable that this user has bad information. As we embellished in part 3 of this series, we also count how many of each case we run into (good data on User, bad data on User, and not found cases).
One interesting attribute that we could start to fool around with is ACL, or Access Control List. This is how eDirectory manages trustees and rights within eDirectory between objects.
For example, the Admin account in your tree, has S (Supervisor) rights that are I (inheritable) to the [Root] object of the tree. Thus Admin has rights to the entire tree. Lothar Haegar published a very nice article on the topic of understanding ACL values, that you can read at: Setting eDirectory ACL Entries with IDM
ACL is a three part structured attribute, that I talk about how you might use XPATH to get at each component... In hindsight, I should have mentioned that there if a test for attributes, source attributes, or destination attributes of type structured, where you can test if a specific component has a value, thus obviating the need for any complex XPATH. Here is a sample of how that might look:
<if-src-attr mode="structured" name="ACL" op="equal">
<component name="protectedName">$VALUE$</component>
</if-src-attr>
The above test is true when a source attribute named ACL has a protectedName component equal to the value in my local variable VALUE.
So you could easily replace the test in my 'fix it' code block of:
<if-xpath op="not-true">$current-node/attr[@name="DirectorDN"]/value/text()=$DIR-DN</if-xpath>
<if-src-attr mode="structured" name="ACL" op="equal">
<component name="protectedName">$VALUE$</component>
</if-src-attr>
for example to know if there is a specific trustee on some object to a specific attribute in the tree. I dunno, say ACL itself! That would be useful to report on, don't you think? Who or what in the tree has explicit direct rights granted to an object to manipulate the ACL attribute?
Now you can see how you can start detecting that case. Deciding if that is a harmless or bad thing depends on another component, the privileges component. It depends on how 'much' rights that ACL is granting. If the user just gets C (Compare) rights, that seems a smidgen odd, but is not really a rights leakage problem. I would wonder why they have it, but probably would not really care that much.
Whereas if they had W (Write), A (add self), or S (Supervisor) rights, I would start to want to be concerned. In fact, what I really want to say is, what if they have W or higher rights! The value is stored as a bitmask, so I would need to do a it bitmask compare.
Alas there is no built in function in DirXML Script to do Binary compares. However with Identity Manager 3.5 and higher, you can now call ECMA Script functions (ECMA Script is now the official fancy name for JavaScript as we know and love it from the browser world).
I ran into this problem, and Father Ramon gave me the answer in the forums, so thanks again Father Ramon!!!
Here is an ECMA Script function that does a bittest and returns true or false, if the tested value contains the bit specified.
/** Perform a binary compare for a bitmask
* @param {int} value binary value, as a decimal representation
* @param {int} bits binary value, as a decimal representation
* @type int
* @return boolean true or false if the tested VALUE contains the BIT specified.
*/
function bittest(value, bits)
{
return ((value & bits) == bits);
}
This is great, because now you have at least one type of bitwise compare available to use. You need to create an ECMA Script object somewhere in the tree. I personally use a Library object at the root of the Driver set, then I create an ECMA Function object underneath the Library and put all my functions in that one location. Makes it easier to find and edit.
Next you would need to edit the driver you are actually using this function in, in Designer, Driver Configuration, there is an ECMA Script tab. You would have to select the object that holds all your functions, to 'link' it into your driver. If you do not do this, the driver will fail to start (see this article for more examples of things that cause a driver to fail to start: Changes that do not require a driver restart, and errors on starting an Identity Manager driver ) with an error about the XPATH of es:bittest() in some way.
Once you have done that, you can call it via XPATH as es:FUNCTIONNAME(ARG1,ARG2,ARG3) , which is our case is es:bittest($ACL-VALUE,8) to see if the value in ACL-VALUE variable contains the Add Self bit. (See Lothar's article, Setting eDirectory ACL Entries with IDM for a nice table of what each bit value means.)
This way we can now read back all ACL's from all objects in the tree, if we so desired (or scope it down to a subset in our Query token) and start looping through them and report back any ACL's that concern us...
Once we start looping through it and reporting, we can of course start doing something about it as well. Maybe flagging every user that has certain rights with an attribute like acmeSuperUser flag, which allows us to store the data in the tree, for easy LDAP querying later.
To start with, lets start with the nice fleshed out rule from Part 3, keeping all the good stuff we spent time building in part 2 and 3, (the memory tracking, object counting, email reporting, different conditions for reporting and fixing, etc) and just modify it a little bit. You will find the completed rule attached at the end of the next article if you want to see the end results in Designer. (You will have to do the ECMA Script import yourself from this article though, as it depends where you want to put it. In the next couple of articles in this series we will talk about some other useful ECMA Script functions, so we will have more to add to the library.)
Lets start by changing the query to show what we really want now, ACL values.
<do-set-local-variable name="USERS" notrace="true" scope="policy">
<arg-node-set>
<token-query class-name="User" datastore="src">
<arg-string>
<token-text xml:space="preserve">ACL</token-text>
</arg-string>
</token-query>
</arg-node-set>
</do-set-local-variable>
Note we are only grabbing Users. If you want to be really detailed, you could choose all objects from the root of the tree, after all, in eDirectory every object is a security principal (unlike some other so called directory services **COUGH**Active Directory***COUGH***, What? I was coughing, I didn't say anything) and therefore can have excessive rights to other objects.
Note that we are leaving the notrace="true" attribute set, because as we discussed in part 3, the query might only take 10 seconds, but displaying in trace while we are debugging it might add a minute or two for thousands of objects. For testing, you probably want to scope it down, so that it only looks for one target object, and turn tracing back on, so you can see if it is doing what you want, and once you are happy, then take a swing at the entire tree. (I found that with trace enabled this could take almost two hours on a real production tree with 4000 objects. With trace tuned where appropriate, my entire much more complex rule than we will attempt here, took only about 4 minutes!)
Ok, so USERS is our node set variable and we have thousands of users with ACL attributes listed. Now we have to be a little more careful inside of for-each loop, because we actually need an inner loop to loop through all the ACL values.
The node set for ACL's that our query returns will look something like this one:
<nds dtdversion="3.5" ndsversion="8.x">
<source>
<product version="3.5.11.20071213 ">DirXML</product>
<contact>Novell, Inc.</contact>
</source>
<output>
<instance class-name="User" event-id="sevrer#20080731204312#1#1" qualified-src-dn="o=acme,ou=people,cn=test" src-dn="\ACMETREE\acme\people\test" src-entry-id="666644" timestamp="1217536992#49">
<attr attr-name="ACL">
<value timestamp="1217536992#45" type="structured">
<component name="protectedName">[All Attributes Rights]</component>
<component name="trustee">\ACMETREE\acme\people</component>
<component name="privileges">2</component>
</value>
</attr>
</instance>
</output>
</nds>
Our main loop through the USERS variable is actually looping at the <instance> node level, and thus we would only do a single loop at that level with this sample document.
To actually do what we want, we need to do a for-each loop through the values. The node set for our for-each is thus XPATH of $current-node/attr[@attr-name="ACL"]/value which saying, the current node of our outer loop (the <instance> node), and then loop through all the <value> nodes.
Then we can do something to each. Our bittest() function or something else, depending on your goals. However that in itself is a large enough topic, that lets hold off for part 5!
We will continue this is Part 5. Same bat time, same bat channel!