Wednesday, January 28, 2009

KOHA LDAP Authentication

Previously I make some troubleshooting for this 1 college. They are using KOHA as their Library System. My tasks were to established authentication connection between KOHA & LDAP server. The tricky part where the user account at LDAP server using different ou(Organizational Unit) some. Current LDAP module (Auth-with-ldap.PM) bundled with KOHA unable to counter this issue. So I come out with new checkpw class. That to the following process:
  • it connects to LDAP using admin account(or account that can search for userid availablein LDAP server)
  • search base on user uid
  • retrive cn
  • compare password
  • then get the LDAP entry
  • and calls the memberadd if necessary
and here the perl code:
# this checkpw is a LDAP based one
# it connects to LDAP using admin account
# search base on user uid
# retrive cn
# compare password
# then get the LDAP entry
# and calls the memberadd if necessary
sub checkpw {
 use Net::LDAP;
 my ($dbh, $userid, $password, $authtype) = @_;
 if ($userid eq C4::Context->config('user') && $password eq C4::Context->config('pass')) {
  # KOHA superuser account
  return 2;
 }
 ################################edited by mbek for [email protected]###########################################
 ### LOCAL
 ### Change the code below to match your own LDAP server.
 ##################################################
 # LDAP connexion parameters
 # LDAP server
 my $ldapserver = '192.168.0.12'; #ur LDAP server's IP/hostname
 my $ldapadmin = 'ldapadminaccount';
 my $ldappassword = 'ldapadminpassword';
 # Base DN for users
 #my $name = "ou=users,dc=tow,dc=net";
 my $name = "cn=$ldapadmin,dc=sub,dc=domain,dc=edu,dc=my";
 my $searchbase = "dc=sub,dc=domain,dc=edu,dc=my";
 my $checkname = "ou=People,dc=sub,dc=domain,dc=edu,dc=my";
 # Bind uses the users full DN, if uid doesn't work try "cn"
 my $binddn = "$name";
 # my $binddn = "uid=$userid,$name";
 my $db = Net::LDAP->new( $ldapserver );
 # do bind
 my $res =$db->bind();
 # check connexion, anything other code than LDAP_SUCCESS (0)
 # is a problem
 if($res->code != 0 ) {
  # auth refused
  warn
  #die
  "LDAP Auth failed server not responding or wrong user password combination";
  return 0;
   
 }else {
  # search user
  my $userdnsearch = $db->search(
   base => "$searchbase",
   filter =>"(uid=$userid)",
  );
  my $userldapentry=$userdnsearch -> shift_entry;
  # build LDAP hash
  my %memberhash;
  my $x =$userldapentry->{asn}{attributes};
  my $key;
  foreach my $k ( @$x) {
   foreach my $k2 (keys %$k) {
    if ($k2 eq 'type') {
     $key = $$k{$k2};
    } else {
     my $a = @$k{$k2};
     foreach my $k3 (@$a) {
      $memberhash{$key} .= $k3."";
     }
    }
   }
  }
  $res = $db->unbind;
  my %borrower;
  if($memberhash{cn}){
   my $newldap = Net::LDAP->new( "$ldapserver" , timeout=>60) or die "[email protected]";
   $checkname = "cn=$memberhash{cn}," . $checkname;
   my $msg = $newldap->bind($checkname, password => $password);
   my $status = $msg->code;
   
   if($msg->code != 0 ) {
    warn "LDAP Auth failed server not responding or wrong user password combination"; 
     return 0; 
   }else{
    $msg = $newldap->search(
    base => "$searchbase",
    filter =>"(uid=$userid)",
    );
    my $userldapentry=$msg -> shift_entry;
    # build LDAP hash
    my %validhash;
    my $x =$userldapentry->{asn}{attributes};
    my $key;
    foreach my $k ( @$x) {
     foreach my $k2 (keys %$k) {
     if ($k2 eq 'type') {
      $key = $$k{$k2};
     } else {
      my $a = @$k{$k2};
      foreach my $k3 (@$a) {
       $validhash{$key} .= $k3." ";
      }
      }
     }
    }
    #
    # BUILD %borrower to CREATE or MODIFY BORROWER
    # change $memberhash{'xxx'} to fit your ldap structure.
    # check twice that mandatory fields are correctly filled
    #
    $borrower{cardnumber} = $userid;
    $borrower{firstname} = $validhash{givenName}; # MANDATORY FIELD
    $borrower{surname} = $validhash{sn}; # MANDATORY FIELD
    $borrower{initials} = substr($borrower{firstname},0,1).substr($borrower{surname},0,1)." "; # MANDATORY FIELD
    $borrower{streetaddress} = $validhash{homePostalAddress}." "; # MANDATORY FIELD
    $borrower{city} = $validhash{l}." "; # MANDATORY FIELD
    $borrower{phone} = $validhash{homePhone}." "; # MANDATORY FIELD
    $borrower{branchcode} = $validhash{businessCategory}; # MANDATORY FIELD
    $borrower{emailaddress} = $validhash{mail};
    $borrower{categorycode} = $validhash{employeeType};
   }
  }else{
   warn "LDAP Auth failed server not responding or wrong user password combination"; 
    return 0;
  }
  ################################End edited by mbek for [email protected]###########################################
  ##################################################
  ### /LOCAL
  ### No change needed after this line (unless there's a bug ;-) )
  ##################################################
  # check if borrower exists
  my $sth=$dbh->prepare("select password from borrowers where cardnumber=?");
  $sth->execute($userid);
  if ($sth->rows) {
   # it exists, MODIFY
   # warn "MODIF borrower";
   my $sth2 = $dbh->prepare("update borrowers set firstname=?,surname=?,initials=?,streetaddress=?,city=?,phone=?, categorycode=?,branchcode=?,emailaddress=?,sort1=? where cardnumber=?");
   $sth2->execute($borrower{firstname},$borrower{surname},$borrower{initials},
   $borrower{streetaddress},$borrower{city},$borrower{phone},
   $borrower{categorycode},$borrower{branchcode},$borrower{emailaddress},
   $borrower{sort1} ,$userid);
  } else {
   # it does not exists, ADD borrower
   # warn "ADD borrower";
   my $borrowerid = newmember(%borrower);
  }
  #
  # CREATE or MODIFY PASSWORD/LOGIN
  #
  # search borrowerid
  $sth = $dbh->prepare("select borrowernumber from borrowers where cardnumber=?");
  $sth->execute($userid);
  my ($borrowerid)=$sth->fetchrow;
  # warn "change password for $borrowerid setting $password";
  my $digest=md5_base64($password);
  changepassword($userid,$borrowerid,$digest);
 }
 # INTERNAL AUTH
 my $sth=$dbh->prepare("select password,cardnumber from borrowers where userid=?");
 $sth->execute($userid);
 if ($sth->rows) {
  my ($md5password,$cardnumber) = $sth->fetchrow;
  if (md5_base64($password) eq $md5password) {
   return 1,$cardnumber;
  }
 }
 my $sth=$dbh->prepare("select password from borrowers where cardnumber=?");
 $sth->execute($userid);
 if ($sth->rows) {
  my ($md5password) = $sth->fetchrow;
  if (md5_base64($password) eq $md5password) {
   return 1,$userid;
  }
 }
 return 0;
}

happy coding

**Gong Xi Fa Cai

1 comment:

Rajender Singh said...

Hi Hairul,

Can you send me your email id?

My email id is [email protected]

I need your expertise in one of the proposal.

Thanks & Regards,

Raj