Managing LDAP book list

I've got a Listmania list on Amazon called "Managing LDAP", my picks for books that every directory admin should have on their bookshelf.

Referential Integrity

Referential integrity is one of those essential functions that every directory needs, but on whose implementation there is no agreement among directory product vendors. The major use of referential integrity is to make sure that the user dns listed in groups actually exist on the directory. Without referential integrity a user could be deleted but the reference to their entry in a particular group (say, "cn=Administrators"), would not. This would be characterized as "A BAD THING (TM)" by most directory admins.

Recognizing the need for such a feature, Netscape included a "referential integrity postoperation plugin" in all of it's directory products from at least iPlanet 4. This plugin has survived to this day in both the latest Sun and Red Hat/Fedora Directory products (see the Red Hat Directory Administrator's Guide). It is disabled by default, but preconfigured to check on uniquemember and member. It can be enabled and re-configured through the GUI directory console or by editing dse.ldif under the directory server root. Once enabled, the plugin is invoked whenever an entry is deleted from the directory. You do take a performance hit with this feature turned on, which is probably the reason for shipping it disabled (you wouldn't want unnecessarily hurt benchmark performance, never mind how unreal-world the shipping configuration is -- kudos to the Fedora Directory team for finally ridding their product of the allidsthreshhold).

Oracle also provides a referential integrity solution. The instructions for enabling and configuring it are contained in the Oracle Internet Directory Administrator's Guide. The procedure detailed there steps you through the process of editing and compiling a Java object and modifying directory entries to enable the feature. It then shows how to edit a script file and run it against the underlying Oracle database. Finally, the manual recommends running the process at frequent intervals via cron or some other scheduler.

Comparing this to the Netscape/Red Hat/Fedora facility, the first word that came to mind was:

P-R-I-M-A-T-I-V-E

and really illustrates the difference in between the two product architectures. The Netscape family directories were optimized over time for use in building flexible directory services, built by directory developers and admins for directory developers and admins. The roots of the Oracle product are clearly in Oracle's relational (object) database product. At almost every turn you can see this legacy. Having to compile some java code, edit a script file and run a PL-SQL command to enforce referential integrity is just one of the more obvious examples.

Of course, to be fair, at least Oracle provides a solution. OpenLDAP doesn't. Referential integrity isn't even on their roadmap. Microsoft actually seems to do the best at this, referential integrity for things like groups is integral to the base product (and is probably one reason it doesn't perform as well as the default Netscape or Oracle configurations on delete and write operations).

Checking Active Directory for Duplicate E-Mail Addresses

Here's an application using Net::LDAP's module for Simple Paged Results Control.

Anyone whose environment depends on Microsoft Exchange for messaging knows what a pain duplicate e-mail addresses can be. Following is a script I wrote to walk the Active Directory tree and do a quick lookup for duplicate addresses. It's kind of raw, but it works. Uses a config file for host name, bind dn and password. Iterates through an array of the target containers specified. In this particular case we've got a separate container for what used to be known as "custom recipients", users who don't log into the domain but whose e-mail addresses we want to be searchable in the Exchange GAL. The script only reports on exact matches on the value of the 'mail' attribute in entries other than the current subject.


#!perl
# Check Active Directory for duplicate e-mail addresses.
# Created 03/12/07 by P Lembo

use strict;
use Net::LDAP;
use Net::LDAP::Entry;
use Net::LDAP::Control::Paged;
use Net::LDAP::Constant qw( LDAP_CONTROL_PAGED );


our($adHost,$adUsr,$adPass);
require "c:/apps/etc/app.conf";
my $errfile = "../addupchk.log";
open LOGZ, ">$errfile" or die $!;

my $count=0;
my $found=0;

my $contactbase = "ou=addressbook,dc=mydomain,dc=mycompany,dc=com";
my $adroot = "dc=mycdomain,dc=mycompany,dc=com";
my @allbases = ( "ou=users,dc=mydomain,dc=mycompany,dc=com",
"ou=others,dc=mydomain,dc=mycompany,dc=com",
"ou=addressbook,dc=mydomain,dc=mycompany,dc=com",
);

my $query = "(mail=*)";
my @attrs = qw(mail displayname cn);
my $timestamp = localtime();
print LOGZ "$timestamp\tBegin Checking AD for duplicate e-mails\n";

my $ldap = Net::LDAP->new($adHost) or die $!;
my $mesg = $ldap->bind($adUsr, password =>$adPass);

foreach my $base(@allbases) {

my $page = Net::LDAP::Control::Paged->new( size => 1000 ) or die $!;
my @args = ( base => $base,
scope => 'sub',
filter => $query,
attrs => \@attrs,
control => [ $page ],
);

my $cookie;

while (1) {

$mesg = $ldap->search ( @args ) or die $!;
while (my $entry = $mesg->shift_entry()) {
my $entrydn = $entry->dn();
my $mail = $entry->get_value('mail');
my $displayname = $entry->get_value('displayname');
my $cn = $entry->get_value('cn');

if($mail !~ /.+\@.+/g) {
print "$entrydn not mail-enabled\n";
}
else {
my @hits = dup_search($entrydn,$mail,$displayname,$cn);
foreach my $hit(@hits) {
if($hit =~ /$entrydn/) {
# Same entry, move on
}
else {
print "\t$displayname $entrydn mail same as\n";
print "\t$hit\n\n";
print LOGZ "\t$displayname $entrydn mail duplicate found\n";
print LOGZ "\t$hit should be deleted from contacts\n\n";

$count++;
}
}
} # else
} # while
my ($resp) = $mesg->control(LDAP_CONTROL_PAGED) or last;
$cookie = $resp->cookie or last;
$page->cookie($cookie);
} # while (1)

if ($cookie) {
$page->cookie($cookie);
$page->size(0);
$ldap->search( @args );
}
} # foreach
close FH;
$ldap->unbind;

$timestamp = localtime();
print "Number of hits: $count\n";
print LOGZ "$timestamp\tChecking completed\n";
close LOGZ;

sub dup_search {

my $addn = @_[0];
my $admail = @_[1];
my $displayname = @_[2];
my $cn = @_[3];
print "$addn $admail\n";
my @matchdns;
my $nldap = Net::LDAP->new($adHost) or die $!;
my $nmesg = $nldap->bind($adUsr, password =>$adPass);
my $nquery = "(mail=$admail)";
my @nattrs = qw(mail displayname cn);
my $npage = Net::LDAP::Control::Paged->new( size => 1000 ) or die $!;

my @nargs = ( base => $contactbase,
scope => 'sub',
filter => $nquery,
attrs => \@nattrs,
control => [ $npage ],
);

my $ncookie;

while (1) {
$nmesg = $nldap->search ( @nargs ) or die $!;
while (my $nentry = $nmesg->shift_entry()) {
my $nentrydn = $nentry->dn();
my $ndisplayname = $nentry->get_value('displayname');
my $acn = $nentry->get_value('cn');
push @matchdns, $nentrydn;
}
my ($nresp) = $nmesg->control(LDAP_CONTROL_PAGED) or last;
$ncookie = $nresp->cookie or last;
$npage->cookie($ncookie);
}
if ($ncookie) {
$npage->cookie($ncookie);
$npage->size(0);
$nldap->search( @nargs );
}
return @matchdns;
$nldap->unbind;
}

__END__;

DST Problem on OIM Release 3 Install

Last couple of days was frustrated in trying to install Oracle Identity Management Release 3 in a VM on my work machine. Since at the OS level it's identical to my home system, I figured there'd be no problem following my expert install procedure (written during my last install on March 9 of this year).

Ha! Did 3 abortive installs, with a trip home in between to re-download and re-burn the CDs (and copy them from disk mounted inside the VM -- I try to be kind to my fellow employees in the bandwidth poor sales branch I work out of), all with the result that dbconsole failed to come up during setup. On the fourth attempt I let the installer complete and then tried starting dbconsole. Clocked.

Damn. DST. Freaking H.R. 6, the "Energy Policy Act of 2005". Signed into law on August 8, 2005. Thank you SO much 109th Congress for wasting my time and my employer's money by changing a 20 year old standard that's been burned into every damn computer system on the planet.

So I shut everything down, grabbed patches 5865568 and 5632264 (READ THE DIRECTIONS!), did the "opatch apply" thing for each and, of course, everything now works.

I'm still waiting for someone to explain to me why it took almost every commercial software vendor two freaking years to come out with patches incorporating the shift from April 1 to March 11 into their products, and why any of them still have bits for download -- especially distribution disks -- that don't include it.

Morons.

For those of you out there who haven't been paying attention, below I've spelled out what the 2007 dates for DST would have been here in the U.S., and what the law now requires.

Would be: April 1 - October 28
Is now: March 11 - November 4

So if anyone out there thought they were clever and took the short cut of just manually advancing their clocks, they're going to be in for a surprise on April 1. And on October 28.

You have been warned.

Simple Paged Results with Net::LDAP

Not sure if I've posted on this before, but I wanted to capture it now. Useful for doing big searches against an Active Directory (default restriction on search results returned is 200). Using Net::LDAP.


#!/usr/bin/perl
use Net::LDAP;
use Net::LDAP::Entry;
use Net::LDAP::Control::Paged;
use Net::LDAP::Constant qw( LDAP_CONTROL_PAGED );

my $adHost = "detroit.domain.com"
my $adUsr = "cn=ldapadmin,cn=users,dc=company,dc=domain,dc=com";
my $adPass = "1234xyz";

my $base = "cn=users,dc=company,dc=domain,dc=com";
my $query = "(mail=*)";
my @attrs = qw(cn displayname mail);

my $ldap = Net::LDAP->new($adHost) or die $!;
my $mesg = $ldap->bind($adUsr, password =>$adPass);

my $page = Net::LDAP::Control::Paged->new( size => 1000 ) or die $!;

my @args = ( base => $base,
scope => 'sub',
filter => $query,
attrs => \@attrs,
control => [ $page ],
);

my $cookie;

while (1) {

$mesg = $ldap->search ( @args ) or die $!;

while (my $entry = $mesg->shift_entry()) {

my $entrydn = $entry->dn();
my $mail = $entry->get_value('mail');
my $displayname = $entry->get_value('displayname');
my $cn = $entry->get_value('cn');

print "\"$displayname\",\"$mail\"\n";

} # while


my ($resp) = $mesg->control(LDAP_CONTROL_PAGED) or last;
$cookie = $resp->cookie or last;
$page->cookie($cookie);

} # while (1)

if ($cookie) {
$page->cookie($cookie);
$page->size(0);
$ldap->search( @args );
}

$ldap->unbind;

Installing Oracle OIM_OIF 10g R3

... otherwise known as Oracle Identity Management 10g, Release 3 (10.1.4.0.1), which is a mouthful, even for an Oracle product.

Note: This article will be updated from time to time as I learn more about how these Oracle products fit together.

Previous versions of 10g Application Server included the Identity Management Infrastructure. With Release 3, it gets split off into it's own distribution on 2 separate CD's.

There are lots of little improvements that go into the new release, not the least of which is the update to database 10.1.0.5 and Application Server 10.1.2.0.2 that relieves you of the time-consuming task of patching up to these versions.

Installing is pretty straightforward. If you use CentOS 4 as I do, you'll need to modify the /etc/redhat-release file by deleting the line referring to CentOS and substituting "Red Hat Enterprise Linux AS release 4" so that the installer will not abort because your system isn't certified.

Assuming my install will be called "infra1", this is how I would lay out the filesystem:

Oracle Base /u01/app/orainfra
Oracle Inventory /u01/app/orainfra/oraInventory
Oracle Home /u01/app/orainfra/product/10.1.4
Database files /u02/oradata/infra1

(the db instance was named "infra1", which is also the SID)

Make sure to have root privileges on the server. There are a couple of scripts that need to be run as root during the install.

There may be kernel parameter changes that need to be done. These can be done dynamically using the sysctl utility, without rebooting the system. The Quick Install Guide has a nice section on how to do this under "Kernel Parameter Settings for OracleAS Metadata Repository".

Thanks to the Energy Policy Act of 2005, every system on the planet needs to be patched to account for DST coming early this year, March 11 to be exact.

Oracle is no exception, and even though it was only release a little while ago, Identity Manager R3 also requires patching.

There are two basic patches that are needed, both only available to customers with support contracts, via MetaLink:

  • 5865568, updates the Oracle JVM used by the App Server with the new timezone data.

  • 5632264, replaces the old timezone data for the database and database clients.

Ah, let the games begin!