Wednesday, October 31, 2012

Accessing Domino LDAP with C# and DirectorySearcher

In enterprise software, it is a common task to look up information on users from the company, using one known property to return an unknown property; for instance, looking up a user's email from their account name.  The .Net framework makes this a relatively simple task, with the System.DirectoryServices namespace, which provides functionality for dealing with LDAP directories.  This integrates very nicely if the directory server being used is Microsoft's Active Directory.  Some customers use a non-Microsoft or mixed software stack, however, but in that case it is still possible to use the DirectoryServices namespace to access directory information; there are just a few gotchas involved.

Let us take for example the common task of looking up a user's CN from their email address.  Below is a function for doing so using DirectorySearcher going against Active Directory.

Equivalent code for accessing a Domino LDAP directory is below.
You'll notice two differences between the two functions.
  1. When accessing a Domino LDAP server, it is necessary to specify AuthenticationTypes.None as the fourth parameter of the DirectoryEntry constructor. This property defaults to AuthenticationTypes.Secure, which uses NTLM or Kerberos to authenticate the client against the server. Domino LDAP typically does not support these authentication types, so if this flag is omitted, the code would throw an error when binding to the server. (Note: this default changed to the current value with version 2.0 of the .Net framework, which may be a source of confusion if viewing out-of-date documentation or older example code)
  2. (objectClass=dominoPerson) vs (objectClass=user) - Active Directory and Domino LDAP use different schemas to represent user account objects. Certain of the attributes of these schemas are more-or-less standard, such as cn and mail, so both filters could have been written simply "(mail={0})", but it is more efficient to filter the results by objectClass first.
In comparison to the alternative of using the interop.Domino.dll COM library to perform lookups on a Domino LDAP server, this DirectoryServices implementation has the virtues of being very similar to standard Active Directory lookup code, which is well-documented and widely available in tutorials, examples, and stack overflow postings, and also does not have any non-framework dependencies, making deployment a simpler task.

Tuesday, October 16, 2012

Sametime privacy list issues during Sametime conversions

In a recent Sametime to Sametime conversion we noticed some strange behavior during post-migration analysis. When migrating users, either from Sametime to Sametime, or from Sametime to Lync, we read from Sametime's User Information database (vpuserinfo.nsf), to extract information, and then stage some XML files which we'll use during our migration process. These files include buddy lists, lists the user is referenced on, public and private groups, etc. The issue we're seeing was specifically centered around Privacy List data which we don't modify during our conversion. The users that were impacted had their privacy lists swapped after the conversion, so users who had previously set conditions in their privacy settings, saw essentially the opposite effect post-conversion. For instance, if I had made it so that you could see me when I was in 'Do not Disturb' mode in Sametime, the opposite would be in place post-conversion, where everybody EXCEPT for you saw me as available post-conversion.

Sametime exposes two types of privacy lists:
  • A list of users that see logged-in users as "Available" when they are in a "Do not disturb" state.

  • A list, either inclusive or exclusive, that controls which users can see the logged-in user when they are online.
These settings are stored in the fields of the 'privacy' record. The relevant fields are:
  • Person - contains the name of the user that has set the privacy rule
  • Buddy - contains a delimited list of names of the users that the privacy rule affects
  • CanSeeMe - this value is 0 if the “buddy” users should not see the logged-in user online.  The value is 1 if only the “buddy” users should see the logged-in user online.  This field does not exists, or is null, if the “buddy” users should see the logged-in user as available when they are set to a status of “Do not disturb.”
  • TriggerStatus - this is the availability status of the logged-in user that triggers the privacy rule. For the 'Do not disturb' rule, this value is 128.
  • DisplayStatus - this is the availability status that will be displayed to the 'buddy' users when the logged-in user's status matches the triggerStatus value.

Setting a Do Not Disturb => Available Rule

In the process of outlining this information, we established a “Do not disturb” rule for the user CN=Peyton McManus/OU=US/O=Instant, with the above six users.  The resulting privacy record on our Sametime server is shown below.

As you can see, buddies shows the six test users defined for the rule.  TriggerStatus is equal to 128 and displayStatus is equal to 32.  Buddies see person, which in this view maps to the underlying canSeeMe field, is set to null.  The key field here is the listID field which is set to a unique ID that we are not able to create.

Setting an Exclusion Rule

Likewise, we tested setting up an exclusionary rule, this time for user “eric richards,” excluding the six users shown. The resulting privacy record on our Sametime server is shown below.

Here, you can see that buddies contains the six test users, and this time, the “buddies see person” value is set to 0, rather than null.  However, the key value is that the listID field is set to theFirstPrivacy_000111 value – which is how Sametime is categorizing people to display on the 2nd tab of the privacy dialog.

Overview of the Conversion Process

During the conversion process, none of these privacy records are modified.  The conversion code only affects field “8193” of the documents that have Form= “StorageAttributes.”  This is where contact lists and server information is stored in an xml format.  After the error with certain users privacy settings were reported, we began reviewing all of the privacy records, attempting to determine if any updates may have impacted any of the privacy settings. After creating a series of views to investigate this behavior, it appears that some automatic process on the Sametime server is updating the listID field of the privacy record.

Here is a screen shot of the privacy view immediately after our conversion.  The listID is exactly the same value both before and after our conversion.

Here is a similar view from the new production server.  Notice that the listID value has been set to the value theFirstPrivacy_000111.  Based on the time stamps, it appears that this value is being set at a server level, possibly by a server process.

Here are the 2 environments (post conversion and new production) with an overlay to show the before/after.

Based on this analysis, we have focused on the upgrade utility provided by IBM to upgrade the vpuserinfo database.  Our internal testing shows that the upgrade utility is converting the listID field of the privacy records.  The following screen shots demonstrate this within one of our test environments. Before running the Lotus Notes Commands to upgrade VPUserInfo database, privacy record look like the following screenshot:

Next, we stop the Domino Server and run the following commands:

After running the command, privacy records look like the following screenshot; listID has changed.

Further Reading: