Setting up a system preference to choose whether CGI::Session uses mysql, or
[koha_fer] / C4 / Auth.pm
1 # -*- tab-width: 8 -*-
2 # NOTE: This file uses 8-character tabs; do not change the tab size!
3
4 package C4::Auth;
5
6 # Copyright 2000-2002 Katipo Communications
7 #
8 # This file is part of Koha.
9 #
10 # Koha is free software; you can redistribute it and/or modify it under the
11 # terms of the GNU General Public License as published by the Free Software
12 # Foundation; either version 2 of the License, or (at your option) any later
13 # version.
14 #
15 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
16 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License along with
20 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
21 # Suite 330, Boston, MA  02111-1307 USA
22
23 use strict;
24 use Digest::MD5 qw(md5_base64);
25 use CGI::Session;
26
27
28 require Exporter;
29 use C4::Context;
30 use C4::Output;    # to get the template
31 use C4::Members;
32 use C4::Koha;
33 use C4::Branch; # GetBranches
34
35 # use utf8;
36 # use Net::LDAP;
37 # use Net::LDAP qw(:all);
38
39 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
40
41 # set the version for version checking
42 $VERSION = do { my @v = '$Revision$' =~ /\d+/g;
43     shift(@v) . "." . join( "_", map { sprintf "%03d", $_ } @v );
44 };
45
46 =head1 NAME
47
48 C4::Auth - Authenticates Koha users
49
50 =head1 SYNOPSIS
51
52   use CGI;
53   use C4::Auth;
54
55   my $query = new CGI;
56
57   my ($template, $borrowernumber, $cookie) 
58     = get_template_and_user(
59         {
60             template_name   => "opac-main.tmpl",
61             query           => $query,
62       type            => "opac",
63       authnotrequired => 1,
64       flagsrequired   => {borrow => 1},
65   }
66     );
67
68   print $query->header(
69     -type => 'utf-8',
70     -cookie => $cookie
71   ), $template->output;
72
73
74 =head1 DESCRIPTION
75
76     The main function of this module is to provide
77     authentification. However the get_template_and_user function has
78     been provided so that a users login information is passed along
79     automatically. This gets loaded into the template.
80
81 =head1 FUNCTIONS
82
83 =over 2
84
85 =cut
86
87 @ISA    = qw(Exporter);
88 @EXPORT = qw(
89   &checkauth
90   &get_template_and_user
91 );
92
93 =item get_template_and_user
94
95   my ($template, $borrowernumber, $cookie)
96     = get_template_and_user(
97         {
98            template_name   => "opac-main.tmpl",
99            query           => $query,
100      type            => "opac",
101      authnotrequired => 1,
102      flagsrequired   => {borrow => 1},
103   }
104     );
105
106     This call passes the C<query>, C<flagsrequired> and C<authnotrequired>
107     to C<&checkauth> (in this module) to perform authentification.
108     See C<&checkauth> for an explanation of these parameters.
109
110     The C<template_name> is then used to find the correct template for
111     the page. The authenticated users details are loaded onto the
112     template in the HTML::Template LOOP variable C<USER_INFO>. Also the
113     C<sessionID> is passed to the template. This can be used in templates
114     if cookies are disabled. It needs to be put as and input to every
115     authenticated page.
116
117     More information on the C<gettemplate> sub can be found in the
118     Output.pm module.
119
120 =cut
121
122 sub get_template_and_user {
123     my $in       = shift;
124     my $template =
125       gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'} );
126     my ( $user, $cookie, $sessionID, $flags ) = checkauth(
127         $in->{'query'},
128         $in->{'authnotrequired'},
129         $in->{'flagsrequired'},
130         $in->{'type'}
131     ) unless ($in->{'template_name'}=~/maintenance/);
132
133     my $borrowernumber;
134     my $insecure = C4::Context->preference('insecure');
135     if ($user or $insecure) {
136
137                 # load the template variables for stylesheets and JavaScript
138                 $template->param( css_libs => $in->{'css_libs'} );
139                 $template->param( css_module => $in->{'css_module'} );
140                 $template->param( css_page => $in->{'css_page'} );
141                 $template->param( css_widgets => $in->{'css_widgets'} );
142
143         $template->param( js_libs => $in->{'js_libs'} );
144         $template->param( js_module => $in->{'js_module'} );
145         $template->param( js_page => $in->{'js_page'} );
146         $template->param( js_widgets => $in->{'js_widgets'} );
147
148                 # user info
149         $template->param( loggedinusername => $user );
150         $template->param( sessionID        => $sessionID );
151
152         $borrowernumber = getborrowernumber($user);
153         my ( $borr, $alternativeflags ) =
154           GetMemberDetails( $borrowernumber );
155         my @bordat;
156         $bordat[0] = $borr;
157         $template->param( "USER_INFO" => \@bordat );
158
159         # We are going to use the $flags returned by checkauth
160         # to create the template's parameters that will indicate
161         # which menus the user can access.
162         if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) {
163             $template->param( CAN_user_circulate        => 1 );
164             $template->param( CAN_user_catalogue        => 1 );
165             $template->param( CAN_user_parameters       => 1 );
166             $template->param( CAN_user_borrowers        => 1 );
167             $template->param( CAN_user_permission       => 1 );
168             $template->param( CAN_user_reserveforothers => 1 );
169             $template->param( CAN_user_borrow           => 1 );
170             $template->param( CAN_user_editcatalogue    => 1 );
171             $template->param( CAN_user_updatecharge     => 1 );
172             $template->param( CAN_user_acquisition      => 1 );
173             $template->param( CAN_user_management       => 1 );
174             $template->param( CAN_user_tools            => 1 ); 
175             $template->param( CAN_user_editauthorities  => 1 );
176             $template->param( CAN_user_serials          => 1 );
177             $template->param( CAN_user_reports          => 1 );
178         }
179
180         if ( $flags && $flags->{circulate} == 1 ) {
181             $template->param( CAN_user_circulate => 1 );
182         }
183
184         if ( $flags && $flags->{catalogue} == 1 ) {
185             $template->param( CAN_user_catalogue => 1 );
186         }
187
188         if ( $flags && $flags->{parameters} == 1 ) {
189             $template->param( CAN_user_parameters => 1 );
190             $template->param( CAN_user_management => 1 );
191         }
192
193         if ( $flags && $flags->{borrowers} == 1 ) {
194             $template->param( CAN_user_borrowers => 1 );
195         }
196
197         if ( $flags && $flags->{permissions} == 1 ) {
198             $template->param( CAN_user_permission => 1 );
199         }
200
201         if ( $flags && $flags->{reserveforothers} == 1 ) {
202             $template->param( CAN_user_reserveforothers => 1 );
203         }
204
205         if ( $flags && $flags->{borrow} == 1 ) {
206             $template->param( CAN_user_borrow => 1 );
207         }
208
209         if ( $flags && $flags->{editcatalogue} == 1 ) {
210             $template->param( CAN_user_editcatalogue => 1 );
211         }
212
213         if ( $flags && $flags->{updatecharges} == 1 ) {
214             $template->param( CAN_user_updatecharge => 1 );
215         }
216
217         if ( $flags && $flags->{acquisition} == 1 ) {
218             $template->param( CAN_user_acquisition => 1 );
219         }
220
221         if ( $flags && $flags->{tools} == 1 ) {
222             $template->param( CAN_user_tools => 1 );
223         }
224   
225         if ( $flags && $flags->{editauthorities} == 1 ) {
226             $template->param( CAN_user_editauthorities => 1 );
227         }
228     
229         if ( $flags && $flags->{serials} == 1 ) {
230             $template->param( CAN_user_serials => 1 );
231         }
232
233         if ( $flags && $flags->{reports} == 1 ) {
234             $template->param( CAN_user_reports => 1 );
235         }
236     }
237     if ( $in->{'type'} eq "intranet" ) {
238         $template->param(
239             intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
240             intranetstylesheet => C4::Context->preference("intranetstylesheet"),
241             IntranetNav        => C4::Context->preference("IntranetNav"),
242             intranetuserjs     => C4::Context->preference("intranetuserjs"),
243             TemplateEncoding   => C4::Context->preference("TemplateEncoding"),
244             AmazonContent      => C4::Context->preference("AmazonContent"),
245             LibraryName        => C4::Context->preference("LibraryName"),
246             LoginBranchcode    => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
247             LoginBranchname    => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
248             LoginFirstname     => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
249             LoginSurname       => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu", 
250             AutoLocation       => C4::Context->preference("AutoLocation"),
251             hide_marc          => C4::Context->preference("hide_marc"),
252             patronimages       => C4::Context->preference("patronimages"),
253             "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
254             advancedMARCEditor      => C4::Context->preference("advancedMARCEditor"),
255             suggestion              => C4::Context->preference("suggestion"),
256             virtualshelves          => C4::Context->preference("virtualshelves"),
257             LibraryName             => C4::Context->preference("LibraryName"),
258             KohaAdminEmailAddress   => "" . C4::Context->preference("KohaAdminEmailAddress"),
259                         IntranetmainUserblock   => C4::Context->preference("IntranetmainUserblock"),
260         );
261     }
262     else {
263                 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
264         my $LibraryNameTitle = C4::Context->preference("LibraryName");
265         $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
266         $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
267   $template->param(
268             KohaAdminEmailAddress  => "" . C4::Context->preference("KohaAdminEmailAddress"),
269             suggestion             => "" . C4::Context->preference("suggestion"),
270             virtualshelves         => "" . C4::Context->preference("virtualshelves"),
271             OpacNav                => "" . C4::Context->preference("OpacNav"),
272             opacheader             => "" . C4::Context->preference("opacheader"),
273             opaccredits            => "" . C4::Context->preference("opaccredits"),
274             opacsmallimage         => "" . C4::Context->preference("opacsmallimage"),
275             opaclargeimage         => "" . C4::Context->preference("opaclargeimage"),
276             opaclayoutstylesheet   => "". C4::Context->preference("opaclayoutstylesheet"),
277             opaccolorstylesheet    => "". C4::Context->preference("opaccolorstylesheet"),
278             opaclanguagesdisplay   => "". C4::Context->preference("opaclanguagesdisplay"),
279             opacuserlogin          => "" . C4::Context->preference("opacuserlogin"),
280             opacbookbag            => "" . C4::Context->preference("opacbookbag"),
281             TemplateEncoding       => "". C4::Context->preference("TemplateEncoding"),
282             AmazonContent          => "" . C4::Context->preference("AmazonContent"),
283             LibraryName            => "" . C4::Context->preference("LibraryName"),
284             LibraryNameTitle       => "" . $LibraryNameTitle,
285             LoginBranchcode        => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
286             LoginBranchname        => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"", 
287             LoginFirstname        => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
288             LoginSurname        => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu", 
289             OpacPasswordChange     => C4::Context->preference("OpacPasswordChange"),
290             opacreadinghistory     => C4::Context->preference("opacreadinghistory"),
291             opacuserjs             => C4::Context->preference("opacuserjs"),
292             OpacCloud              => C4::Context->preference("OpacCloud"),
293             OpacTopissue           => C4::Context->preference("OpacTopissue"),
294             OpacAuthorities        => C4::Context->preference("OpacAuthorities"),
295             OpacBrowser            => C4::Context->preference("OpacBrowser"),
296             RequestOnOpac          => C4::Context->preference("RequestOnOpac"),
297             reviewson              => C4::Context->preference("reviewson"),
298             hide_marc              => C4::Context->preference("hide_marc"),
299             patronimages           => C4::Context->preference("patronimages"),
300             "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
301         );
302     }
303     return ( $template, $borrowernumber, $cookie );
304 }
305
306 =item checkauth
307
308   ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
309
310 Verifies that the user is authorized to run this script.  If
311 the user is authorized, a (userid, cookie, session-id, flags)
312 quadruple is returned.  If the user is not authorized but does
313 not have the required privilege (see $flagsrequired below), it
314 displays an error page and exits.  Otherwise, it displays the
315 login page and exits.
316
317 Note that C<&checkauth> will return if and only if the user
318 is authorized, so it should be called early on, before any
319 unfinished operations (e.g., if you've opened a file, then
320 C<&checkauth> won't close it for you).
321
322 C<$query> is the CGI object for the script calling C<&checkauth>.
323
324 The C<$noauth> argument is optional. If it is set, then no
325 authorization is required for the script.
326
327 C<&checkauth> fetches user and session information from C<$query> and
328 ensures that the user is authorized to run scripts that require
329 authorization.
330
331 The C<$flagsrequired> argument specifies the required privileges
332 the user must have if the username and password are correct.
333 It should be specified as a reference-to-hash; keys in the hash
334 should be the "flags" for the user, as specified in the Members
335 intranet module. Any key specified must correspond to a "flag"
336 in the userflags table. E.g., { circulate => 1 } would specify
337 that the user must have the "circulate" privilege in order to
338 proceed. To make sure that access control is correct, the
339 C<$flagsrequired> parameter must be specified correctly.
340
341 The C<$type> argument specifies whether the template should be
342 retrieved from the opac or intranet directory tree.  "opac" is
343 assumed if it is not specified; however, if C<$type> is specified,
344 "intranet" is assumed if it is not "opac".
345
346 If C<$query> does not have a valid session ID associated with it
347 (i.e., the user has not logged in) or if the session has expired,
348 C<&checkauth> presents the user with a login page (from the point of
349 view of the original script, C<&checkauth> does not return). Once the
350 user has authenticated, C<&checkauth> restarts the original script
351 (this time, C<&checkauth> returns).
352
353 The login page is provided using a HTML::Template, which is set in the
354 systempreferences table or at the top of this file. The variable C<$type>
355 selects which template to use, either the opac or the intranet 
356 authentification template.
357
358 C<&checkauth> returns a user ID, a cookie, and a session ID. The
359 cookie should be sent back to the browser; it verifies that the user
360 has authenticated.
361
362 =cut
363
364 sub checkauth {
365     my $query = shift;
366   # warn "Checking Auth";
367     # $authnotrequired will be set for scripts which will run without authentication
368     my $authnotrequired = shift;
369     my $flagsrequired   = shift;
370     my $type            = shift;
371     $type = 'opac' unless $type;
372
373     my $dbh     = C4::Context->dbh;
374     my $timeout = C4::Context->preference('timeout');
375     $timeout = 600 unless $timeout;
376
377
378     # If Version syspref is unavailable, it means Koha is beeing installed,
379     # and so we must redirect to OPAC maintenance page or to the WebInstaller
380     unless (C4::Context->preference('Version')) {
381       if ($type ne 'opac') {
382         warn "Install required, redirecting to Installer";
383         print $query->redirect("/cgi-bin/koha/installer/install.pl");
384       } 
385       else {
386         warn "OPAC Install required, redirecting to maintenance";
387         print $query->redirect("/cgi-bin/koha/maintenance.pl");
388       }
389       exit;
390     }
391
392
393     # state variables
394     my $loggedin = 0;
395     my %info;
396     my ( $userid, $cookie, $sessionID, $flags );
397     my $logout = $query->param('logout.x');
398     if ( $userid = $ENV{'REMOTE_USER'} ) {
399         # Using Basic Authentication, no cookies required
400         $cookie = $query->cookie(
401             -name    => 'CGISESSID',
402             -value   => '',
403             -expires => ''
404         );
405         $loggedin = 1;
406     }
407     elsif ( $sessionID = $query->cookie("CGISESSID")) {
408                 my $storage_method = C4::Context->preference('SessionStorage');
409                 my $session;
410                 if ($storage_method eq 'mysql'){
411                     $session = new CGI::Session("driver:MySQL", $sessionID, {Handle=>$dbh});
412                 }
413                 else {
414                         # catch all defaults to tmp should work on all systems
415                         $session = new CGI::Session("driver:File", $sessionID, {Directory=>'/tmp'});                    
416                 }
417         C4::Context->_new_userenv($sessionID);
418         if ($session){
419             C4::Context::set_userenv(
420                 $session->param('number'),       $session->param('id'),
421                 $session->param('cardnumber'),   $session->param('firstname'),
422                 $session->param('surname'),      $session->param('branch'),
423                 $session->param('branchname'),   $session->param('flags'),
424                 $session->param('emailaddress'), $session->param('branchprinter')
425             );
426 #             warn       "".$session->param('cardnumber').",   ".$session->param('firstname').",
427 #                 ".$session->param('surname').",      ".$session->param('branch');
428         }
429         my $ip;
430         my $lasttime;
431         if ($session) {
432           $ip = $session->param('ip');
433           $lasttime = $session->param('lasttime');
434                 $userid = $session->param('id');
435         }
436         
437     
438         if ($logout) {
439
440             # voluntary logout the user
441 #             $session->delete;
442             $session->flush;      
443             C4::Context->_unset_userenv($sessionID);
444             $sessionID = undef;
445             $userid    = undef;
446             open L, ">>/tmp/sessionlog";
447             my $time = localtime( time() );
448             printf L "%20s from %16s logged out at %30s (manually).\n", $userid,
449               $ip, $time;
450             close L;
451         }
452         if ($userid) {
453             if ( $lasttime < time() - $timeout ) {
454                 # timed logout
455                 $info{'timed_out'} = 1;
456                 $session->delete();
457                 C4::Context->_unset_userenv($sessionID);
458                 $userid    = undef;
459                 $sessionID = undef;
460                 open L, ">>/tmp/sessionlog";
461                 my $time = localtime( time() );
462                 printf L "%20s from %16s logged out at %30s (inactivity).\n",
463                   $userid, $ip, $time;
464                 close L;
465             }
466             elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
467                 # Different ip than originally logged in from
468                 $info{'oldip'}        = $ip;
469                 $info{'newip'}        = $ENV{'REMOTE_ADDR'};
470                 $info{'different_ip'} = 1;
471         $session->delete();
472                 C4::Context->_unset_userenv($sessionID);
473                 $sessionID = undef;
474                 $userid    = undef;
475                 open L, ">>/tmp/sessionlog";
476                 my $time = localtime( time() );
477                 printf L
478 "%20s from logged out at %30s (ip changed from %16s to %16s).\n",
479                   $userid, $time, $ip, $info{'newip'};
480                 close L;
481             }
482             else {
483                 $cookie = $query->cookie( CGISESSID => $session->id );
484                 $session->param('lasttime',time());
485                 $flags = haspermission( $dbh, $userid, $flagsrequired );
486                 if ($flags) {
487                     $loggedin = 1;
488                 }
489                 else {
490                     $info{'nopermission'} = 1;
491                 }
492             }
493         }
494     }
495     unless ($userid) {
496 #         my $session = new CGI::Session("driver:MySQL", undef, {Handle=>$dbh});    
497         my $session = new CGI::Session("driver:File", undef, {Directory=>'/tmp'});    
498         my $sessionID;
499     if ($session) {
500       $sessionID = $session->id;
501       }
502         $userid    = $query->param('userid');
503         C4::Context->_new_userenv($sessionID);
504         my $password = $query->param('password');
505         C4::Context->_new_userenv($sessionID);
506         my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
507         if ($return) {
508             open L, ">>/tmp/sessionlog";
509             my $time = localtime( time() );
510             printf L "%20s from %16s logged in  at %30s.\n", $userid,
511               $ENV{'REMOTE_ADDR'}, $time;
512             close L;
513             $cookie = $query->cookie(CGISESSID => $sessionID);
514             if ( $flags = haspermission( $dbh, $userid, $flagsrequired ) ) {
515                 $loggedin = 1;
516             }
517             else {
518                 $info{'nopermission'} = 1;
519                 C4::Context->_unset_userenv($sessionID);
520             }
521             if ( $return == 1 ) {
522                 my (
523                     $borrowernumber, $firstname,  $surname,
524                     $userflags,      $branchcode, $branchname,
525                     $branchprinter,  $emailaddress
526                 );
527                 my $sth =
528                   $dbh->prepare(
529 "select borrowernumber, firstname, surname, flags, borrowers.branchcode, branches.branchname as branchname,branches.branchprinter as branchprinter, email from borrowers left join branches on borrowers.branchcode=branches.branchcode where userid=?"
530                   );
531                 $sth->execute($userid);
532                 (
533                     $borrowernumber, $firstname,  $surname,
534                     $userflags,      $branchcode, $branchname,
535                     $branchprinter,  $emailaddress
536                   )
537                   = $sth->fetchrow
538                   if ( $sth->rows );
539
540 #         warn "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress";
541                 unless ( $sth->rows ) {
542                     my $sth =
543                       $dbh->prepare(
544 "select borrowernumber, firstname, surname, flags, borrowers.branchcode, branches.branchname as branchname, branches.branchprinter as branchprinter, email from borrowers left join branches on borrowers.branchcode=branches.branchcode where cardnumber=?"
545                       );
546                     $sth->execute($cardnumber);
547                     (
548                         $borrowernumber, $firstname,  $surname,
549                         $userflags,      $branchcode, $branchname,
550                         $branchprinter,  $emailaddress
551                       )
552                       = $sth->fetchrow
553                       if ( $sth->rows );
554
555 #           warn "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress";
556                     unless ( $sth->rows ) {
557                         $sth->execute($userid);
558                         (
559                             $borrowernumber, $firstname, $surname, $userflags,
560                             $branchcode, $branchname, $branchprinter, $emailaddress
561                           )
562                           = $sth->fetchrow
563                           if ( $sth->rows );
564                     }
565                 }
566
567 # launch a sequence to check if we have a ip for the branch, if we have one we replace the branchcode of the userenv by the branch bound in the ip.
568                 my $ip       = $ENV{'REMOTE_ADDR'};
569                 # if they specify at login, use that
570                 if ($query->param('branch')) {
571                     $branchcode  = $query->param('branch');
572                     $branchname = GetBranchName($branchcode);
573                 }
574                 my $branches = GetBranches();
575                 my @branchesloop;
576                 foreach my $br ( keys %$branches ) {
577                     #     now we work with the treatment of ip
578                     my $domain = $branches->{$br}->{'branchip'};
579                     if ( $domain && $ip =~ /^$domain/ ) {
580                         $branchcode = $branches->{$br}->{'branchcode'};
581
582                         # new op dev : add the branchprinter and branchname in the cookie
583                         $branchprinter = $branches->{$br}->{'branchprinter'};
584                         $branchname    = $branches->{$br}->{'branchname'};
585                     }
586                 }
587                 $session->param('number',$borrowernumber);
588                 $session->param('id',$userid);
589                 $session->param('cardnumber',$cardnumber);
590                 $session->param('firstname',$firstname);
591                 $session->param('surname',$surname);
592                 $session->param('branch',$branchcode);
593                 $session->param('branchname',$branchname);
594                 $session->param('flags',$userflags);
595                 $session->param('emailaddress',$emailaddress);
596                 $session->param('ip',$session->remote_addr());
597                 $session->param('lasttime',time());
598 #            warn       "".$session->param('cardnumber').",   ".$session->param('firstname').",
599 #                 ".$session->param('surname').",      ".$session->param('branch');
600             }
601             elsif ( $return == 2 ) {
602                 #We suppose the user is the superlibrarian
603                         $session->param('number',0);
604                         $session->param('id',C4::Context->config('user'));
605                         $session->param('cardnumber',C4::Context->config('user'));
606                         $session->param('firstname',C4::Context->config('user'));
607                         $session->param('surname',C4::Context->config('user'));
608                         $session->param('branch','NO_LIBRARY_SET');
609                         $session->param('branchname','NO_LIBRARY_SET');
610                         $session->param('flags',1);
611                         $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
612                         $session->param('ip',$session->remote_addr());
613                         $session->param('lasttime',time());
614                 }
615                 if ($session){
616                     C4::Context::set_userenv(
617                         $session->param('number'),       $session->param('id'),
618                         $session->param('cardnumber'),   $session->param('firstname'),
619                         $session->param('surname'),      $session->param('branch'),
620                         $session->param('branchname'),   $session->param('flags'),
621                         $session->param('emailaddress'), $session->param('branchprinter')
622                     );
623                 }
624         }
625
626         else {
627             if ($userid) {
628                 $info{'invalid_username_or_password'} = 1;
629                 C4::Context->_unset_userenv($sessionID);
630             }
631         }
632     }
633     my $insecure = C4::Context->boolean_preference('insecure');
634
635     # finished authentification, now respond
636     if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
637     {
638         # successful login
639         unless ($cookie) {
640             $cookie = $query->cookie( CGISESSID => ''
641             );
642         }
643     return ( $userid, $cookie, $sessionID, $flags );
644
645     }
646
647 #
648 #
649 # AUTH rejected, show the login/password template, after checking the DB.
650 #
651 #
652     
653     # get the inputs from the incoming query
654     my @inputs = ();
655     foreach my $name ( param $query) {
656         (next) if ( $name eq 'userid' || $name eq 'password' );
657         my $value = $query->param($name);
658         push @inputs, { name => $name, value => $value };
659     }
660     # get the branchloop, which we need for authentication
661     my $branches = GetBranches();
662     my @branch_loop;
663     for my $branch_hash (keys %$branches) {
664                 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
665     }
666
667     # check that database and koha version are the same
668     # there is no DB version, it's a fresh install,
669     # go to web installer
670     # there is a DB version, compare it to the code version
671     my $kohaversion=C4::Context::KOHAVERSION;
672     # remove the 3 last . to have a Perl number
673     $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
674 #     warn "kohaversion : $kohaversion";
675     if (C4::Context->preference('Version') < $kohaversion){
676       if ($type ne 'opac'){
677       warn "Database update needed, redirecting to Installer. Database is ".C4::Context->preference('Version')." and Koha is : ".C4::Context->config("kohaversion");
678         print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
679       } else {
680       warn "OPAC :Database update needed, redirecting to maintenance. Database is ".C4::Context->preference('Version')." and Koha is : ".C4::Context->config("kohaversion");
681         print $query->redirect("/cgi-bin/koha/maintenance.pl");
682       }       
683       exit;
684     }
685     my $template_name;
686     if ( $type eq 'opac' ) {
687         $template_name = "opac-auth.tmpl";
688     }
689     else {
690         $template_name = "auth.tmpl";
691     }
692     my $template = gettemplate( $template_name, $type, $query );
693     $template->param(branchloop => \@branch_loop,);
694     $template->param(
695     login        => 1,
696         INPUTS               => \@inputs,
697         suggestion           => C4::Context->preference("suggestion"),
698         virtualshelves       => C4::Context->preference("virtualshelves"),
699         opaclargeimage       => C4::Context->preference("opaclargeimage"),
700         LibraryName          => C4::Context->preference("LibraryName"),
701         OpacNav              => C4::Context->preference("OpacNav"),
702         opaccredits          => C4::Context->preference("opaccredits"),
703         opacreadinghistory   => C4::Context->preference("opacreadinghistory"),
704         opacsmallimage       => C4::Context->preference("opacsmallimage"),
705         opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
706         opaccolorstylesheet  => C4::Context->preference("opaccolorstylesheet"),
707         opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
708         opacuserjs           => C4::Context->preference("opacuserjs"),
709
710         intranetcolorstylesheet =>
711           C4::Context->preference("intranetcolorstylesheet"),
712         intranetstylesheet => C4::Context->preference("intranetstylesheet"),
713         IntranetNav        => C4::Context->preference("IntranetNav"),
714         intranetuserjs     => C4::Context->preference("intranetuserjs"),
715         TemplateEncoding   => C4::Context->preference("TemplateEncoding"),
716
717     );
718     $template->param( loginprompt => 1 ) unless $info{'nopermission'};
719
720     my $self_url = $query->url( -absolute => 1 );
721     $template->param(
722         url         => $self_url,
723         LibraryName => => C4::Context->preference("LibraryName"),
724     );
725     $template->param( \%info );
726 #    $cookie = $query->cookie(CGISESSID => $session->id
727 #   );
728     print $query->header(
729                 -type   => 'text/html',
730         -charset => 'utf-8',
731         -cookie => $cookie
732       ),
733       $template->output;
734     exit;
735 }
736
737 sub checkpw {
738
739     my ( $dbh, $userid, $password ) = @_;
740
741     # INTERNAL AUTH
742     my $sth =
743       $dbh->prepare(
744 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
745       );
746     $sth->execute($userid);
747     if ( $sth->rows ) {
748         my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
749             $surname, $branchcode, $flags )
750           = $sth->fetchrow;
751         if ( md5_base64($password) eq $md5password ) {
752
753             C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
754                 $firstname, $surname, $branchcode, $flags );
755             return 1, $cardnumber;
756         }
757     }
758     $sth =
759       $dbh->prepare(
760 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
761       );
762     $sth->execute($userid);
763     if ( $sth->rows ) {
764         my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
765             $surname, $branchcode, $flags )
766           = $sth->fetchrow;
767         if ( md5_base64($password) eq $md5password ) {
768
769             C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
770                 $firstname, $surname, $branchcode, $flags );
771             return 1, $userid;
772         }
773     }
774     if (   $userid && $userid eq C4::Context->config('user')
775         && "$password" eq C4::Context->config('pass') )
776     {
777
778 # Koha superuser account
779 #     C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
780         return 2;
781     }
782     if (   $userid && $userid eq 'demo'
783         && "$password" eq 'demo'
784         && C4::Context->config('demo') )
785     {
786
787 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
788 # some features won't be effective : modify systempref, modify MARC structure,
789         return 2;
790     }
791     return 0;
792 }
793
794 sub getuserflags {
795     my $cardnumber = shift;
796     my $dbh        = shift;
797     my $userflags;
798     my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE cardnumber=?");
799     $sth->execute($cardnumber);
800     my ($flags) = $sth->fetchrow;
801     $flags = 0 unless $flags;
802     $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
803     $sth->execute;
804
805     while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
806         if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
807             $userflags->{$flag} = 1;
808         }
809         else {
810             $userflags->{$flag} = 0;
811         }
812     }
813     return $userflags;
814 }
815
816 sub haspermission {
817     my ( $dbh, $userid, $flagsrequired ) = @_;
818     my $sth = $dbh->prepare("SELECT cardnumber FROM borrowers WHERE userid=?");
819     $sth->execute($userid);
820     my ($cardnumber) = $sth->fetchrow;
821     ($cardnumber) || ( $cardnumber = $userid );
822     my $flags = getuserflags( $cardnumber, $dbh );
823     my $configfile;
824     if ( $userid eq C4::Context->config('user') ) {
825
826         # Super User Account from /etc/koha.conf
827         $flags->{'superlibrarian'} = 1;
828     }
829     if ( $userid eq 'demo' && C4::Context->config('demo') ) {
830
831         # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
832         $flags->{'superlibrarian'} = 1;
833     }
834     return $flags if $flags->{superlibrarian};
835     foreach ( keys %$flagsrequired ) {
836         return $flags if $flags->{$_};
837     }
838     return 0;
839 }
840
841 sub getborrowernumber {
842     my ($userid) = @_;
843     my $dbh = C4::Context->dbh;
844     for my $field ( 'userid', 'cardnumber' ) {
845         my $sth =
846           $dbh->prepare("select borrowernumber from borrowers where $field=?");
847         $sth->execute($userid);
848         if ( $sth->rows ) {
849             my ($bnumber) = $sth->fetchrow;
850             return $bnumber;
851         }
852     }
853     return 0;
854 }
855
856 END { }    # module clean-up code here (global destructor)
857 1;
858 __END__
859
860 =back
861
862 =head1 SEE ALSO
863
864 CGI(3)
865
866 C4::Output(3)
867
868 Digest::MD5(3)
869
870 =cut