Foswiki’s LDAPContrib module does not support configuring multiple LDAP servers for redundancy or resiliency purposes. I’m not talking about consuming multiple difference LDAP stores but rather having the ability to configure a primary and backup(s) LDAP server.

I created a patch to enable this in a rudimentary fashion. It is available on GitHub https://github.com/mike2038/LdapContrib/commit/9260ce093b73f09bd6bfefc1d8b455ee1c4e5c02?diff=unified

This relies on the Net::LDAP module’s behaviour when an array is passed to the host entry for new LDAP connections. This is a preference order based array of ldap:// or ldaps:// URIs, host:port pairs or hostnames.

A couple of points to note about this patch

  • No caching of the most recently used LDAP server. For example, in a list of three LDAP servers where the first two are unavailable connection attempts will still be made to them before the third is tried. This will likely cause delays during login or other activities which require working LDAP connections. In my opinion, these delays are preferable to users being unable to login at all.
  • If you need different ports or connection schemes (i.e. ldap vs ldaps) or different Root CA certs then results might be mixed. Assuming you have certificates issued by the same Root CA then Net::LDAP seems to work with different hosts and ports. I suggest testing be carried out.

 

I ran into a problem performing memberOf LDAP queries against an Active Directory controller. Turns out that  while almost everyone is able to read most attributes from user objects, by default memberOf is not visible. The result is that queries using memberOf do not return a result. I was frustrated that the queries worked when tested on a domain controller using the LDP.exe utility, when tested as a domain admin. I was focused on getting the LDAP query correct and had not stopped to consider that the LDAP bind account I was using could not read the memberOf attribute.

Anyway, turns out that “Read Member Of” is a property you can assign via the Active Directory Users and Computers MMC. Right click on the top OU from where you want the permission to be granted (this might be the root of the AD tree or a sub-OU) and select “Properties”. Select the “Security” tab and then click “Advanced”. You can now add a permission for the LDAP bind user (or group as needed) using the “Add” button. Select a principal (i.e. user / group as needed), choose “Allow” for the “Type” and then you’ll probably want “Applies to” to be “This object and all descendant objects”. Scroll the bottom of the screen and choose “Clear all” and then find and select the property “Read Member Of”. Save and close the windows and your LDAP bind account should be able to filter based on group membership.

The below is a simple Perl snippet (based on code from http://search.cpan.org/dist/perl-ldap-0.55/lib/Net/LDAP/Examples.pod) to test the LDAP queries.

use Net::LDAP;

$ldap = Net::LDAP->new ( "dc01.example.com" ) or die "$@";
#if you has LDAPS enabled you can use the following instead
#$ldap = Net::LDAP->new ( "ldaps://dc01.example.com" ) or die "$@";
$mesg = $ldap->bind ( "CN=ldapbind,OU=Services,DC=ad,DC=example,DC=com",
 password => "LongSecretPassword",
 version => 3 );

#@Attrs = (); # to request all attributes
 my @Attrs = ( 'cn','mail','givenName','sn','sAMAccountName');
 my $base = "DC=ad,DC=example,DC=com";
 #find members of the group
 #my $filter = "(memberOf=CN=Some-group,OU=Groups,DC=ad,DC=example,DC=com)";
 #find all objects with a sn attribute
 #my $filter = "sn=*";
 #find active users within the specified group
 my $filter = "(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(memberOf=CN=Other-group,OU=Groups,DC=ad,DC=example,DC=com))";
 #my $result = LDAPsearch ( $ldap, $query, \@Attrs );
 my $result = $ldap->search ( base => "$base",
 scope => "sub",
 filter => "$filter",
 attrs => \@Attrs
 );

my @entries = $result->entries;
 my $entr;

foreach $entr ( @entries ) {
 print "DN: ", $entr->dn, "\n";

my $attr;
 foreach $attr ( sort $entr->attributes ) {
 # skip binary we can't handle
 next if ( $attr =~ /;binary$/ );
 print " $attr : ", $entr->get_value ( $attr ) ,"\n";
 }
 print "#-------------------------------\n";
 }

A useful summary of LDAP search options supported by AD servers (incl for disabled accounts and nested-group membership) is in an article titled “Active Directory: LDAP Syntax Filters“.