Bug 8435: DBRev 3.13.00.038
[koha.git] / C4 / Auth_with_ldap.pm
index 0efeb95..3b40015 100644 (file)
@@ -19,13 +19,15 @@ package C4::Auth_with_ldap;
 
 use strict;
 #use warnings; FIXME - Bug 2505
-use Digest::MD5 qw(md5_base64);
+use Carp;
 
 use C4::Debug;
 use C4::Context;
 use C4::Members qw(AddMember changepassword);
 use C4::Members::Attributes;
 use C4::Members::AttributeTypes;
+use C4::Auth qw(checkpw_internal);
+use Koha::AuthUtils qw(hash_password);
 use List::MoreUtils qw( any );
 use Net::LDAP;
 use Net::LDAP::Filter;
@@ -110,26 +112,46 @@ sub checkpw_ldap {
        #$debug and $db->debug(5);
     my $userldapentry;
 
-  if ( $ldap->{auth_by_bind} ) {
-    # Perform an anonymous bind
-    my $res = $db->bind;
-    if ( $res->code ) {
-      $debug and warn "Anonymous LDAP bind failed: ". description($res);
-      return 0;
-    }
+    if ( $ldap->{auth_by_bind} ) {
+        my $principal_name;
+        if ( $ldap->{anonymous_bind} ) {
 
-    # Perform a LDAP search for the given username
-    my $search = search_method($db, $userid) or return 0;   # warnings are in the sub
-    $userldapentry = $search->shift_entry;
+            # Perform an anonymous bind
+            my $res = $db->bind;
+            if ( $res->code ) {
+                warn "Anonymous LDAP bind failed: " . description($res);
+                return 0;
+            }
 
-    # Perform a LDAP bind for the given username using the matched DN
-    $res = $db->bind( $userldapentry->dn, password => $password );
-    if ( $res->code ) {
-      $debug and warn "LDAP bind failed as kohauser $userid: ". description($res);
-      return 0;
-    }
+            # Perform a LDAP search for the given username
+            my $search = search_method( $db, $userid )
+              or return 0;    # warnings are in the sub
+            $userldapentry = $search->shift_entry;
+            $principal_name = $userldapentry->dn;
+        }
+        else {
+            $principal_name = $ldap->{principal_name};
+            if ( $principal_name and $principal_name =~ /\%/ ) {
+                $principal_name = sprintf( $principal_name, $userid );
+            }
+            else {
+                $principal_name = $userid;
+            }
+        }
 
-  } else {
+        # Perform a LDAP bind for the given username using the matched DN
+        my $res = $db->bind( $principal_name, password => $password );
+        if ( $res->code ) {
+            warn "LDAP bind failed as kohauser $userid: " . description($res);
+            return 0;
+        }
+        if ( !defined($userldapentry)
+            && ( $config{update} or $config{replicate} ) )
+        {
+            my $search = search_method( $db, $userid ) or return 0;
+            $userldapentry = $search->shift_entry;
+        }
+    } else {
                my $res = ($config{anonymous}) ? $db->bind : $db->bind($ldapname, password=>$ldappassword);
                if ($res->code) {               # connection refused
                        warn "LDAP bind failed as ldapuser " . ($ldapname || '[ANONYMOUS]') . ": " . description($res);
@@ -257,44 +279,43 @@ sub exists_local {
 }
 
 sub _do_changepassword {
-    my ($userid, $borrowerid, $digest) = @_;
+    my ($userid, $borrowerid, $password) = @_;
+
+    my $digest = hash_password($password);
+
     $debug and print STDERR "changing local password for borrowernumber=$borrowerid to '$digest'\n";
     changepassword($userid, $borrowerid, $digest);
 
-       # Confirm changes
-       my $sth = C4::Context->dbh->prepare("SELECT password,cardnumber FROM borrowers WHERE borrowernumber=? ");
-       $sth->execute($borrowerid);
-       if ($sth->rows) {
-               my ($md5password, $cardnum) = $sth->fetchrow;
-        ($digest eq $md5password) and return $cardnum;
-               warn "Password mismatch after update to cardnumber=$cardnum (borrowernumber=$borrowerid)";
-               return;
-       }
-       die "Unexpected error after password update to userid/borrowernumber: $userid / $borrowerid.";
+    my ($ok, $cardnum) = checkpw_internal(C4::Context->dbh, $userid, $password);
+    return $cardnum if $ok;
+
+    warn "Password mismatch after update to borrowernumber=$borrowerid";
+    return;
 }
 
 sub update_local {
-       my   $userid   = shift             or return;
-       my   $digest   = md5_base64(shift) or return;
-       my $borrowerid = shift             or return;
-       my $borrower   = shift             or return;
-       my @keys = keys %$borrower;
-       my $dbh = C4::Context->dbh;
-       my $query = "UPDATE  borrowers\nSET     " . 
-               join(',', map {"$_=?"} @keys) .
-               "\nWHERE   borrowernumber=? "; 
-       my $sth = $dbh->prepare($query);
-       if ($debug) {
-               print STDERR $query, "\n",
-                       join "\n", map {"$_ = '" . $borrower->{$_} . "'"} @keys;
-               print STDERR "\nuserid = $userid\n";
-       }
-       $sth->execute(
-               ((map {$borrower->{$_}} @keys), $borrowerid)
-       );
+    my $userid     = shift or croak "No userid";
+    my $password   = shift or croak "No password";
+    my $borrowerid = shift or croak "No borrowerid";
+    my $borrower   = shift or croak "No borrower record";
+
+    my @keys = keys %$borrower;
+    my $dbh = C4::Context->dbh;
+    my $query = "UPDATE  borrowers\nSET     " .
+        join(',', map {"$_=?"} @keys) .
+        "\nWHERE   borrowernumber=? ";
+    my $sth = $dbh->prepare($query);
+    if ($debug) {
+        print STDERR $query, "\n",
+            join "\n", map {"$_ = '" . $borrower->{$_} . "'"} @keys;
+        print STDERR "\nuserid = $userid\n";
+    }
+    $sth->execute(
+        ((map {$borrower->{$_}} @keys), $borrowerid)
+    );
 
-       # MODIFY PASSWORD/LOGIN
-       _do_changepassword($userid, $borrowerid, $digest);
+    # MODIFY PASSWORD/LOGIN
+    _do_changepassword($userid, $borrowerid, $password);
 }
 
 1;
@@ -414,6 +435,12 @@ Example XML stanza for LDAP configuration in KOHA_CONF.
     <update>1</update>             <!-- update existing users in Koha database -->
     <auth_by_bind>0</auth_by_bind> <!-- set to 1 to authenticate by binding instead of
                                         password comparison, e.g., to use Active Directory -->
+    <anonymous_bind>0</anonymous_bind> <!-- set to 1 if users should be searched using
+                                            an anonymous bind, even when auth_by_bind is on -->
+    <principal_name>%s@my_domain.com</principal_name>
+                                   <!-- optional, for auth_by_bind: a printf format to make userPrincipalName from koha userid.
+                                        Not used with anonymous_bind. -->
+
     <mapping>                  <!-- match koha SQL field names to your LDAP record field names -->
       <firstname    is="givenname"      ></firstname>
       <surname      is="sn"             ></surname>