#!/usr/bin/perl -w # This is an example script that reads entries on an existing LDAP # server, munges the schema a bit, and then sends changes to a new LDAP # server. It generates output only when it performs updates on the new # server, as it is intended to be run from cron, and we don't need # e-mail from cron if nothing interesting is happening. # In this scenario, we were deploying the new LDAP service, and needed # to be able to synchronize data from an incumbent LDAP service, as well # as an EU-based company that we had acquired. This script synchronizes # data from the EU Exchange server in to the new LDAP system. Use Net::LDAP; # The LDAP server we are reading data from. In this case, an Exchange # server in Europe. $oldap = Net::LDAP->new('REMOTE_LDAP_SERVER') or die "REMOTE_LDAP_SERVER: $!\n"; $oldap->bind or die "bind: $!\n"; # Our local LDAP server. We will populate it with data from EU. $nldap = Net::LDAP->new('LOCAL_LDAP_SERVER') or die "LOCAL_LDAP_SERVER: $!\n"; # Bind with a DN that has sufficient privileges to add and update # entries. $nldap->bind( dn => '??????', password => '******' ) or die "bind: $!\n"; $omsg = $oldap->search( # ea1 set to correspond to Unix UID, so this is what we want to # search for. I forget what ea3 was supposed to be, but we only # want entries that have that set as well. filter => '(&(extension-attribute-1=*)(extension-attribute-3=*)(objectclass=organizationalPerson))', # We read THESE attributes to the new server. attrs => [ 'Extension-Attribute-1', 'cn', 'givenName', 'sn', 'mail', 'Extension-Attribute-2', 'telephoneNumber', 'mobile', 'title', 'physicalDeliveryOfficeName', 'co' ] ); $omsg->code && die $omsg->error; while( $oentry = $omsg->pop_entry ) { # Extension-Attribute-1 -> US uid my $uid = ($oentry->get_value('Extension-Attribute-1'))[0]; $uid = lc($uid); # What do we have on the new server for this UID? $nmsg = $nldap->search( base => 'NEW_LDAP_BASE_DN', filter => "uid=$uid", attrs => [ 'uid', 'cn', 'givenName', 'sn', 'mail', 'tellmeAIMID', 'telephoneNumber', 'mobile', 'title', 'physicalDeliveryOfficeName', 'co', 'objectClass' ] ); $nmsg->code && die $nmsg->error; $nmsg->count > 1 && die "More than one entry found where uid=$uid!\n"; $nentry = $nmsg->entry(0); # Munge $oentry to look like $nentry should look ... # Set DN $oentry->dn("uid=$uid,NEW_LDAP_BASE_DN"); # Add UID $oentry->add('uid'=>$uid); # Set ea2 -> tellmeAIMID if( $oentry->get_value('Extension-Attribute-2') ) { $oentry->add('tellmeAIMID'=>$oentry->get_value('Extension-Attribute-2')); } # Discard ea1, ea2 $oentry->delete('Extension-Attribute-1'); $oentry->delete('Extension-Attribute-2'); # If there was no entry on the new server ... if( ! $nentry ) { # ADD $oentry->add('objectClass'=>'tellmePerson'); print "Adding $uid ... "; $result = $nldap->add($oentry); if( $result->code ) { print "FAILED! (" . $result->error . ")\n"; } else { print "DONE!\n"; } } else { # UPDATE # Set the object class $oentry->add('objectClass'=>[$nentry->get_value('objectClass')]); # We determine if $oentry != $nentry by placing the values of # allof their attributes in a string. # This is a hackish way of comparing multiply-defined attributes # that depends on LDAP implementation returning attributes in a # consistent order. There is nothing to say that the LDAP # server must do us this favor, though. # Worst case, though, we just end up doing extra work below, not # sending spurious updates ... my( $ostr, $nstr ); foreach my $a (sort $oentry->attributes) { $ostr .= join(":",lc($a), $oentry->get_value($a)); } foreach my $a (sort $nentry->attributes) { $nstr .= join(":",lc($a), $nentry->get_value($a)); } if( $ostr ne $nstr ) { # print "ostr: $ostr\nnstr: $nstr\n"; # Replace supplied attributes foreach my $a ($oentry->attributes) { # See, this time, we go through each attribute and SORT # them in to a string. Much nicer. my $oas = join('', sort $oentry->get_value($a)); my $nas = join('', sort $nentry->get_value($a)); # Then, we just replace all values for the given # attribute. if( $oas ne $nas ) { $nentry->replace($a=>[$oentry->get_value($a)]); print "UID $uid: replacing $a attribute.\n"; } } # Remove removed attributes foreach my $a ($nentry->attributes) { if(! defined $oentry->get_value($a) ) { $nentry->delete($a); print "UID $uid: removing $a attribute.\n"; } } print "Updating $uid ... "; $result = $nentry->update($nldap); if( $result->code ) { print "FAILED! (" . $result->error . ")\n"; } else { print "DONE!\n"; } } else { # print "$uid unchanged.\n"; } } }