LDAP script

[email protected] ("Iglesias, Edward G. (Library)")
Newsgroups perl.perl4lib
Message-ID <[email protected]>
Hello All,

I have been given the task of customizing a script from III (included below) to make it hit multiple LDAP servers.  The issue is that I want to insert some logic so that it only hits the right server.  The input is going to come from an html page with a dropdown menu that will pass [email protected].  We have 4 possible domains (ccsu.edu, scsu.edu etc...)  What I think I need is something that will read the incoming string and say if ccsu.edu go to ldap.ccsu.edu.  I am just not sure where to insert this and what effect it will have on the rest of the script.  

Has anyone got such a script stowed away somewhere they would be willing to share?

Thanks,
~~~~~~~~~~~~~
Edward Iglesias
Systems Librarian
Central Connecticut State University



#!/iiidb/software/tpp/perl/bin/perl -w

use Net::LDAP;
use Net::LDAPS;
use strict;
use CGI qw(unescape);

#
#-----------------------------------------------------------------------
# This perl script is provided as an example for libraries that want to
# employ Innovative's "plugin" model for External Patron Verification.
#
# The example script queries multiple LDAP servers depending on the type 
# of user, e.g. staff/faculty vs. student; campus1 vs. campus2; etc.
#
# This script is not intended to be "standalone" but is instead called by 
# the Innovative checkLDAP script.
#
# Innovative provides this example script as a courtesy to libraries that
# have multiple LDAP servers.  The library is responsible for customizing 
# this script with local values, enhanced functionality, etc. 
#-----------------------------------------------------------------------
#

my $debug = "yes";

open (DEBUG, ">>/tmp/checkLDAP_dlevy.log") if ($debug eq "yes");

#
# LDAP connection information is stored in arrays of size 2.
# The script assumes that the first location (0) in the array 
# contains the connection information for the student LDAP server, 
# i.e. the input extpatserver = student, if  extpatserver = staff the
# script uses the connection information from the second location (1)
# in the following arrays.
# If there is no value in extpatserver, the script queryies the student
# LDAP server.
# These arrays should be initialized here with the real values.
# To customize this script for you system, configure the following 
# arrays:
#
# m_sLDAPServer : contains the host domain name of the LDAP servers.
#
# m_nPort :  specifies the port used to connect to the LDAP server.
#
# m_bUseLDAPS : set to 1 if you use a secure connection i.e. port 636, 
# 		set to 0 if you use a non-secure connection.
# m_sBindBase : contains a string that defines which database to use 
# 		on the LDAP server on the first bind command; 
#		if empty use anonymous bind.
#
# m_sBindPassword : contains the password of the administrator account 
#	      	   you use to bind. Set the password unencrypted, 
#		   i.e. use the string you received from the LDAP 
#		   adminstartor as it is.  
# m_sBindUser : set the login of the administartor account, or an empty string
#
# m_bUseOneBind: set to 1 if you use user's credentials to bind.
#
# m_sSearchAttribute : contains the primary search attribute to be used 
#		   (university ID, for example) when searching for 
#		   a given patron on the LDAP server.
#
# m_sSearchBase : contains the search DN used to retrieve user records.
#
# m_sIDAttribute : contains the attribute in the data returned by the LDAP
#                  server which is used as the patron search key on the
#		   Millennium server.	
#

my @m_sLDAPServer = ("ldap.ccsu.edu","");
my @m_nPort = ("636","");
my @m_bUseLDAPS = (1,0);
my @m_sBindBase = ("BIND_BASE= dn=ccsu,dc=edu","");
my @m_sBindPassword = ("","");
my @m_sBindUser = ("","");
my @m_bUseOneBind = (1,0);
my @m_sSearchAttribute = ("USERPrincipalName","");
#This is proboably going to need some work
my @m_sSearchBase = ("search_base =, DC=ccsu,DC=edu","");
my @m_sIDAttribute = ("","");


my $m_bUseLDAPPassword = 1;
my $m_bTryMillenniumAfterBadVerify = 0;

my @m_nTimeOut = (3,3);
my $m_sLDAPVersion = 3;

#
# Input variables are stored in teh following variables
#
my $m_sUserName = "";
my $m_sUserId = "";
my $m_sPassword = "";
my $m_sServer= "";

my $m_hLDAP;
my $m_whichServer = 0; #Identifies which server to query by index, either 0 or 1
my $m_hSearchMessage;
my $m_bVerbose = 0;
#my $m_bVerbose = 1;
my $m_nTimeOut = 3;


my $m_sOriginalUserId;
my $m_sResult = "Failed";
my $m_sLogMessage = "";
my $m_sIIIDB = "";

#------------------------------------------------------------
# log a message for each verification to $IIIDB/errlog/ldap.log
# append if is used to wrap log so it doesn't grow to big
#------------------------------------------------------------
sub logger
{
my $sMessage = shift;

my $sOutName = $m_sIIIDB . "/errlog/ldap.log";

if( ( -e "$sOutName" ) && ( -s "$sOutName" ) > 100000 )
    {
    rename( "$sOutName", "$sOutName.old" );
    system( "touch $sOutName" );
    }

open( OUT, ">>$sOutName" ) ||
    die( "Cannot appendf $sOutName\n" );

my ($nSec,$nMin,$nHr,$nDay,$nMonth,$nYear,$sRest) = localtime();

my $sDenseTime = sprintf "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d",
                $nYear + 1900, $nMonth + 1, $nDay, $nHr, $nMin, $nSec;
print OUT "$sDenseTime:$m_sOriginalUserId:$m_sUserId" .
          ":$m_sLogMessage:$sMessage\n";
if( $m_bVerbose )
    {
    print "$sDenseTime:$m_sOriginalUserId:$m_sUserId" .
              ":$m_sLogMessage:$sMessage\n";
    }
close( OUT );
}
#----------------------------------------------------------------
#
# Innovative checkLDAP script
# The Innovative Perl script sends the following name/value pairs
# via STDIN to this plugin script:
#
#    extpatid= is the patron ID number.
#
#    extpatpw= is the patron password.
#
#    extpatserver= indicates the patron's choice of 
#    "student" or "staff"
#
#----------------------------------------------------------------
sub parseInput
{
while( <> )
        {
        chomp;

        for( split /\&/ )
                {
                if( /^extpatname=(.*)/ )
                        {
                        $m_sUserName = unescape($1);
                        }
                elsif( /^extpatid=(.*)/ )
                        {
                        $m_sUserId = unescape($1);
                        }
                elsif( /^extpatpw=(.*)/ )
                        {
                        $m_sPassword = unescape($1);
                        }
                elsif( /^extpatserver=(.*)/ )
                        {
                        $m_sServer= unescape($1);
                        }
                }
        }
}
#---------------------------------------------------------------
#
# Open a connection to the LDAP server.
#
#---------------------------------------------------------------
sub doConnect
{
my      @aLDAPArgs;

push( @aLDAPArgs, $m_sLDAPServer[$m_whichServer] );
push( @aLDAPArgs, port => $m_nPort[$m_whichServer] );


if( $m_bUseLDAPS[$m_whichServer] )
    {
      $m_hLDAP = new Net::LDAPS( @aLDAPArgs ) ||
                 systemError( "Cannot create session, args=@aLDAPArgs" );
     }

else
    {

    $m_hLDAP = Net::LDAP->new( @aLDAPArgs ) ||
               systemError( "Cannot create session, args=@aLDAPArgs" );
     }

if( ! defined( $m_hLDAP ) )
     {
       systemError( "Bad return from LDAP->new with args=@aLDAPArgs" );
      }

if( $m_bVerbose )
        {
        print "LDAP connection established args=@aLDAPArgs\n";
        }

return 1;
}
#------------------------------------------------------------
sub doBind
{
#expects the following parameters
my $sBindBase = shift;
my $sBindUser = shift;
my $sBindPassword = shift;
my $sMessage = shift;
my $bReturnOnVerifyError = shift;

my @aBindArgs;
my $mesg;

if( $sBindBase ne "" )
    {
    push( @aBindArgs, dn => $sBindBase ) ;
    }
if( $sBindPassword ne "" )
    {
    push( @aBindArgs, password => $sBindPassword );
    }
if( $m_sLDAPVersion ne "" )
    {
    push( @aBindArgs, version => $m_sLDAPVersion );
    }

$mesg = $m_hLDAP->bind( @aBindArgs ) ||
     systemError("Failed to bind args=dn $sBindBase version $m_sLDAPVersion");

if( $m_bVerbose )
    {
    print "Bindargs = @aBindArgs\n";
    }

if( $mesg->code() )
    {
    if( $bReturnOnVerifyError )
        {
        return( 0 );
        }
    else
        {
        verifyError( "$sMessage:bind failed:mesg=" .
          "(". $mesg->code() . "),args=dn $sBindBase version $m_sLDAPVersion" );
        exit( 0 );
        }
    }
if( $m_bVerbose )
    {
    print "Bind succeeded\n";
    }
return( 1 );
}

#------------------------------------------------------------
# performs a search, if the count is not 1 and there exists
# a fallback search method try that.
#------------------------------------------------------------

sub doSearch
{
my @aSearchArgs;

push( @aSearchArgs, base => $m_sSearchBase[$m_whichServer] );
my $sFilter = "$m_sSearchAttribute[$m_whichServer]=$m_sUserId";
push( @aSearchArgs, filter => "$sFilter" );

alarm $m_nTimeOut;

$m_hSearchMessage = $m_hLDAP->search( @aSearchArgs ) ||
                     systemError ("Failed on search args=@aSearchArgs");


alarm 0;

if( $m_hSearchMessage->code() )
    {
    verifyError( "search failed mesg=" .
                 "(". $m_hSearchMessage->code() . "),args=@aSearchArgs" );
    return 0;
    }

if( $m_bVerbose )
    {
    printf( "Search done: args=@aSearchArgs\n" );
    }

my @aList = $m_hSearchMessage->all_entries;

my $nCount = $#aList + 1;

if( $m_bVerbose ) { print "Search found $nCount matching patrons\n"; }

return $nCount;
}

#------------------------------------------------------------
sub checkPassword
{
my $hEntry = shift;

if( $m_bUseLDAPPassword == 0 ) { return 1; }

my $sVerifyUserName = $m_sUserId;

if( ! doBind( $hEntry->dn(),
        $sVerifyUserName,
        $m_sPassword,
        "Bad password",
         $m_bTryMillenniumAfterBadVerify ) )
    {
    return( 0 );
    }

$m_sLogMessage .= "good password ";

if( $m_bVerbose )
    {
    print "Password check succeeded\n";
    }
return( 1 );
}

#------------------------------------------------------------
# verification has failed log and exit with string Error
#------------------------------------------------------------
sub verifyError
{
my $sMessage = shift;

$m_sUserId = "Error"; # this is the string we will return to the webpac
if( defined $m_hLDAP )
    {
    $m_hLDAP->unbind;
    }
logger( $sMessage );

print "extvererr=$sMessage\n";
}

#------------------------------------------------------------
# log an error message to the syserr file
#------------------------------------------------------------
sub logError
{
my $sMessage = shift;
my $sSyserr = $m_sIIIDB . "/errlog/syserr";
if( open( SYSERR, ">>$sSyserr" ) )
    {
    print SYSERR "$sMessage\n";
    close( SYSERR );
    }
}

#------------------------------------------------------------
# log an error message to the syserr file and exit with Error
#------------------------------------------------------------
sub systemError
{
my $sMessage = shift;

$m_sUserId = "Error"; # this is the string we will return to the webpac
if( defined $m_hLDAP )
    {
    $m_hLDAP->unbind;
    }
logger( "$sMessage ($!)" );
logError( "$sMessage ($!)" );

print "extsyserr=$sMessage\n";
exit 1;
}


#---------------------------------------------------------------
sub cleanUpAndExit
{
$m_hLDAP->unbind; # take down session
exit 0;
}
#---------------------------------------------------------------

sub notFound
{
my $sMessage = shift;

if( ! defined( $sMessage ) ) { $sMessage = "no message"; }
print "extvererr=$sMessage\n";
cleanUpAndExit();
}

#------------------------------------------------------------
# catches alarm, we set alarm so we don't wait forever for the ldap server
#------------------------------------------------------------
sub catchAlarm
{
logError( "Timeout occurred" );
}

#------------------------------------------------------------
#
#   main() starts here
#
# it is CRITICAL that nothing read stdin that will be done by
# parseInput after the globals are loaded and a user script
# has been ruled out
#
#------------------------------------------------------------
$SIG{ALRM} = \&catchAlarm;

parseInput();

 print DEBUG "The userID entered is: $m_sUserId\n" if ($debug eq "yes");


$m_sOriginalUserId = $m_sUserId;
if( $m_sPassword eq "" )
        {
        verifyError( "password not specified" );
        exit( 0 );
        }

if( $m_sUserId eq "" )
        {
        verifyError( "got empty lookup field" );
        exit( 0 );
        }

if( $m_sServer eq "staff" )
    {
	$m_whichServer = 1;
    }
else
	{
	$m_whichServer = 0;
	}

if( ! doConnect() )
    {
    exit 0;
    }

if( $m_bUseOneBind[$m_whichServer] )
    {
    my $sTempUserName = $m_sUserId;
	}


if( ! doBind($m_sBindBase[$m_whichServer], $m_sBindUser[$m_whichServer], 
     $m_sBindPassword[$m_whichServer] ,"",$m_bUseOneBind[$m_whichServer] ))
    {
    notFound( "no bind" );
    }

my $nMatchCount = doSearch();

if( $nMatchCount == 0 )
    {
    $m_sLogMessage .= "not on LDAP, ";
    notFound( "not found" );
    }
elsif( $nMatchCount == 1 )
    {
    $m_sLogMessage .= "on LDAP ";

    my @aList = $m_hSearchMessage->all_entries;
    my $hEntry = pop @aList;

    if( $m_bVerbose )
        {
        print "Entry dn= " . $hEntry->dn() . "\n";
        $hEntry->dump;
        }

    if( ! $m_bUseOneBind[$m_whichServer] && ! checkPassword( $hEntry ) )
        {
        $m_sLogMessage .= "failed password on LDAP, ";
        notFound( "failed password" );
        }
    else
        {
        my @sID = $hEntry->get_value( $m_sIDAttribute[$m_whichServer] );
        my $sID;

        if( ! @sID )
        {
        verifyError("Could not find attribute $m_sIDAttribute[$m_whichServer]");
            notFound( "no attribute" );
        }

        $sID = $sID[0];
        if( ! defined( $sID ) || $sID eq "" )
        {
        verifyError("Could not get attribute $m_sIDAttribute[$m_whichServer]");
            notFound( "no attribute" );
        }

        $m_sUserId = $sID;
        }
    }
else
    {
    $m_sLogMessage .= "many on LDAP ";

    verifyError( "$m_sUserId had $nMatchCount matches on LDAP server" );
    }

logger( "OK" );

print "extid=$m_sUserId\n";

cleanUpAndExit();

1;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.