Walking through the Office 365 IDM driver – Part 8

0 Likes




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 this article I will continue through the Command Transform.

The remaining policies in the Subscriber Command Transform are the Password Synchronization rules. These have remained reasonably static for the last many driver configuration revisions. Though to be fair, I have not checked to compare the earlier versions to the latest packaged versions, but the functionality is about the same, which mutes most of my concerns.


  1. NOVLPWDSYNC-sub-ctp-TransformDistPwd

  • NOVLPWDSYNC-sub-ctp-DefaultPwd

  • NOVLPWDSYNC-sub-ctp-CheckPwdGCV

  • NOVLPWDSYNC-sub-ctp-AddPwdPayload

  • NOVLOFFIPSWD-sub-ctp-TransformPwd



Of those remaining five policies, the first four are the standard ones out of the Password Synchronization Common package that almost all the drivers use, so very little new here to see. The twist is that there is one addition password related policy from an Office 365 driver package: NOVLOFFIPSWD-sub-ctp-TransformPwd

To understand what is going on in the Subscriber channel password transformation rules, I will refer to an article I wrote on the topic, and its other half for the Publisher channel. I found these very helpful, since they helped me understand what is going on, and some of the why it is going on:






To summarize for those not interested in the literally gory details in those articles, when a password comes out of eDirectory on any driver (the Subscriber channel) it can come two ways. First as a change in Private Key and Public Key attributes. These are the old Password Sync 1.0 approach and really only worked to sync to another eDirectory instance. There was some workaround I never fully understood in IDM 2.0 that allowed you to sync this to an Active Directory system. The second is to send a modify event for the attribute nspmDistributionPassword which is a change of the Universal Password (synced to the Distribution Password, see the password tunneling article for why that is).

How this password change is sent to the connected system is mostly controlled by these policies, following the GCV settings. Shims, often implement the <modify-password> event in both directions. That is, a password change coming out of an application, if it is possible to trap it, will generate a <modify-password> event on the Subscriber channel. Converesly, to send a password change event to the shim, often a different function (other just modifying an attribute) is sent to the connected system, and is triggered by sending in a <modify-password> event to the shim.

Thus the <modify> event for attr-name="nspmDistributionPassword" needs to become a <modify-password> event in most drivers.

In the other channel, the <modify-password> event needs to be converted to a >modify> event of the nspmDistributionPasssword, unless you really wish to change the RSA password, in which case, let the <modify-password> through and the engine will process it that way. The downside is that this usually counts as an administrative password reset, and if your password policy is set to Expire passwords when Admin reset (It is actually worded in the negative "Do not expire passwords on administrative reset") then a <modify-password> event will expire the password as well.

9. NOVLOFFIPSWD-sub-ctp-TransformPwd



Now this driver has a different step, and it has a single rule:

1. Transform modify-password to modify




This rule detects the <modify-passsword> event for a User, (amusingly case is wrong, it is user in the policy not User) and then it does some shenanigans to avoid showing the password in the trace.

First it adds a destination attribute, Password, with a blank value. Then it sets the XML attribute type to password-ref, on the <add-value> node of the <modify-attr> for the Password attribute.

Then the is-sensitive XML attribute is set to hide the contents. Only then is the text of the password node added.

The XPATH to add the password value makes more sense if you have seen a <modify-password> event, which looks something like this:

<nds dtdversion="4.0" ndsversion="8.x">
<source>
<product version="?.?.?.?">DirXML</product>
<contact>Novell, Inc.</contact>
</source>
<input>
<modify-password>
<password>SomePassword</password>
</modify-password>
</input>
</nds>


Normally the is-sensitive XML attribute is set on the <password> node coming out of the engine or the shim which would hide its contents, but the snippet above is just an example so I did not show it hidden. Thus the XPATH of ./password/text() will get the password value. This action is set to no-trace, so that it will not show up in the trace.

The modify that has been tweaked here will look something like:

<nds dtdversion="4.0" ndsversion="8.x">
<source>
<product version="?.?.?.?">DirXML</product>
<contact>Novell, Inc.</contact>
</source>
<input>
<modify class-name="User">
<modify-attr attr-name="Password">
<add-value>
<value type="password-ref" is-sensitive="true">SomePassword</value>
</add-value>
</modify-attr>
</modify>
</input>
</nds>


Except that when the engine renders an is-sensitive attribute it looks more like:

<nds dtdversion="4.0" ndsversion="8.x">
<source>
<product version="?.?.?.?">DirXML</product>
<contact>Novell, Inc.</contact>
</source>
<input>
<modify class-name="User">
<modify-attr attr-name="Password"><!-- content suppressed -->
</modify-attr>
</modify>
</input>
</nds>


Which makes it hard to understand the XPATH you might need under the covers.

Finally a Strip by XPATH of . (a period, meaning the current operation) removes the <modify-password> event. A Veto probably would have sufficed, since the Add Destination Attribute will have added a second event, the <modify> that a Veto would not stop.

Now having seen how this is done, you need to realize that a bit of circular logic has happened here.

Coming out of eDirectory, a <modify> of nspmDistributionPassword was the event that started the process. The standard Password Synchronization rules mapped that to a <modify-password> event and now we are converting that back to a <modify> event for a Password attribute. That sounds remarkably like a job for the Schema Map to perform, instead of this dance of modify to modify-password to modify again. But I suspect they wanted to maintain the basic password policies, and then modify after the fact, to keep things vaguely consistent.

That wraps up the Command Transform for the Subscriber channel. On to the Output Transform!

Output Transform Policy Set:



There are five policy objects in this policy set.


  1. NOVLOFFIENTEX-otp-EntitlementsImpl

  • NOVLOFFIDCFG-otp-Transform

  • NOVLATRKBASE-otp-Subscribe

  • NOVLOFFIPSWD-otp-Transform

  • NOVLPWDSYNC-otp-EmailOnFailedPwdPub



1. NOVLOFFIENTEX-otp-EntitlementsImpl



There is a single rule here, called "Intercept outbound queries for MSolDomain" and as the name suggests (I love when that works out) it looks for a query for class MSolDomain.

If so, it mucks about with the XDS. A query for MSolDomain might look something like this coming into the policy:

<nds dtdversion="4.0" ndsversion="8.x">
<source>
<product version="?.?.?.?">DirXML</product>
<contact>Novell, Inc.</contact>
</source>
<input>
<query class-name="MSolDomain" dest-dn="MyDomain">
<read-attr/>
</query>
</input>
</nds>


This policy will transform it to look like this:

<nds dtdversion="4.0" ndsversion="8.x">
<source>
<product version="?.?.?.?">DirXML</product>
<contact>Novell, Inc.</contact>
</source>
<input>
<query event-id="query-driver-ident" scope="entry">
<search-class class-name="__driver_identification_class__"/>
<read-attr/>
<operation-data UserAccountEntitlementQuery="MyDomain"/>
</query>
</input>
</nds>


This seems to be how the drivers workaround not being able to answer a query that either Reporting or User Apps code map refresh might ask, and therefore rather than error, reformat as this, which returns the driver startup message, and a policy in the Input Transform will convert it back to a nonsensical informationless response. But the key is, the query will thus succeed, even if it returns no useful data.

The process to get there uses the XML modification tokens as follows.

Strip by XPATH the class-name attribute from the <query> node, using this XPATH:

../query[last()]/@class-name



Of course @class-name is the same thing, not sure why the extra complexity is needed.

Then set XML attribute event-id to a specific string of query-driver-ident that our ITP rule will trigger upon in a moment.

Then set the scope XML attribute to entry.

Strip off the search-class node, with ../query[last()]/search-class which as before would have sufficed with just an XPATH of search-class.

Then the search-class element is added back, this time with a class-name XML attribute of "__driver_identification_class__". If you have ever watched a driver startup in detail you will see it execute a query for this class, which returns basic information about the shim. What license credential it requires, whether it supports query-ex or the like. I once tried to intercept that query, and append the XML needed to tell the shim it DID support query-ex because I needed query-ex support enabled for my SOAP driver. At that time, the SOAP shim did not allow query-ex support. They did after opening a service request, update the shim to use a configuration variable to control query-ex support. But regardless of my efforts the engine is doing something funny with this query response, and did NOT, during driver startup, let me touch it. I thought it was a clever workaround approach, but of course, if I could manipulate the query-ex nodes of that response document, the what would stop me from changing the license requirement nodes as well, so you can understand why it was protected.

Then the read-attr element is removed, using again, a needlessly complex Strip by XPATH just like the ones before, and an empty read-attr is appended to the event to replace it.

Finally an operation property, UserAccountEntitlementQuery is added with the value of the Destination DN token.

You can see how all those steps add up to the resulting document above.

2. NOVLOFFIDCFG-otp-Transform



There are seven rules in this policy, and here we see some Office 365 specific attribute manipulations.


  1. strip "Managed By" from group

  • re-format Member values

  • Update add event

  • Check Query

  • reformat BlockCredential

  • reformat ImmutableID

  • Handle AlternateEmailAddresses



1. strip "Managed By" from group



This rule puzzles me. It looks for the class MSolGroup (post schema map remember) and it strips the ManagedBy attribute which was mapped to Owner. But why leave it in the filter as Synchronize if you are just going to strip it? And why map it as well? Just seems odd.

2. re-format Member values



This is an interesting approach to a simple problem. DN's in eDirectory are based on the objects location in the tree and contains the parent nodes. So \TREE\myO\myOU\yourOU\weAllOU\together but in Office 365 it is a bit flatter, and you want just the naming attribute.

Now of course the proper way to handle this is a with a simple Reformat Operational Attribute token, which is designed to handle this exact case.

You would just simply:
<do-reformat-op-attr name="Member">
<arg-value type="string">
<token-parse-dn length="1" start="-1">
<token-local-variable name="current-value"/>
</token-parse-dn>
</arg-value>
</do-reformat-op-attr>


However the author decided to take a 'harder' approach. First set local variable attrNS to the XPATH of:
modify-attr[@attr-name='Member']//value | add-attr[@attr-name='Member']/value

This tries to handle the modify and add cases since their XML is slightly different. To me this seems exactly the case that the Reformat Op Attr token was built to handle.

Once it has that single nodeset of values, for the two event types, it loops over it, takes the current-node variable and uses ParseDN on a DN value to get just the leaf most node (ParseDN with length of 1, start at -1).

Then it strips out the value as a DN and then adds it back with the Append XML text token and the leaf most node that was just calculated.

3. Update add event



This rule basically converts the CN value which is mapped to UserPrincipalName (UPN) but from eDirectory is coming on an add event as a simple name, to more UPN like formatting. UPN is the critical naming thing in Office 365 and needs to look like an email address normally, but in the Office 365 case, actually will be the email address.

This is another example where Reformat Operation Attribute would do the trick much simpler than all the work needed in these policies. The old value being added is stored in a variable domainName, oddly with an XPATH of:

./add-attr[@attr-name="UserPrincipalName"]/value/text()



By now, most readers of my articles should recognize that is the same as using the Operation Attribute noun token, for UserPrincipalName. Much less typing that way!

Then strip out that value, and append it back as XML text as an email formatted UPN, of the domainName variable, the @ sign, and then the GCV value for the domain name. All that was really the work of a single token:

<do-reformat-op-attr name="UserPrincipalName">
<arg-value type="string">
<token-local-variable name="current-value"/>
<token-text xml:space="preserve">@</token-text>
<token-global-variable name="drv.domain.name"/>
</arg-value>
</do-reformat-op-attr>


Then the BlockCredential attribute, which is mapped to Login Disabled (which is nice, since the state is the same. Login Disabled = true is the same as BlockCredential = true. This is more of a pain when they are opposites! Perhaps if it was EnableAccount = true being mapped to Login Disabled) needs to be reformatted to account for the fact that Login Disabled being absent means the account is enabled, so BlockCredentail needs to be set to false in that case.

If it is there, they do the same three step Reformat Op Attr work, store the current value in a variable, strip it, then put it back converted.

Then far far stranger, they build an XDS Query event document in a variable and use a call the Java query processor, rather than simply using the Query token. Really an odd approach to my mind. I do not understand why they might have decided to go this route.

Regardless, a Query for GUID is performed, and the results stored in query-result. Then ImmutableId is set to the XPATH of:

$query-results//attr[@attr-name="GUID"]/value[1]/text()



This takes the first value of GUID, which I always assumed was single valued (Yep, checked it is). To further complicate this, they do not use the ECMA function in the LibAJC library to convert the binary GUID format returned to the GUID standard, as you might see as an association value on an Active Directory or eDirectory driver. Which is a shame.

Interestingly it uses the eDirectory GUID as the ImmutableId. I assumed when I saw the GCV for the eDirectory or Active Directory driver modes, that if you selected Active Directory, that the association value from that driver would be used (since it is the ObjectGUID in the connected Active Directory) as the ImmutableId. After all, that is what ADFS (The Microsoft solution for Single Sign On to Office 365) implementation would be expecting to use. This is fairly easy to rewrite to do it properly. Probably the hardest part to handle would be if one user could be in two Active Directory domains synched to your Identity Vault. Then you would have to think about what you wanted to do, and there might not be a simple predictable solution.

That is about all the time I have for this article now. Stay tuned for the next in the series as I continue through the Output Transform.

Labels:

How To-Best Practice
Comment List
Related
Recommended