Bug 7973- Allow for new type of LDAP authentication
[koha.git] / C4 / Auth_with_ldap.pm
index 7f56d9c..d29d8e8 100644 (file)
@@ -35,7 +35,7 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug);
 
 BEGIN {
        require Exporter;
-       $VERSION = 3.10;        # set the version for version checking
+    $VERSION = 3.07.00.049;    # set the version for version checking
        @ISA    = qw(Exporter);
        @EXPORT = qw( checkpw_ldap );
 }
@@ -47,7 +47,7 @@ BEGIN {
 # ~ then gets the LDAP entry
 # ~ and calls the memberadd if necessary
 
-sub ldapserver_error ($) {
+sub ldapserver_error {
        return sprintf('No ldapserver "%s" defined in KOHA_CONF: ' . $ENV{KOHA_CONF}, shift);
 }
 
@@ -70,8 +70,8 @@ my %config = (
        update => defined($ldap->{update}   ) ? $ldap->{update}    : 1,  # update from LDAP to Koha database for existing user
 );
 
-sub description ($) {
-       my $result = shift or return undef;
+sub description {
+       my $result = shift or return;
        return "LDAP error #" . $result->code
                        . ": " . $result->error_name . "\n"
                        . "# " . $result->error_text . "\n";
@@ -105,30 +105,27 @@ sub checkpw_ldap {
     my $db = Net::LDAP->new(\@hosts);
        #$debug and $db->debug(5);
     my $userldapentry;
-       if ( $ldap->{auth_by_bind} ) {
-        my $principal_name = $ldap->{principal_name};
-        if ($principal_name and $principal_name =~ /\%/) {
-            $principal_name = sprintf($principal_name,$userid);
-        } else {
-            $principal_name = $userid;
-        }
-               my $res = $db->bind( $principal_name, password => $password );
-        if ( $res->code ) {
-            $debug and warn "LDAP bind failed as kohauser $principal_name: ". description($res);
-            return 0;
-        }
 
-       # FIXME dpavlin -- we really need $userldapentry leater on even if using auth_by_bind!
+  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;
+    }
+
+    # 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;
 
-       # BUG #5094
-       # 2010-08-04 JeremyC
-       # a $userldapentry is only needed if either updating or replicating are enabled
-       if($config{update} or $config{replicate}) {
-           my $search = search_method($db, $userid) or return 0;   # warnings are in the sub
-           $userldapentry = $search->shift_entry;
-       }
+    # Perform a LDAP bind for the given username using the matched DN
+    my $res = $db->bind( $userldapentry->dn, password => $password );
+    if ( $res->code ) {
+      $debug and warn "LDAP bind failed as kohauser $userid: ". description($res);
+      return 0;
+    }
 
-       } else {
+  } 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);
@@ -169,11 +166,13 @@ sub checkpw_ldap {
         return 0;   # B2, D2
     }
        if (C4::Context->preference('ExtendedPatronAttributes') && $borrowernumber && ($config{update} ||$config{replicate})) {
-               my @types = C4::Members::AttributeTypes::GetAttributeTypes();
-               my @attributes = grep{my $key=$_; any{$_ eq $key}@types;} keys %borrower;
         my $extended_patron_attributes;
-        @{$extended_patron_attributes} =
-          map { { code => $_, value => $borrower{$_} } } @attributes;
+        foreach my $attribute_type ( C4::Members::AttributeTypes::GetAttributeTypes() ) {
+                my $code = $attribute_type->{code};
+                if ( exists($borrower{$code}) && $borrower{$code} !~ m/^\s*$/ ) { # skip empty values
+                        push @$extended_patron_attributes, { code => $code, value => $borrower{$code} };
+                }
+        }
                my @errors;
                #Check before add
                for (my $i; $i< scalar(@$extended_patron_attributes)-1;$i++) {
@@ -197,7 +196,7 @@ return(1, $cardnumber, $userid);
 # Edit KOHA_CONF so $memberhash{'xxx'} fits your ldap structure.
 # Ensure that mandatory fields are correctly filled!
 #
-sub ldap_entry_2_hash ($$) {
+sub ldap_entry_2_hash {
        my $userldapentry = shift;
        my %borrower = ( cardnumber => shift );
        my %memberhash;
@@ -209,7 +208,7 @@ sub ldap_entry_2_hash ($$) {
                        hashdump("LDAP key: ",$userldapentry->{$_});
                }
        }
-       my $x = $userldapentry->{attrs} or return undef;
+       my $x = $userldapentry->{attrs} or return;
        foreach (keys %$x) {
                $memberhash{$_} = join ' ', @{$x->{$_}};        
                $debug and print STDERR sprintf("building \$memberhash{%s} = ", $_, join(' ', @{$x->{$_}})), "\n";
@@ -228,10 +227,21 @@ sub ldap_entry_2_hash ($$) {
                ( substr($borrower{'firstname'},0,1)
                . substr($borrower{ 'surname' },0,1)
                . " ");
+
+       # check if categorycode exists, if not, fallback to default from koha-conf.xml
+       my $dbh = C4::Context->dbh;
+       my $sth = $dbh->prepare("SELECT categorycode FROM categories WHERE categorycode = ?");
+       $sth->execute( uc($borrower{'categorycode'}) );
+       unless ( my $row = $sth->fetchrow_hashref ) {
+               my $default = $mapping{'categorycode'}->{content};
+               $debug && warn "Can't find ", $borrower{'categorycode'}, " default to: $default for ", $borrower{userid};
+               $borrower{'categorycode'} = $default
+       }
+
        return %borrower;
 }
 
-sub exists_local($) {
+sub exists_local {
        my $arg = shift;
        my $dbh = C4::Context->dbh;
        my $select = "SELECT borrowernumber,cardnumber,userid,password FROM borrowers ";
@@ -260,16 +270,16 @@ sub _do_changepassword {
                my ($md5password, $cardnum) = $sth->fetchrow;
         ($digest eq $md5password) and return $cardnum;
                warn "Password mismatch after update to cardnumber=$cardnum (borrowernumber=$borrowerid)";
-               return undef;
+               return;
        }
        die "Unexpected error after password update to userid/borrowernumber: $userid / $borrowerid.";
 }
 
-sub update_local($$$$) {
-       my   $userid   = shift             or return undef;
-       my   $digest   = md5_base64(shift) or return undef;
-       my $borrowerid = shift             or return undef;
-       my $borrower   = shift             or return undef;
+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     " . 
@@ -356,7 +366,8 @@ C4::Auth - Authenticates Koha users
                | dateexpiry          | date         | YES  |     | NULL    |                |
                | gonenoaddress       | tinyint(1)   | YES  |     | NULL    |                |
                | lost                | tinyint(1)   | YES  |     | NULL    |                |
-               | debarred            | tinyint(1)   | YES  |     | NULL    |                |
+               | debarred            | date         | YES  |     | NULL    |                |
+               | debarredcomment     | varchar(255) | YES  |     | NULL    |                |
                | contactname         | mediumtext   | YES  |     | NULL    |                |
                | contactfirstname    | text         | YES  |     | NULL    |                |
                | contacttitle        | text         | YES  |     | NULL    |                |
@@ -405,8 +416,6 @@ 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 -->
-    <principal_name>%s@my_domain.com</principal_name>
-                                   <!-- optional, for auth_by_bind: a printf format to make userPrincipalName from koha userid -->
     <mapping>                  <!-- match koha SQL field names to your LDAP record field names -->
       <firstname    is="givenname"      ></firstname>
       <surname      is="sn"             ></surname>