autocomplete.css - unclosed comment error removed
[koha.git] / C4 / Auth.pm
blob42574c891786ab89553c28ec0fdbecac1e32e292
2 # -*- tab-width: 8 -*-
3 # NOTE: This file uses 8-character tabs; do not change the tab size!
5 package C4::Auth;
7 # Copyright 2000-2002 Katipo Communications
9 # This file is part of Koha.
11 # Koha is free software; you can redistribute it and/or modify it under the
12 # terms of the GNU General Public License as published by the Free Software
13 # Foundation; either version 2 of the License, or (at your option) any later
14 # version.
16 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
17 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
18 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License along with
21 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
22 # Suite 330, Boston, MA 02111-1307 USA
24 use strict;
25 use Digest::MD5 qw(md5_base64);
26 use CGI::Session;
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
35 # use utf8;
36 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap);
38 BEGIN {
39 $VERSION = 3.01; # set version for version checking
40 $debug = $ENV{DEBUG} || 0 ;
41 @ISA = qw(Exporter);
42 @EXPORT = qw(&checkauth &get_template_and_user);
43 @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw);
44 $ldap = C4::Context->config('useldapserver') || 0;
45 if ($ldap) {
46 require C4::Auth_with_ldap; # no import
47 import C4::Auth_with_ldap qw(checkpw_ldap);
51 =head1 NAME
53 C4::Auth - Authenticates Koha users
55 =head1 SYNOPSIS
57 use CGI;
58 use C4::Auth;
60 my $query = new CGI;
62 my ($template, $borrowernumber, $cookie)
63 = get_template_and_user(
65 template_name => "opac-main.tmpl",
66 query => $query,
67 type => "opac",
68 authnotrequired => 1,
69 flagsrequired => {borrow => 1},
73 print $query->header(
74 -type => 'utf-8',
75 -cookie => $cookie
76 ), $template->output;
79 =head1 DESCRIPTION
81 The main function of this module is to provide
82 authentification. However the get_template_and_user function has
83 been provided so that a users login information is passed along
84 automatically. This gets loaded into the template.
86 =head1 FUNCTIONS
88 =over 2
90 =item get_template_and_user
92 my ($template, $borrowernumber, $cookie)
93 = get_template_and_user(
95 template_name => "opac-main.tmpl",
96 query => $query,
97 type => "opac",
98 authnotrequired => 1,
99 flagsrequired => {borrow => 1},
103 This call passes the C<query>, C<flagsrequired> and C<authnotrequired>
104 to C<&checkauth> (in this module) to perform authentification.
105 See C<&checkauth> for an explanation of these parameters.
107 The C<template_name> is then used to find the correct template for
108 the page. The authenticated users details are loaded onto the
109 template in the HTML::Template LOOP variable C<USER_INFO>. Also the
110 C<sessionID> is passed to the template. This can be used in templates
111 if cookies are disabled. It needs to be put as and input to every
112 authenticated page.
114 More information on the C<gettemplate> sub can be found in the
115 Output.pm module.
117 =cut
119 sub get_template_and_user {
120 my $in = shift;
121 my $template =
122 gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'} );
123 my ( $user, $cookie, $sessionID, $flags ) = checkauth(
124 $in->{'query'},
125 $in->{'authnotrequired'},
126 $in->{'flagsrequired'},
127 $in->{'type'}
128 ) unless ($in->{'template_name'}=~/maintenance/);
130 my $borrowernumber;
131 my $insecure = C4::Context->preference('insecure');
132 if ($user or $insecure) {
134 # load the template variables for stylesheets and JavaScript
135 $template->param( css_libs => $in->{'css_libs'} );
136 $template->param( css_module => $in->{'css_module'} );
137 $template->param( css_page => $in->{'css_page'} );
138 $template->param( css_widgets => $in->{'css_widgets'} );
140 $template->param( js_libs => $in->{'js_libs'} );
141 $template->param( js_module => $in->{'js_module'} );
142 $template->param( js_page => $in->{'js_page'} );
143 $template->param( js_widgets => $in->{'js_widgets'} );
145 # user info
146 $template->param( loggedinusername => $user );
147 $template->param( sessionID => $sessionID );
149 $borrowernumber = getborrowernumber($user);
150 my ( $borr, $alternativeflags ) =
151 GetMemberDetails( $borrowernumber );
152 my @bordat;
153 $bordat[0] = $borr;
154 $template->param( "USER_INFO" => \@bordat );
156 my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow
157 editcatalogue updatecharge management tools editauthorities serials reports);
158 # We are going to use the $flags returned by checkauth
159 # to create the template's parameters that will indicate
160 # which menus the user can access.
161 if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) {
162 $template->param( CAN_user_circulate => 1 );
163 $template->param( CAN_user_catalogue => 1 );
164 $template->param( CAN_user_parameters => 1 );
165 $template->param( CAN_user_borrowers => 1 );
166 $template->param( CAN_user_permission => 1 );
167 $template->param( CAN_user_reserveforothers => 1 );
168 $template->param( CAN_user_borrow => 1 );
169 $template->param( CAN_user_editcatalogue => 1 );
170 $template->param( CAN_user_updatecharge => 1 );
171 $template->param( CAN_user_acquisition => 1 );
172 $template->param( CAN_user_management => 1 );
173 $template->param( CAN_user_tools => 1 );
174 $template->param( CAN_user_editauthorities => 1 );
175 $template->param( CAN_user_serials => 1 );
176 $template->param( CAN_user_reports => 1 );
177 $template->param( CAN_user_staffaccess => 1 );
180 if ( $flags && $flags->{circulate} == 1 ) {
181 $template->param( CAN_user_circulate => 1 );
184 if ( $flags && $flags->{catalogue} == 1 ) {
185 $template->param( CAN_user_catalogue => 1 );
188 if ( $flags && $flags->{parameters} == 1 ) {
189 $template->param( CAN_user_parameters => 1 );
190 $template->param( CAN_user_management => 1 );
193 if ( $flags && $flags->{borrowers} == 1 ) {
194 $template->param( CAN_user_borrowers => 1 );
197 if ( $flags && $flags->{permissions} == 1 ) {
198 $template->param( CAN_user_permission => 1 );
201 if ( $flags && $flags->{reserveforothers} == 1 ) {
202 $template->param( CAN_user_reserveforothers => 1 );
205 if ( $flags && $flags->{borrow} == 1 ) {
206 $template->param( CAN_user_borrow => 1 );
209 if ( $flags && $flags->{editcatalogue} == 1 ) {
210 $template->param( CAN_user_editcatalogue => 1 );
213 if ( $flags && $flags->{updatecharges} == 1 ) {
214 $template->param( CAN_user_updatecharge => 1 );
217 if ( $flags && $flags->{acquisition} == 1 ) {
218 $template->param( CAN_user_acquisition => 1 );
221 if ( $flags && $flags->{tools} == 1 ) {
222 $template->param( CAN_user_tools => 1 );
225 if ( $flags && $flags->{editauthorities} == 1 ) {
226 $template->param( CAN_user_editauthorities => 1 );
229 if ( $flags && $flags->{serials} == 1 ) {
230 $template->param( CAN_user_serials => 1 );
233 if ( $flags && $flags->{reports} == 1 ) {
234 $template->param( CAN_user_reports => 1 );
236 if ( $flags && $flags->{staffaccess} == 1 ) {
237 $template->param( CAN_user_staffaccess => 1 );
240 if ( $in->{'type'} eq "intranet" ) {
241 $template->param(
242 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
243 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
244 IntranetNav => C4::Context->preference("IntranetNav"),
245 intranetuserjs => C4::Context->preference("intranetuserjs"),
246 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
247 AmazonContent => C4::Context->preference("AmazonContent"),
248 LibraryName => C4::Context->preference("LibraryName"),
249 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
250 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
251 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
252 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
253 AutoLocation => C4::Context->preference("AutoLocation"),
254 hide_marc => C4::Context->preference("hide_marc"),
255 patronimages => C4::Context->preference("patronimages"),
256 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
257 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
258 suggestion => C4::Context->preference("suggestion"),
259 virtualshelves => C4::Context->preference("virtualshelves"),
260 LibraryName => C4::Context->preference("LibraryName"),
261 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
262 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
263 IndependantBranches => C4::Context->preference("IndependantBranches"),
264 CircAutocompl => C4::Context->preference("CircAutocompl"),
265 yuipath => C4::Context->preference("yuipath"),
266 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
267 AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
270 else {
271 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
272 my $LibraryNameTitle = C4::Context->preference("LibraryName");
273 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
274 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
275 $template->param(
276 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
277 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
278 suggestion => "" . C4::Context->preference("suggestion"),
279 virtualshelves => "" . C4::Context->preference("virtualshelves"),
280 OpacNav => "" . C4::Context->preference("OpacNav"),
281 opacheader => "" . C4::Context->preference("opacheader"),
282 opaccredits => "" . C4::Context->preference("opaccredits"),
283 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
284 opaclargeimage => "" . C4::Context->preference("opaclargeimage"),
285 opaclayoutstylesheet => "". C4::Context->preference("opaclayoutstylesheet"),
286 opaccolorstylesheet => "". C4::Context->preference("opaccolorstylesheet"),
287 opaclanguagesdisplay => "". C4::Context->preference("opaclanguagesdisplay"),
288 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
289 opacbookbag => "" . C4::Context->preference("opacbookbag"),
290 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
291 AmazonContent => "" . C4::Context->preference("AmazonContent"),
292 LibraryName => "" . C4::Context->preference("LibraryName"),
293 LibraryNameTitle => "" . $LibraryNameTitle,
294 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
295 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
296 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
297 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
298 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
299 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
300 opacuserjs => C4::Context->preference("opacuserjs"),
301 OpacCloud => C4::Context->preference("OpacCloud"),
302 OpacTopissue => C4::Context->preference("OpacTopissue"),
303 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
304 OpacBrowser => C4::Context->preference("OpacBrowser"),
305 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
306 reviewson => C4::Context->preference("reviewson"),
307 hide_marc => C4::Context->preference("hide_marc"),
308 patronimages => C4::Context->preference("patronimages"),
309 mylibraryfirst => C4::Context->preference("SearchMyLibraryFirst"),
310 "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
311 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
314 return ( $template, $borrowernumber, $cookie, $flags);
317 =item checkauth
319 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
321 Verifies that the user is authorized to run this script. If
322 the user is authorized, a (userid, cookie, session-id, flags)
323 quadruple is returned. If the user is not authorized but does
324 not have the required privilege (see $flagsrequired below), it
325 displays an error page and exits. Otherwise, it displays the
326 login page and exits.
328 Note that C<&checkauth> will return if and only if the user
329 is authorized, so it should be called early on, before any
330 unfinished operations (e.g., if you've opened a file, then
331 C<&checkauth> won't close it for you).
333 C<$query> is the CGI object for the script calling C<&checkauth>.
335 The C<$noauth> argument is optional. If it is set, then no
336 authorization is required for the script.
338 C<&checkauth> fetches user and session information from C<$query> and
339 ensures that the user is authorized to run scripts that require
340 authorization.
342 The C<$flagsrequired> argument specifies the required privileges
343 the user must have if the username and password are correct.
344 It should be specified as a reference-to-hash; keys in the hash
345 should be the "flags" for the user, as specified in the Members
346 intranet module. Any key specified must correspond to a "flag"
347 in the userflags table. E.g., { circulate => 1 } would specify
348 that the user must have the "circulate" privilege in order to
349 proceed. To make sure that access control is correct, the
350 C<$flagsrequired> parameter must be specified correctly.
352 The C<$type> argument specifies whether the template should be
353 retrieved from the opac or intranet directory tree. "opac" is
354 assumed if it is not specified; however, if C<$type> is specified,
355 "intranet" is assumed if it is not "opac".
357 If C<$query> does not have a valid session ID associated with it
358 (i.e., the user has not logged in) or if the session has expired,
359 C<&checkauth> presents the user with a login page (from the point of
360 view of the original script, C<&checkauth> does not return). Once the
361 user has authenticated, C<&checkauth> restarts the original script
362 (this time, C<&checkauth> returns).
364 The login page is provided using a HTML::Template, which is set in the
365 systempreferences table or at the top of this file. The variable C<$type>
366 selects which template to use, either the opac or the intranet
367 authentification template.
369 C<&checkauth> returns a user ID, a cookie, and a session ID. The
370 cookie should be sent back to the browser; it verifies that the user
371 has authenticated.
373 =cut
375 sub _version_check ($$) {
376 my $type = shift;
377 my $query = shift;
378 my $version;
379 # If Version syspref is unavailable, it means Koha is beeing installed,
380 # and so we must redirect to OPAC maintenance page or to the WebInstaller
381 #warn "about to check version";
382 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
383 if ($type ne 'opac') {
384 warn "Install required, redirecting to Installer";
385 print $query->redirect("/cgi-bin/koha/installer/install.pl");
387 else {
388 warn "OPAC Install required, redirecting to maintenance";
389 print $query->redirect("/cgi-bin/koha/maintenance.pl");
391 exit;
394 # check that database and koha version are the same
395 # there is no DB version, it's a fresh install,
396 # go to web installer
397 # there is a DB version, compare it to the code version
398 my $kohaversion=C4::Context::KOHAVERSION;
399 # remove the 3 last . to have a Perl number
400 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
401 $debug and print STDERR "kohaversion : $kohaversion\n";
402 if ($version < $kohaversion){
403 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is "
404 . C4::Context->config("kohaversion");
405 if ($type ne 'opac'){
406 warn sprintf($warning, 'Installer');
407 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
408 } else {
409 warn sprintf("OPAC: " . $warning, 'maintenance');
410 print $query->redirect("/cgi-bin/koha/maintenance.pl");
412 exit;
416 sub _session_log {
417 (@_) or return 0;
418 open L, ">>/tmp/sessionlog";
419 printf L join("\n",@_);
420 close L;
423 sub checkauth {
424 my $query = shift;
425 # warn "Checking Auth";
426 # $authnotrequired will be set for scripts which will run without authentication
427 my $authnotrequired = shift;
428 my $flagsrequired = shift;
429 my $type = shift;
430 $type = 'opac' unless $type;
432 my $dbh = C4::Context->dbh;
433 my $timeout = C4::Context->preference('timeout');
434 # days
435 if ($timeout =~ /(\d+)[dD]/) {
436 $timeout = $1 * 86400;
438 $timeout = 600 unless $timeout;
440 _version_check($type,$query);
441 # state variables
442 my $loggedin = 0;
443 my %info;
444 my ( $userid, $cookie, $sessionID, $flags );
445 my $logout = $query->param('logout.x');
446 if ( $userid = $ENV{'REMOTE_USER'} ) {
447 # Using Basic Authentication, no cookies required
448 $cookie = $query->cookie(
449 -name => 'CGISESSID',
450 -value => '',
451 -expires => ''
453 $loggedin = 1;
455 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison (?)
456 my $session = get_session($sessionID);
457 C4::Context->_new_userenv($sessionID);
458 if ($session){
459 C4::Context::set_userenv(
460 $session->param('number'), $session->param('id'),
461 $session->param('cardnumber'), $session->param('firstname'),
462 $session->param('surname'), $session->param('branch'),
463 $session->param('branchname'), $session->param('flags'),
464 $session->param('emailaddress'), $session->param('branchprinter')
466 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
468 my $ip;
469 my $lasttime;
470 if ($session) {
471 $ip = $session->param('ip');
472 $lasttime = $session->param('lasttime');
473 $userid = $session->param('id');
476 if ($logout) {
477 # voluntary logout the user
478 $session->flush;
479 $session->delete();
480 C4::Context->_unset_userenv($sessionID);
481 $sessionID = undef;
482 $userid = undef;
483 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,localtime);
485 if ($userid) {
486 if ( $lasttime < time() - $timeout ) {
487 # timed logout
488 $info{'timed_out'} = 1;
489 $session->delete();
490 C4::Context->_unset_userenv($sessionID);
491 $userid = undef;
492 $sessionID = undef;
493 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,localtime);
495 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
496 # Different ip than originally logged in from
497 $info{'oldip'} = $ip;
498 $info{'newip'} = $ENV{'REMOTE_ADDR'};
499 $info{'different_ip'} = 1;
500 $session->delete();
501 C4::Context->_unset_userenv($sessionID);
502 $sessionID = undef;
503 $userid = undef;
504 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,localtime, $info{'newip'});
506 else {
507 $cookie = $query->cookie( CGISESSID => $session->id );
508 $session->param('lasttime',time());
509 $flags = haspermission( $dbh, $userid, $flagsrequired );
510 if ($flags) {
511 $loggedin = 1;
513 else {
514 $info{'nopermission'} = 1;
519 unless ($userid) {
520 my $session = get_session("");
521 my $sessionID;
522 if ($session) {
523 $sessionID = $session->id;
525 $userid = $query->param('userid');
526 C4::Context->_new_userenv($sessionID);
527 my $password = $query->param('password');
528 C4::Context->_new_userenv($sessionID);
529 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
530 if ($return) {
531 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},localtime);
532 $cookie = $query->cookie(CGISESSID => $sessionID);
533 if ( $flags = haspermission( $dbh, $userid, $flagsrequired ) ) {
534 $loggedin = 1;
536 else {
537 $info{'nopermission'} = 1;
538 C4::Context->_unset_userenv($sessionID);
540 if ( $return == 1 ) {
541 my (
542 $borrowernumber, $firstname, $surname, $userflags,
543 $branchcode, $branchname, $branchprinter, $emailaddress
545 my $select = "
546 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
547 branches.branchname as branchname,
548 branches.branchprinter as branchprinter,
549 email
550 FROM borrowers
551 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
553 my $sth = $dbh->prepare("$select where userid=?");
554 $sth->execute($userid);
555 ($sth->rows) and (
556 $borrowernumber, $firstname, $surname, $userflags,
557 $branchcode, $branchname, $branchprinter, $emailaddress
558 ) = $sth->fetchrow;
560 $debug and print STDERR "AUTH_1: $cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
561 unless ( $sth->rows ) {
562 my $sth = $dbh->prepare("$select where cardnumber=?");
563 $sth->execute($cardnumber);
564 ($sth->rows) and (
565 $borrowernumber, $firstname, $surname, $userflags,
566 $branchcode, $branchname, $branchprinter, $emailaddress
567 ) = $sth->fetchrow;
569 $debug and print STDERR "AUTH_2: $cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
570 unless ( $sth->rows ) {
571 $sth->execute($userid);
572 ($sth->rows) and (
573 $borrowernumber, $firstname, $surname, $userflags,
574 $branchcode, $branchname, $branchprinter, $emailaddress
575 ) = $sth->fetchrow;
579 # launch a sequence to check if we have a ip for the branch, i
580 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
582 my $ip = $ENV{'REMOTE_ADDR'};
583 # if they specify at login, use that
584 if ($query->param('branch')) {
585 $branchcode = $query->param('branch');
586 $branchname = GetBranchName($branchcode);
588 my $branches = GetBranches();
589 my @branchesloop;
590 foreach my $br ( keys %$branches ) {
591 # now we work with the treatment of ip
592 my $domain = $branches->{$br}->{'branchip'};
593 if ( $domain && $ip =~ /^$domain/ ) {
594 $branchcode = $branches->{$br}->{'branchcode'};
596 # new op dev : add the branchprinter and branchname in the cookie
597 $branchprinter = $branches->{$br}->{'branchprinter'};
598 $branchname = $branches->{$br}->{'branchname'};
601 $session->param('number',$borrowernumber);
602 $session->param('id',$userid);
603 $session->param('cardnumber',$cardnumber);
604 $session->param('firstname',$firstname);
605 $session->param('surname',$surname);
606 $session->param('branch',$branchcode);
607 $session->param('branchname',$branchname);
608 $session->param('flags',$userflags);
609 $session->param('emailaddress',$emailaddress);
610 $session->param('ip',$session->remote_addr());
611 $session->param('lasttime',time());
612 $debug and printf STDERR "AUTH_3: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
614 elsif ( $return == 2 ) {
615 #We suppose the user is the superlibrarian
616 $session->param('number',0);
617 $session->param('id',C4::Context->config('user'));
618 $session->param('cardnumber',C4::Context->config('user'));
619 $session->param('firstname',C4::Context->config('user'));
620 $session->param('surname',C4::Context->config('user'));
621 $session->param('branch','NO_LIBRARY_SET');
622 $session->param('branchname','NO_LIBRARY_SET');
623 $session->param('flags',1);
624 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
625 $session->param('ip',$session->remote_addr());
626 $session->param('lasttime',time());
628 if ($session) {
629 C4::Context::set_userenv(
630 $session->param('number'), $session->param('id'),
631 $session->param('cardnumber'), $session->param('firstname'),
632 $session->param('surname'), $session->param('branch'),
633 $session->param('branchname'), $session->param('flags'),
634 $session->param('emailaddress'), $session->param('branchprinter')
638 else {
639 if ($userid) {
640 $info{'invalid_username_or_password'} = 1;
641 C4::Context->_unset_userenv($sessionID);
645 my $insecure = C4::Context->boolean_preference('insecure');
647 # finished authentification, now respond
648 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
650 # successful login
651 unless ($cookie) {
652 $cookie = $query->cookie( CGISESSID => '' );
654 return ( $userid, $cookie, $sessionID, $flags );
659 # AUTH rejected, show the login/password template, after checking the DB.
663 # get the inputs from the incoming query
664 my @inputs = ();
665 foreach my $name ( param $query) {
666 (next) if ( $name eq 'userid' || $name eq 'password' );
667 my $value = $query->param($name);
668 push @inputs, { name => $name, value => $value };
670 # get the branchloop, which we need for authentication
671 my $branches = GetBranches();
672 my @branch_loop;
673 for my $branch_hash (keys %$branches) {
674 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
677 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
678 my $template = gettemplate( $template_name, $type, $query );
679 $template->param(branchloop => \@branch_loop,);
680 $template->param(
681 login => 1,
682 INPUTS => \@inputs,
683 suggestion => C4::Context->preference("suggestion"),
684 virtualshelves => C4::Context->preference("virtualshelves"),
685 opaclargeimage => C4::Context->preference("opaclargeimage"),
686 LibraryName => C4::Context->preference("LibraryName"),
687 OpacNav => C4::Context->preference("OpacNav"),
688 opaccredits => C4::Context->preference("opaccredits"),
689 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
690 opacsmallimage => C4::Context->preference("opacsmallimage"),
691 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
692 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
693 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
694 opacuserjs => C4::Context->preference("opacuserjs"),
696 intranetcolorstylesheet =>
697 C4::Context->preference("intranetcolorstylesheet"),
698 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
699 IntranetNav => C4::Context->preference("IntranetNav"),
700 intranetuserjs => C4::Context->preference("intranetuserjs"),
701 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
702 IndependantBranches => C4::Context->preference("IndependantBranches"),
703 AutoLocation => C4::Context->preference("AutoLocation"),
704 yuipath => C4::Context->preference("yuipath"),
706 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
708 my $self_url = $query->url( -absolute => 1 );
709 $template->param(
710 url => $self_url,
711 LibraryName => => C4::Context->preference("LibraryName"),
713 $template->param( \%info );
714 # $cookie = $query->cookie(CGISESSID => $session->id
715 # );
716 print $query->header(
717 -type => 'text/html',
718 -charset => 'utf-8',
719 -cookie => $cookie
721 $template->output;
722 exit;
725 =item check_api_auth
727 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
729 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
730 cookie, determine if the user has the privileges specified by C<$userflags>.
732 C<check_api_auth> is is meant for authenticating users of web services, and
733 consequently will always return and will not attempt to redirect the user
734 agent.
736 If a valid session cookie is already present, check_api_auth will return a status
737 of "ok", the cookie, and the Koha session ID.
739 If no session cookie is present, check_api_auth will check the 'userid' and 'password
740 parameters and create a session cookie and Koha session if the supplied credentials
741 are OK.
743 Possible return values in C<$status> are:
745 =over 4
747 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
749 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
751 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
753 =item "expired -- session cookie has expired; API user should resubmit userid and password
755 =back
757 =cut
759 sub check_api_auth {
760 my $query = shift;
761 my $flagsrequired = shift;
763 my $dbh = C4::Context->dbh;
764 my $timeout = C4::Context->preference('timeout');
765 $timeout = 600 unless $timeout;
767 unless (C4::Context->preference('Version')) {
768 # database has not been installed yet
769 return ("maintenance", undef, undef);
771 my $kohaversion=C4::Context::KOHAVERSION;
772 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
773 if (C4::Context->preference('Version') < $kohaversion) {
774 # database in need of version update; assume that
775 # no API should be called while databsae is in
776 # this condition.
777 return ("maintenance", undef, undef);
780 # FIXME -- most of what follows is a copy-and-paste
781 # of code from checkauth. There is an obvious need
782 # for refactoring to separate the various parts of
783 # the authentication code, but as of 2007-11-19 this
784 # is deferred so as to not introduce bugs into the
785 # regular authentication code for Koha 3.0.
787 # see if we have a valid session cookie already
788 # however, if a userid parameter is present (i.e., from
789 # a form submission, assume that any current cookie
790 # is to be ignored
791 my $sessionID = undef;
792 unless ($query->param('userid')) {
793 $sessionID = $query->cookie("CGISESSID");
795 if ($sessionID) {
796 my $session = get_session($sessionID);
797 C4::Context->_new_userenv($sessionID);
798 if ($session) {
799 C4::Context::set_userenv(
800 $session->param('number'), $session->param('id'),
801 $session->param('cardnumber'), $session->param('firstname'),
802 $session->param('surname'), $session->param('branch'),
803 $session->param('branchname'), $session->param('flags'),
804 $session->param('emailaddress'), $session->param('branchprinter')
807 my $ip = $session->param('ip');
808 my $lasttime = $session->param('lasttime');
809 my $userid = $session->param('id');
810 if ( $lasttime < time() - $timeout ) {
811 # time out
812 $session->delete();
813 C4::Context->_unset_userenv($sessionID);
814 $userid = undef;
815 $sessionID = undef;
816 return ("expired", undef, undef);
817 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
818 # IP address changed
819 $session->delete();
820 C4::Context->_unset_userenv($sessionID);
821 $userid = undef;
822 $sessionID = undef;
823 return ("expired", undef, undef);
824 } else {
825 my $cookie = $query->cookie( CGISESSID => $session->id );
826 $session->param('lasttime',time());
827 my $flags = haspermission( $dbh, $userid, $flagsrequired );
828 if ($flags) {
829 return ("ok", $cookie, $sessionID);
830 } else {
831 $session->delete();
832 C4::Context->_unset_userenv($sessionID);
833 $userid = undef;
834 $sessionID = undef;
835 return ("failed", undef, undef);
838 } else {
839 return ("expired", undef, undef);
841 } else {
842 # new login
843 my $userid = $query->param('userid');
844 my $password = $query->param('password');
845 unless ($userid and $password) {
846 # caller did something wrong, fail the authenticateion
847 return ("failed", undef, undef);
849 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
850 if ($return and haspermission( $dbh, $userid, $flagsrequired)) {
851 my $session = get_session("");
852 return ("failed", undef, undef) unless $session;
854 my $sessionID = $session->id;
855 C4::Context->_new_userenv($sessionID);
856 my $cookie = $query->cookie(CGISESSID => $sessionID);
857 if ( $return == 1 ) {
858 my (
859 $borrowernumber, $firstname, $surname,
860 $userflags, $branchcode, $branchname,
861 $branchprinter, $emailaddress
863 my $sth =
864 $dbh->prepare(
865 "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=?"
867 $sth->execute($userid);
869 $borrowernumber, $firstname, $surname,
870 $userflags, $branchcode, $branchname,
871 $branchprinter, $emailaddress
872 ) = $sth->fetchrow if ( $sth->rows );
874 unless ($sth->rows ) {
875 my $sth = $dbh->prepare(
876 "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=?"
878 $sth->execute($cardnumber);
880 $borrowernumber, $firstname, $surname,
881 $userflags, $branchcode, $branchname,
882 $branchprinter, $emailaddress
883 ) = $sth->fetchrow if ( $sth->rows );
885 unless ( $sth->rows ) {
886 $sth->execute($userid);
888 $borrowernumber, $firstname, $surname, $userflags,
889 $branchcode, $branchname, $branchprinter, $emailaddress
890 ) = $sth->fetchrow if ( $sth->rows );
894 my $ip = $ENV{'REMOTE_ADDR'};
895 # if they specify at login, use that
896 if ($query->param('branch')) {
897 $branchcode = $query->param('branch');
898 $branchname = GetBranchName($branchcode);
900 my $branches = GetBranches();
901 my @branchesloop;
902 foreach my $br ( keys %$branches ) {
903 # now we work with the treatment of ip
904 my $domain = $branches->{$br}->{'branchip'};
905 if ( $domain && $ip =~ /^$domain/ ) {
906 $branchcode = $branches->{$br}->{'branchcode'};
908 # new op dev : add the branchprinter and branchname in the cookie
909 $branchprinter = $branches->{$br}->{'branchprinter'};
910 $branchname = $branches->{$br}->{'branchname'};
913 $session->param('number',$borrowernumber);
914 $session->param('id',$userid);
915 $session->param('cardnumber',$cardnumber);
916 $session->param('firstname',$firstname);
917 $session->param('surname',$surname);
918 $session->param('branch',$branchcode);
919 $session->param('branchname',$branchname);
920 $session->param('flags',$userflags);
921 $session->param('emailaddress',$emailaddress);
922 $session->param('ip',$session->remote_addr());
923 $session->param('lasttime',time());
924 } elsif ( $return == 2 ) {
925 #We suppose the user is the superlibrarian
926 $session->param('number',0);
927 $session->param('id',C4::Context->config('user'));
928 $session->param('cardnumber',C4::Context->config('user'));
929 $session->param('firstname',C4::Context->config('user'));
930 $session->param('surname',C4::Context->config('user'));
931 $session->param('branch','NO_LIBRARY_SET');
932 $session->param('branchname','NO_LIBRARY_SET');
933 $session->param('flags',1);
934 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
935 $session->param('ip',$session->remote_addr());
936 $session->param('lasttime',time());
938 C4::Context::set_userenv(
939 $session->param('number'), $session->param('id'),
940 $session->param('cardnumber'), $session->param('firstname'),
941 $session->param('surname'), $session->param('branch'),
942 $session->param('branchname'), $session->param('flags'),
943 $session->param('emailaddress'), $session->param('branchprinter')
945 return ("ok", $cookie, $sessionID);
946 } else {
947 return ("failed", undef, undef);
952 =item check_cookie_auth
954 ($status, $sessionId) = check_api_auth($cookie, $userflags);
956 Given a CGISESSID cookie set during a previous login to Koha, determine
957 if the user has the privileges specified by C<$userflags>.
959 C<check_cookie_auth> is meant for authenticating special services
960 such as tools/upload-file.pl that are invoked by other pages that
961 have been authenticated in the usual way.
963 Possible return values in C<$status> are:
965 =over 4
967 =item "ok" -- user authenticated; C<$sessionID> have valid values.
969 =item "failed" -- credentials are not correct; C<$sessionid> are undef
971 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
973 =item "expired -- session cookie has expired; API user should resubmit userid and password
975 =back
977 =cut
979 sub check_cookie_auth {
980 my $cookie = shift;
981 my $flagsrequired = shift;
983 my $dbh = C4::Context->dbh;
984 my $timeout = C4::Context->preference('timeout');
985 $timeout = 600 unless $timeout;
987 unless (C4::Context->preference('Version')) {
988 # database has not been installed yet
989 return ("maintenance", undef);
991 my $kohaversion=C4::Context::KOHAVERSION;
992 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
993 if (C4::Context->preference('Version') < $kohaversion) {
994 # database in need of version update; assume that
995 # no API should be called while databsae is in
996 # this condition.
997 return ("maintenance", undef);
1000 # FIXME -- most of what follows is a copy-and-paste
1001 # of code from checkauth. There is an obvious need
1002 # for refactoring to separate the various parts of
1003 # the authentication code, but as of 2007-11-23 this
1004 # is deferred so as to not introduce bugs into the
1005 # regular authentication code for Koha 3.0.
1007 # see if we have a valid session cookie already
1008 # however, if a userid parameter is present (i.e., from
1009 # a form submission, assume that any current cookie
1010 # is to be ignored
1011 unless (defined $cookie and $cookie) {
1012 return ("failed", undef);
1014 my $sessionID = $cookie;
1015 my $session = get_session($sessionID);
1016 C4::Context->_new_userenv($sessionID);
1017 if ($session) {
1018 C4::Context::set_userenv(
1019 $session->param('number'), $session->param('id'),
1020 $session->param('cardnumber'), $session->param('firstname'),
1021 $session->param('surname'), $session->param('branch'),
1022 $session->param('branchname'), $session->param('flags'),
1023 $session->param('emailaddress'), $session->param('branchprinter')
1026 my $ip = $session->param('ip');
1027 my $lasttime = $session->param('lasttime');
1028 my $userid = $session->param('id');
1029 if ( $lasttime < time() - $timeout ) {
1030 # time out
1031 $session->delete();
1032 C4::Context->_unset_userenv($sessionID);
1033 $userid = undef;
1034 $sessionID = undef;
1035 return ("expired", undef);
1036 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1037 # IP address changed
1038 $session->delete();
1039 C4::Context->_unset_userenv($sessionID);
1040 $userid = undef;
1041 $sessionID = undef;
1042 return ("expired", undef);
1043 } else {
1044 $session->param('lasttime',time());
1045 my $flags = haspermission( $dbh, $userid, $flagsrequired );
1046 if ($flags) {
1047 return ("ok", $sessionID);
1048 } else {
1049 $session->delete();
1050 C4::Context->_unset_userenv($sessionID);
1051 $userid = undef;
1052 $sessionID = undef;
1053 return ("failed", undef);
1056 } else {
1057 return ("expired", undef);
1061 =item get_session
1063 use CGI::Session;
1064 my $session = get_session($sessionID);
1066 Given a session ID, retrieve the CGI::Session object used to store
1067 the session's state. The session object can be used to store
1068 data that needs to be accessed by different scripts during a
1069 user's session.
1071 If the C<$sessionID> parameter is an empty string, a new session
1072 will be created.
1074 =cut
1076 sub get_session {
1077 my $sessionID = shift;
1078 my $storage_method = C4::Context->preference('SessionStorage');
1079 my $dbh = C4::Context->dbh;
1080 my $session;
1081 if ($storage_method eq 'mysql'){
1082 $session = new CGI::Session("driver:MySQL", $sessionID, {Handle=>$dbh});
1084 elsif ($storage_method eq 'Pg') {
1085 $session = new CGI::Session("driver:PostgreSQL", $sessionID, {Handle=>$dbh});
1087 else {
1088 # catch all defaults to tmp should work on all systems
1089 $session = new CGI::Session("driver:File", $sessionID, {Directory=>'/tmp'});
1091 return $session;
1094 sub checkpw {
1096 my ( $dbh, $userid, $password ) = @_;
1097 if ($ldap) {
1098 $debug and print "## checkpw - checking LDAP\n";
1099 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1100 ($retval) and return ($retval,$retcard);
1103 # INTERNAL AUTH
1104 my $sth =
1105 $dbh->prepare(
1106 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1108 $sth->execute($userid);
1109 if ( $sth->rows ) {
1110 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1111 $surname, $branchcode, $flags )
1112 = $sth->fetchrow;
1113 if ( md5_base64($password) eq $md5password ) {
1115 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1116 $firstname, $surname, $branchcode, $flags );
1117 return 1, $cardnumber;
1120 $sth =
1121 $dbh->prepare(
1122 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1124 $sth->execute($userid);
1125 if ( $sth->rows ) {
1126 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1127 $surname, $branchcode, $flags )
1128 = $sth->fetchrow;
1129 if ( md5_base64($password) eq $md5password ) {
1131 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1132 $firstname, $surname, $branchcode, $flags );
1133 return 1, $userid;
1136 if ( $userid && $userid eq C4::Context->config('user')
1137 && "$password" eq C4::Context->config('pass') )
1140 # Koha superuser account
1141 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1142 return 2;
1144 if ( $userid && $userid eq 'demo'
1145 && "$password" eq 'demo'
1146 && C4::Context->config('demo') )
1149 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1150 # some features won't be effective : modify systempref, modify MARC structure,
1151 return 2;
1153 return 0;
1156 =item getuserflags
1158 $authflags = getuserflags($flags,$dbh);
1159 Translates integer flags into permissions strings hash.
1161 C<$flags> is the integer userflags value ( borrowers.userflags )
1162 C<$authflags> is a hashref of permissions
1164 =cut
1166 sub getuserflags {
1167 my $flags = shift;
1168 my $dbh = shift;
1169 my $userflags;
1170 $flags = 0 unless $flags;
1171 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1172 $sth->execute;
1174 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1175 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1176 $userflags->{$flag} = 1;
1178 else {
1179 $userflags->{$flag} = 0;
1182 return $userflags;
1185 =item haspermission
1187 $flags = ($dbh,$member,$flagsrequired);
1189 C<$member> may be either userid or overloaded with $borrower hashref from GetMemberDetails.
1190 C<$flags> is a hashref of required flags lik C<$borrower-&lt;{authflags}>
1192 Returns member's flags or 0 if a permission is not met.
1194 =cut
1196 sub haspermission {
1197 my ( $dbh, $userid, $flagsrequired ) = @_;
1198 my ($flags,$intflags);
1199 $dbh=C4::Context->dbh unless($dbh);
1200 if(ref($userid)) {
1201 $intflags = $userid->{'flags'};
1202 } else {
1203 my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1204 $sth->execute($userid);
1205 my ($intflags) = $sth->fetchrow;
1206 $flags = getuserflags( $intflags, $dbh );
1208 if ( $userid eq C4::Context->config('user') ) {
1209 # Super User Account from /etc/koha.conf
1210 $flags->{'superlibrarian'} = 1;
1212 if ( $userid eq 'demo' && C4::Context->config('demo') ) {
1213 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1214 $flags->{'superlibrarian'} = 1;
1216 return $flags if $flags->{superlibrarian};
1217 foreach ( keys %$flagsrequired ) {
1218 return 0 unless( $flags->{$_} );
1220 return $flags;
1221 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1225 sub getborrowernumber {
1226 my ($userid) = @_;
1227 my $dbh = C4::Context->dbh;
1228 for my $field ( 'userid', 'cardnumber' ) {
1229 my $sth =
1230 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1231 $sth->execute($userid);
1232 if ( $sth->rows ) {
1233 my ($bnumber) = $sth->fetchrow;
1234 return $bnumber;
1237 return 0;
1240 END { } # module clean-up code here (global destructor)
1242 __END__
1244 =back
1246 =head1 SEE ALSO
1248 CGI(3)
1250 C4::Output(3)
1252 Digest::MD5(3)
1254 =cut