Walking through the Office 365 IDM driver – Part 12
In part one of this series I walked through some of the configuration, Packages, and GCVs used in the Office 365 IDM driver.
In part two of this series I walked through more of the GCVs and looked at some possible values for the License entitlements.
In part three of this series I looked at the Filter and Schema Map and some more entitlement issues.
In part four of this series I looked at the configuration settings and then on to actual policies, getting through the Subscriber Event Transform policy set.
In part five of this series I worked through the Subscriber Match and Create policy sets.
In part six of this series I started in on the Subscriber Command Transform policy set.
In part seven of this series I continued through the EntitlementsImpl policy in the Command Transform.
In part eight of this series I finished up the Command Transform and started into the Output Transform.
In part nine of this series I finished walking through the Output Transform.
In part ten of this series I started down the Input Transform policy set getting through the first six policy objects.
In part eleven of this series I finished the Input Transform policy set and got through the Publisher Event Transform policy set.
In this article I will continue down the Publisher channel starting with the Match Policy.
There are three policy objects here:
These look very familiar, since they look like the model used in all the Packaged drivers, a scoping, then Entitlements handler, and finally a real matching policy.
There is the usual single rule here: remember relative position in hierarchy
Though, this basically makes sure the attempt-to-match operation property is set to true, whereas on the Subscriber channel this rule usually looks at the Source DN, and makes sure the event is in scope. In the Office 365 case, there is no structure per se (at least as exposed to the end user, probably Microsoft has some way of handling the different customers under the covers) exposed to the driver, so everybody in this tenant instance is in scope, so no need for the usual scoping. In fact, it looks like the only reason this is here, is to maintain similarity with other drivers.
The test condition is to see if attempt-to-match is not equal to false, in which case it is set to true. What else would set this Operation Property? The Subscriber channel, sure, but those events would not be processed here. I suppose a Migrate into Identity Vault, sends a query, on the Sub Channel, and the results become events for the Publisher channel to process, but none of that would get tagged with an operation property.
I wonder if there is some specific meaning to this, or if this is just a simple case of not cleaning up unneeded policies?
This policy checks on the case of a User if the GCV enabling the entitlement model is true, in which case it sets attempt-to-match to false.
That is, the matching rule will not attempt to match, if entitlements are enabled, and we have a user in Office 365 being created (raw add) or a modify of a non-associated object (Synthetic add). Which is sort of odd, since you would think, in the case where you are trying to reconstitute a system that had been matched you would
As usual, this policy does the actual matching, after the Scoping and Entitlements implementation rules have allowed the event through. The Scoping was passed along as a operation property and modified by the Entitlements if needed, and if the operation property attempt-to-match is not set to true, then the event is vetoed.
Users and Groups attempt to match by the CN event attribute against CN in eDirectory which is remapped from User Principal Name earlier in the policy flow (by removing the @domain.com part) in the Output Transform.
Creation Policy Set:
Only a single policy object here.
There are three rules in here:
- Set CN if not already set
- Get Surname from Full Name if necessary
- Get Given Name from Full Name if available
1. Set CN if not already set
eDirectory requires a naming attribute, and generically CN is used. It happens UniqueID is part of schema, flagged as a naming attribute on a User object. You could of course define your own different naming attribute if you desired to muck with base schema. Thus if CN is not available for some reason, I guess the object did not have a UserPrincipalName style name set in Office 365, then use the Full Name attribute, since that should always be there, and is at least some form of naming, specific to the end user.
2. Get Surname from Full Name if necessary
eDirectory also requires a Surname attribute as a mandatory attribute on the User class, so if there is no Surname, this rule looks at the Full Name attribute, uses the split token, splitting Full Name by spaces, into a nodeset variable called fullNameNodeset.
Then it sets the Surname value as the XPATH $fullNameNodeset[last()] which gets the last value of that nodeset, which is pretty close to Surname. Of course there are all sorts of communities with funny naming models, like Indonesians who often do not have a Surname at all. Rather they have a very long first name, but they are exceptions in this regard. Someone with a hyphenated last name would work here, but someone with a two word Surname would be missed.
3. Get Given Name from Full Name if available
The same approach is taken for Given Name, using the nodeset of Full Name split upon spaces, and using the XPATH of $fullNameNodeset to get the first value. It turns out that the notation $var is actually shorthand for $var[position()=1] which is subtly different. I only recently learned that you can use less than/greater than tricks in here as well. For example you could say something like:
$fullNameNodeset[position() > 1]
This should return all nodes hat are greater than position 1. I had been wondering if it was possible if you had a nodeset of values (not XML, but like this case, just a series of values in a nodeset, the closest thing to an array IDM provides) if you could poke a value into the middle of it. Someone suggested you could use the Join token, and define your nodeset to be joined as $fullNameNodeset[position() 2] which would accomplish vaguely what I wanted but not in a simple XPATHy way.
Placement Policy Set:
Only a single policy object here as well.
This policy has two rules:
- Placement for Users
- Placement for Groups
1. Placement for Users
The destination DN is set simply as the idv.dit.data.users GCV value slash the CN that either preexisted in O365 or we just set in the Publisher Creation policy set.
2. Placement for Groups
Placement is the same for groups, differing only in the name of GCV that holds the group container for the Identity Vault.
Command Transformation Policy Set:
There are eight policy objects in this policy set.
The last five policy objects are really the very standard Password policy objects that almost all drivers share in common.
There are two rules in this policy object.
- AccountTracking - disregard policy if disabled
- AccountTracking - on add operation add Dirxml-Accounts
1. AccountTracking - disregard policy if disabled
This is a nice rule that allows you to have the package installed, but use a GCV to turn it off. If drv.acctTrk.enable is not true, then break out of this policy object.
2. AccountTracking - on add operation add Dirxml-Accounts
This fires on add events. It adds on the DirXML-Identity aux class, by adding that value to the Object Class attribute. But it does it in a sort of odd looking way. It says Add destination attribute, but instead of saying, add to current operation, but it specifies a Destination object DN as the current events Destination DN. Initially I thought, that is probably a mistake. But in fact, I think I know why that might be on purpose.
The engine has a funny way of handling "Current object". It is almost as if it has a very short attention span, like that of a three year old. The current object focus can be lost very easily. When there is any reference to another object, it starts a fresh event, and you cannot get back to adding to the current event. Which with an object class is bad news, if your attribute is in the or another from the add of the aux class, you get schema errors when you do it. I think, by explicitly specifying another object by a DN it is forcing a new event to guarantee that the following attribute adds get sent in the same event. That is a clever way of forcing it that I had never noticed before.
Then it loops over the Account Identifiers GCV which is userPrincipalName and association in this driver and for each of them looks at the Op Properties for one named with it and if so, uses the data to build the DirXML-Accounts attribute to be added. Finally it clears a couple of operation properties, which I am not sure why they bother.
This policy object has four rules in it.
- Prevent unassociated users from being removed from groups
- re-format CN
- convert modify CN to rename
- veto empty modify
Prevent unassociated users from being removed from groups
This looks for modify events, of Group objects with changing Member values. Note, not available Member values, rather changing, which means remove or add of a value.
It loops over each Removed Attribute of Member in for each loop and then stores the current-node value in memberDN variable because it is going to loop again and that will remove access to this loops current-node variable.
The inner loop is the old fashioned way of doing a nested IF before the IF statement token was added. You loop over an XPATH that returns or not. Of it returns, do something if not don't. Well that is an IF test, isn't it? This time it does an XPATH of:
query:readObject($destQueryProcessor, '', $memberDN, '', '')[not(association[. != ''])]
This makes a Java query for the $memberDN's specific object DN. In the results, it does a predicate, to only loop over those whose predicate is true:
[not(association[. != ''])]
That is, NOT one whose association is not equal to null. That is, who does not have an association value. For those, who do not have an association value to this driver, they strip by XPATH the $memberDN.
Then they strip any remove-value nodes who are missing nodes. Then the strip by XPATH any modify-attr nodes that are now empty.
This means if a member of a group in Office 365 is being removed, but is not associated in eDirectory to this driver do not process that attribute. But this has me wondering, shouldn't the engine have done that for us? Why do we need to do this again. Will have to think about that one.
2. re-format CN
This is another reinvention of the Reformat Operation Attribute token, made funnier, since the rule name is even reformat CN! If a user then it gets a variable attrNS with the add or modify versions of the operation attribute the hard way in XPATH. Then it splits that value into a string variable on the @ sign, strips the current node and then appends the XML text back of just the CN part of the UPN (What CN is mapped to in this driver). A simple reformat operation attribute is all that is really needed.
3. convert modify CN to rename
If it is a user or group being modified, and the op attribute CN is available, that is a rename, so lets change over to a rename event.
They store the class name in a variable, (since we test for user or group) and then do a rename destination object, which I do not think requires the class, just the DN. especially since they are renaming the current object, not even specifying a DN and then they strip out the CN modify from the event.
4. veto empty modify
Because we might strip out the only modify-attr part of a modify (CN changing in a rename, the earlier remove of only one group member being removed, but unassociated so stripped) they added a rule that if it is a modify and count(./modify-attr)=0 (I.e. Zero modify-attr nodes then veto the modify.
That about wraps up this episode, stay tuned, same bat channel, probably not the same bat time for wrapping up the last bits of this driver.
I was thinking I would then go through the latest packages and compare the differences between the ones I reviewed and the newest to see which of my comments they looked at and changed. Could be interesting to see what has changed.