3 # NOTE: This file uses 8-character tabs; do not change the tab size!
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
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
25 use Digest::MD5 qw(md5_base64);
30 use C4::Output; # to get the template
33 use C4::Branch; # GetBranches
34 use C4::VirtualShelves 3.02 qw(GetShelvesSummary);
37 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap);
40 $VERSION = 3.02; # set version for version checking
41 $debug = $ENV{DEBUG} || 0 ;
43 @EXPORT = qw(&checkauth &get_template_and_user);
44 @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &get_all_subpermissions &get_user_subpermissions);
45 %EXPORT_TAGS = (EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)]);
46 $ldap = C4::Context->config('useldapserver') || 0;
48 require C4::Auth_with_ldap; # no import
49 import C4::Auth_with_ldap qw(checkpw_ldap);
55 C4::Auth - Authenticates Koha users
65 my ($template, $borrowernumber, $cookie)
66 = get_template_and_user(
68 template_name => "opac-main.tmpl",
72 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
76 output_html_with_http_headers $query, $cookie, $template->output;
80 The main function of this module is to provide
81 authentification. However the get_template_and_user function has
82 been provided so that a users login information is passed along
83 automatically. This gets loaded into the template.
89 =item get_template_and_user
91 my ($template, $borrowernumber, $cookie)
92 = get_template_and_user(
94 template_name => "opac-main.tmpl",
98 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
102 This call passes the C<query>, C<flagsrequired> and C<authnotrequired>
103 to C<&checkauth> (in this module) to perform authentification.
104 See C<&checkauth> for an explanation of these parameters.
106 The C<template_name> is then used to find the correct template for
107 the page. The authenticated users details are loaded onto the
108 template in the HTML::Template LOOP variable C<USER_INFO>. Also the
109 C<sessionID> is passed to the template. This can be used in templates
110 if cookies are disabled. It needs to be put as and input to every
113 More information on the C<gettemplate> sub can be found in the
118 sub get_template_and_user {
121 gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'} );
122 my ( $user, $cookie, $sessionID, $flags ) = checkauth(
124 $in->{'authnotrequired'},
125 $in->{'flagsrequired'},
127 ) unless ($in->{'template_name'}=~/maintenance/);
130 my $insecure = C4::Context->preference('insecure');
131 if ($user or $insecure) {
133 # load the template variables for stylesheets and JavaScript
134 $template->param( css_libs => $in->{'css_libs'} );
135 $template->param( css_module => $in->{'css_module'} );
136 $template->param( css_page => $in->{'css_page'} );
137 $template->param( css_widgets => $in->{'css_widgets'} );
139 $template->param( js_libs => $in->{'js_libs'} );
140 $template->param( js_module => $in->{'js_module'} );
141 $template->param( js_page => $in->{'js_page'} );
142 $template->param( js_widgets => $in->{'js_widgets'} );
145 $template->param( loggedinusername => $user );
146 $template->param( sessionID => $sessionID );
148 my ($pubshelves, $barshelves) = C4::Context->get_shelves_userenv();
149 if (defined($pubshelves)) {
150 $template->param( pubshelves => scalar (@$pubshelves));
151 $template->param( pubshelvesloop => $pubshelves);
153 if (defined($barshelves)) {
154 $template->param( barshelves => scalar (@$barshelves));
155 $template->param( barshelvesloop => $barshelves);
158 $borrowernumber = getborrowernumber($user);
159 my ( $borr ) = GetMemberDetails( $borrowernumber );
162 $template->param( "USER_INFO" => \@bordat );
164 my $all_perms = get_all_subpermissions();
166 my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow
167 editcatalogue updatecharges management tools editauthorities serials reports);
168 # We are going to use the $flags returned by checkauth
169 # to create the template's parameters that will indicate
170 # which menus the user can access.
171 if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) {
172 $template->param( CAN_user_circulate => 1 );
173 $template->param( CAN_user_catalogue => 1 );
174 $template->param( CAN_user_parameters => 1 );
175 $template->param( CAN_user_borrowers => 1 );
176 $template->param( CAN_user_permissions => 1 );
177 $template->param( CAN_user_reserveforothers => 1 );
178 $template->param( CAN_user_borrow => 1 );
179 $template->param( CAN_user_editcatalogue => 1 );
180 $template->param( CAN_user_updatecharges => 1 );
181 $template->param( CAN_user_acquisition => 1 );
182 $template->param( CAN_user_management => 1 );
183 $template->param( CAN_user_tools => 1 );
184 $template->param( CAN_user_editauthorities => 1 );
185 $template->param( CAN_user_serials => 1 );
186 $template->param( CAN_user_reports => 1 );
187 $template->param( CAN_user_staffaccess => 1 );
188 foreach my $module (keys %$all_perms) {
189 foreach my $subperm (keys %{ $all_perms->{$module} }) {
190 $template->param( "CAN_user_${module}_${subperm}" => 1 );
195 if (C4::Context->preference('GranularPermissions')) {
197 foreach my $module (keys %$all_perms) {
198 if ( $flags->{$module} == 1) {
199 foreach my $subperm (keys %{ $all_perms->{$module} }) {
200 $template->param( "CAN_user_${module}_${subperm}" => 1 );
202 } elsif ( ref($flags->{$module}) ) {
203 foreach my $subperm (keys %{ $flags->{$module} } ) {
204 $template->param( "CAN_user_${module}_${subperm}" => 1 );
210 foreach my $module (keys %$all_perms) {
211 foreach my $subperm (keys %{ $all_perms->{$module} }) {
212 $template->param( "CAN_user_${module}_${subperm}" => 1 );
218 foreach my $module (keys %$flags) {
219 if ( $flags->{$module} == 1 or ref($flags->{$module}) ) {
220 $template->param( "CAN_user_$module" => 1 );
221 if ($module eq "parameters") {
222 $template->param( CAN_user_management => 1 );
228 else { # if this is an anonymous session, setup to display public lists...
230 # load the template variables for stylesheets and JavaScript
231 $template->param( css_libs => $in->{'css_libs'} );
232 $template->param( css_module => $in->{'css_module'} );
233 $template->param( css_page => $in->{'css_page'} );
234 $template->param( css_widgets => $in->{'css_widgets'} );
236 $template->param( js_libs => $in->{'js_libs'} );
237 $template->param( js_module => $in->{'js_module'} );
238 $template->param( js_page => $in->{'js_page'} );
239 $template->param( js_widgets => $in->{'js_widgets'} );
241 $template->param( sessionID => $sessionID );
243 my ($pubshelves) = C4::Context->get_shelves_userenv(); # an anonymous user has no 'barshelves'...
244 if (defined(($pubshelves))) {
245 $template->param( pubshelves => scalar (@$pubshelves));
246 $template->param( pubshelvesloop => $pubshelves);
251 # these template parameters are set the same regardless of $in->{'type'}
253 "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
254 EnhancedMessagingPreferences => C4::Context->preference('EnhancedMessagingPreferences'),
255 GoogleJackets => C4::Context->preference("GoogleJackets"),
256 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
257 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
258 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
259 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
260 TagsEnabled => C4::Context->preference("TagsEnabled"),
261 hide_marc => C4::Context->preference("hide_marc"),
262 'item-level_itypes' => C4::Context->preference('item-level_itypes'),
263 patronimages => C4::Context->preference("patronimages"),
264 singleBranchMode => C4::Context->preference("singleBranchMode"),
267 if ( $in->{'type'} eq "intranet" ) {
269 AmazonContent => C4::Context->preference("AmazonContent"),
270 AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
271 AutoLocation => C4::Context->preference("AutoLocation"),
272 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
273 CircAutocompl => C4::Context->preference("CircAutocompl"),
274 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
275 IndependantBranches => C4::Context->preference("IndependantBranches"),
276 IntranetNav => C4::Context->preference("IntranetNav"),
277 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
278 LibraryName => C4::Context->preference("LibraryName"),
279 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
280 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
281 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
282 canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
283 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
284 intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
285 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
286 intranetuserjs => C4::Context->preference("intranetuserjs"),
287 noItemTypeImages => C4::Context->preference("noItemTypeImages"),
288 suggestion => C4::Context->preference("suggestion"),
289 virtualshelves => C4::Context->preference("virtualshelves"),
293 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
294 my $LibraryNameTitle = C4::Context->preference("LibraryName");
295 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
296 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
298 AmazonContent => "" . C4::Context->preference("AmazonContent"),
299 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
300 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
301 LibraryName => "" . C4::Context->preference("LibraryName"),
302 LibraryNameTitle => "" . $LibraryNameTitle,
303 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
304 OPACAmazonSimilarItems => "" . C4::Context->preference("OPACAmazonSimilarItems"),
305 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
306 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
307 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
308 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
309 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
310 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
311 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
312 OPACBaseURL => ($in->{'query'}->https() ? "https://" : "http://") .
313 $ENV{'SERVER_NAME'} .
314 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
315 OpacBrowser => C4::Context->preference("OpacBrowser"),
316 OpacCloud => C4::Context->preference("OpacCloud"),
317 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
318 OpacNav => "" . C4::Context->preference("OpacNav"),
319 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
320 OpacTopissue => C4::Context->preference("OpacTopissue"),
321 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
322 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
323 'Version' => C4::Context->preference('Version'),
324 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
325 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
326 hidelostitems => C4::Context->preference("hidelostitems"),
327 mylibraryfirst => C4::Context->preference("SearchMyLibraryFirst"),
328 opacbookbag => "" . C4::Context->preference("opacbookbag"),
329 opaccolorstylesheet => "". C4::Context->preference("opaccolorstylesheet"),
330 opaccredits => "" . C4::Context->preference("opaccredits"),
331 opacheader => "" . C4::Context->preference("opacheader"),
332 opaclanguagesdisplay => "". C4::Context->preference("opaclanguagesdisplay"),
333 opaclayoutstylesheet => "". C4::Context->preference("opaclayoutstylesheet"),
334 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
335 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
336 opacuserjs => C4::Context->preference("opacuserjs"),
337 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
338 reviewson => C4::Context->preference("reviewson"),
339 suggestion => "" . C4::Context->preference("suggestion"),
340 virtualshelves => "" . C4::Context->preference("virtualshelves"),
343 $template->param(listloop=>[{shelfname=>"Freelist", shelfnumber=>110}]);
344 return ( $template, $borrowernumber, $cookie, $flags);
349 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
351 Verifies that the user is authorized to run this script. If
352 the user is authorized, a (userid, cookie, session-id, flags)
353 quadruple is returned. If the user is not authorized but does
354 not have the required privilege (see $flagsrequired below), it
355 displays an error page and exits. Otherwise, it displays the
356 login page and exits.
358 Note that C<&checkauth> will return if and only if the user
359 is authorized, so it should be called early on, before any
360 unfinished operations (e.g., if you've opened a file, then
361 C<&checkauth> won't close it for you).
363 C<$query> is the CGI object for the script calling C<&checkauth>.
365 The C<$noauth> argument is optional. If it is set, then no
366 authorization is required for the script.
368 C<&checkauth> fetches user and session information from C<$query> and
369 ensures that the user is authorized to run scripts that require
372 The C<$flagsrequired> argument specifies the required privileges
373 the user must have if the username and password are correct.
374 It should be specified as a reference-to-hash; keys in the hash
375 should be the "flags" for the user, as specified in the Members
376 intranet module. Any key specified must correspond to a "flag"
377 in the userflags table. E.g., { circulate => 1 } would specify
378 that the user must have the "circulate" privilege in order to
379 proceed. To make sure that access control is correct, the
380 C<$flagsrequired> parameter must be specified correctly.
382 If the GranularPermissions system preference is ON, the
383 value of each key in the C<flagsrequired> hash takes on an additional
388 The user must have access to all subfunctions of the module
389 specified by the hash key.
393 The user must have access to at least one subfunction of the module
394 specified by the hash key.
396 =item specific permission, e.g., 'export_catalog'
398 The user must have access to the specific subfunction list, which
399 must correspond to a row in the permissions table.
401 The C<$type> argument specifies whether the template should be
402 retrieved from the opac or intranet directory tree. "opac" is
403 assumed if it is not specified; however, if C<$type> is specified,
404 "intranet" is assumed if it is not "opac".
406 If C<$query> does not have a valid session ID associated with it
407 (i.e., the user has not logged in) or if the session has expired,
408 C<&checkauth> presents the user with a login page (from the point of
409 view of the original script, C<&checkauth> does not return). Once the
410 user has authenticated, C<&checkauth> restarts the original script
411 (this time, C<&checkauth> returns).
413 The login page is provided using a HTML::Template, which is set in the
414 systempreferences table or at the top of this file. The variable C<$type>
415 selects which template to use, either the opac or the intranet
416 authentification template.
418 C<&checkauth> returns a user ID, a cookie, and a session ID. The
419 cookie should be sent back to the browser; it verifies that the user
424 sub _version_check ($$) {
428 # If Version syspref is unavailable, it means Koha is beeing installed,
429 # and so we must redirect to OPAC maintenance page or to the WebInstaller
430 # also, if OpacMaintenance is ON, OPAC should redirect to maintenance
431 if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') {
432 warn "OPAC Install required, redirecting to maintenance";
433 print $query->redirect("/cgi-bin/koha/maintenance.pl");
435 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
436 if ($type ne 'opac') {
437 warn "Install required, redirecting to Installer";
438 print $query->redirect("/cgi-bin/koha/installer/install.pl");
441 warn "OPAC Install required, redirecting to maintenance";
442 print $query->redirect("/cgi-bin/koha/maintenance.pl");
447 # check that database and koha version are the same
448 # there is no DB version, it's a fresh install,
449 # go to web installer
450 # there is a DB version, compare it to the code version
451 my $kohaversion=C4::Context::KOHAVERSION;
452 # remove the 3 last . to have a Perl number
453 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
454 $debug and print STDERR "kohaversion : $kohaversion\n";
455 if ($version < $kohaversion){
456 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
457 if ($type ne 'opac'){
458 warn sprintf($warning, 'Installer');
459 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
461 warn sprintf("OPAC: " . $warning, 'maintenance');
462 print $query->redirect("/cgi-bin/koha/maintenance.pl");
470 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
471 printf L join("\n",@_);
477 $debug and warn "Checking Auth";
478 # $authnotrequired will be set for scripts which will run without authentication
479 my $authnotrequired = shift;
480 my $flagsrequired = shift;
482 $type = 'opac' unless $type;
484 my $dbh = C4::Context->dbh;
485 my $timeout = C4::Context->preference('timeout');
487 if ($timeout =~ /(\d+)[dD]/) {
488 $timeout = $1 * 86400;
490 $timeout = 600 unless $timeout;
492 _version_check($type,$query);
496 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
497 my $logout = $query->param('logout.x');
499 if ( $userid = $ENV{'REMOTE_USER'} ) {
500 # Using Basic Authentication, no cookies required
501 $cookie = $query->cookie(
502 -name => 'CGISESSID',
508 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
509 my $session = get_session($sessionID);
510 C4::Context->_new_userenv($sessionID);
511 my ($ip, $lasttime, $sessiontype);
513 C4::Context::set_userenv(
514 $session->param('number'), $session->param('id'),
515 $session->param('cardnumber'), $session->param('firstname'),
516 $session->param('surname'), $session->param('branch'),
517 $session->param('branchname'), $session->param('flags'),
518 $session->param('emailaddress'), $session->param('branchprinter')
520 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
521 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
522 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
523 $ip = $session->param('ip');
524 $lasttime = $session->param('lasttime');
525 $userid = $session->param('id');
526 $sessiontype = $session->param('sessiontype');
529 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
530 #if a user enters an id ne to the id in the current session, we need to log them in...
531 #first we need to clear the anonymous session...
532 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
535 C4::Context->_unset_userenv($sessionID);
540 # voluntary logout the user
543 C4::Context->_unset_userenv($sessionID);
544 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,localtime);
548 elsif ( $lasttime < time() - $timeout ) {
550 $info{'timed_out'} = 1;
552 C4::Context->_unset_userenv($sessionID);
553 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,localtime);
557 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
558 # Different ip than originally logged in from
559 $info{'oldip'} = $ip;
560 $info{'newip'} = $ENV{'REMOTE_ADDR'};
561 $info{'different_ip'} = 1;
563 C4::Context->_unset_userenv($sessionID);
564 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,localtime, $info{'newip'});
569 $cookie = $query->cookie( CGISESSID => $session->id );
570 $session->param('lasttime',time());
571 unless ( $sessiontype eq 'anon' ) { #if this is an anonymous session, we want to update the session, but not behave as if they are logged in...
572 $flags = haspermission( $dbh, $userid, $flagsrequired );
576 $info{'nopermission'} = 1;
581 unless ($userid || $sessionID) {
582 #we initiate a session prior to checking for a username to allow for anonymous sessions...
583 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
584 my $sessionID = $session->id;
585 C4::Context->_new_userenv($sessionID);
586 $cookie = $query->cookie(CGISESSID => $sessionID);
587 if ( $userid = $query->param('userid') ) {
588 my $password = $query->param('password');
589 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
591 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},localtime);
592 if ( $flags = haspermission( $dbh, $userid, $flagsrequired ) ) {
596 $info{'nopermission'} = 1;
597 C4::Context->_unset_userenv($sessionID);
600 my ($borrowernumber, $firstname, $surname, $userflags,
601 $branchcode, $branchname, $branchprinter, $emailaddress);
603 if ( $return == 1 ) {
605 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
606 branches.branchname as branchname,
607 branches.branchprinter as branchprinter,
610 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
612 my $sth = $dbh->prepare("$select where userid=?");
613 $sth->execute($userid);
614 unless ($sth->rows) {
615 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
616 $sth = $dbh->prepare("$select where cardnumber=?");
617 $sth->execute($cardnumber);
618 unless ($sth->rows) {
619 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
620 $sth->execute($userid);
621 unless ($sth->rows) {
622 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
627 ($borrowernumber, $firstname, $surname, $userflags,
628 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
629 $debug and print STDERR "AUTH_3 results: " .
630 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
632 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
635 # launch a sequence to check if we have a ip for the branch, i
636 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
638 my $ip = $ENV{'REMOTE_ADDR'};
639 # if they specify at login, use that
640 if ($query->param('branch')) {
641 $branchcode = $query->param('branch');
642 $branchname = GetBranchName($branchcode);
644 my $branches = GetBranches();
645 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
646 # we have to check they are coming from the right ip range
647 my $domain = $branches->{$branchcode}->{'branchip'};
648 if ($ip !~ /^$domain/){
650 $info{'wrongip'} = 1;
655 foreach my $br ( keys %$branches ) {
656 # now we work with the treatment of ip
657 my $domain = $branches->{$br}->{'branchip'};
658 if ( $domain && $ip =~ /^$domain/ ) {
659 $branchcode = $branches->{$br}->{'branchcode'};
661 # new op dev : add the branchprinter and branchname in the cookie
662 $branchprinter = $branches->{$br}->{'branchprinter'};
663 $branchname = $branches->{$br}->{'branchname'};
666 $session->param('number',$borrowernumber);
667 $session->param('id',$userid);
668 $session->param('cardnumber',$cardnumber);
669 $session->param('firstname',$firstname);
670 $session->param('surname',$surname);
671 $session->param('branch',$branchcode);
672 $session->param('branchname',$branchname);
673 $session->param('flags',$userflags);
674 $session->param('emailaddress',$emailaddress);
675 $session->param('ip',$session->remote_addr());
676 $session->param('lasttime',time());
677 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
679 elsif ( $return == 2 ) {
680 #We suppose the user is the superlibrarian
682 $session->param('number',0);
683 $session->param('id',C4::Context->config('user'));
684 $session->param('cardnumber',C4::Context->config('user'));
685 $session->param('firstname',C4::Context->config('user'));
686 $session->param('surname',C4::Context->config('user'));
687 $session->param('branch','NO_LIBRARY_SET');
688 $session->param('branchname','NO_LIBRARY_SET');
689 $session->param('flags',1);
690 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
691 $session->param('ip',$session->remote_addr());
692 $session->param('lasttime',time());
694 C4::Context::set_userenv(
695 $session->param('number'), $session->param('id'),
696 $session->param('cardnumber'), $session->param('firstname'),
697 $session->param('surname'), $session->param('branch'),
698 $session->param('branchname'), $session->param('flags'),
699 $session->param('emailaddress'), $session->param('branchprinter')
702 # Grab borrower's shelves and add to the session...
703 $barshelves = GetShelvesSummary($borrowernumber,2,10);
704 $session->param('barshelves', $barshelves);
705 C4::Context::set_shelves_userenv('bar',$barshelves);
707 # Grab the public shelves and add to the session...
708 $pubshelves = GetShelvesSummary(0,2,10);
709 $session->param('pubshelves', $pubshelves);
710 C4::Context::set_shelves_userenv('pub',$pubshelves);
714 $info{'invalid_username_or_password'} = 1;
715 C4::Context->_unset_userenv($sessionID);
718 } # END if ( $userid = $query->param('userid') )
719 elsif ($type eq "opac") {
720 # if we are here this is an anonymous session; add public lists to it and a few other items...
721 # anonymous sessions are created only for the OPAC
722 $debug and warn "Initiating an anonymous session...";
724 # Grab the public shelves and add to the session...
725 $pubshelves = GetShelvesSummary(0,2,10);
726 $session->param('pubshelves', $pubshelves);
727 C4::Context::set_shelves_userenv('pub',$pubshelves);
729 # setting a couple of other session vars...
730 $session->param('ip',$session->remote_addr());
731 $session->param('lasttime',time());
732 $session->param('sessiontype','anon');
734 } # END unless ($userid)
735 my $insecure = C4::Context->boolean_preference('insecure');
737 # finished authentification, now respond
738 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
742 $cookie = $query->cookie( CGISESSID => '' );
744 return ( $userid, $cookie, $sessionID, $flags );
749 # AUTH rejected, show the login/password template, after checking the DB.
753 # get the inputs from the incoming query
755 foreach my $name ( param $query) {
756 (next) if ( $name eq 'userid' || $name eq 'password' );
757 my $value = $query->param($name);
758 push @inputs, { name => $name, value => $value };
760 # get the branchloop, which we need for authentication
761 my $branches = GetBranches();
763 for my $branch_hash (sort keys %$branches) {
764 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
767 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
768 my $template = gettemplate( $template_name, $type, $query );
769 $template->param(branchloop => \@branch_loop,);
773 suggestion => C4::Context->preference("suggestion"),
774 virtualshelves => C4::Context->preference("virtualshelves"),
775 LibraryName => C4::Context->preference("LibraryName"),
776 opacuserlogin => C4::Context->preference("opacuserlogin"),
777 OpacNav => C4::Context->preference("OpacNav"),
778 opaccredits => C4::Context->preference("opaccredits"),
779 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
780 opacsmallimage => C4::Context->preference("opacsmallimage"),
781 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
782 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
783 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
784 opacuserjs => C4::Context->preference("opacuserjs"),
785 opacbookbag => "" . C4::Context->preference("opacbookbag"),
786 OpacCloud => C4::Context->preference("OpacCloud"),
787 OpacTopissue => C4::Context->preference("OpacTopissue"),
788 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
789 OpacBrowser => C4::Context->preference("OpacBrowser"),
790 opacheader => C4::Context->preference("opacheader"),
791 TagsEnabled => C4::Context->preference("TagsEnabled"),
792 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
793 intranetcolorstylesheet =>
794 C4::Context->preference("intranetcolorstylesheet"),
795 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
796 IntranetNav => C4::Context->preference("IntranetNav"),
797 intranetuserjs => C4::Context->preference("intranetuserjs"),
798 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
799 IndependantBranches=> C4::Context->preference("IndependantBranches"),
800 AutoLocation => C4::Context->preference("AutoLocation"),
801 wrongip => $info{'wrongip'}
804 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
806 my $self_url = $query->url( -absolute => 1 );
809 LibraryName => C4::Context->preference("LibraryName"),
811 $template->param( \%info );
812 # $cookie = $query->cookie(CGISESSID => $session->id
814 print $query->header(
815 -type => 'text/html',
825 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
827 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
828 cookie, determine if the user has the privileges specified by C<$userflags>.
830 C<check_api_auth> is is meant for authenticating users of web services, and
831 consequently will always return and will not attempt to redirect the user
834 If a valid session cookie is already present, check_api_auth will return a status
835 of "ok", the cookie, and the Koha session ID.
837 If no session cookie is present, check_api_auth will check the 'userid' and 'password
838 parameters and create a session cookie and Koha session if the supplied credentials
841 Possible return values in C<$status> are:
845 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
847 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
849 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
851 =item "expired -- session cookie has expired; API user should resubmit userid and password
859 my $flagsrequired = shift;
861 my $dbh = C4::Context->dbh;
862 my $timeout = C4::Context->preference('timeout');
863 $timeout = 600 unless $timeout;
865 unless (C4::Context->preference('Version')) {
866 # database has not been installed yet
867 return ("maintenance", undef, undef);
869 my $kohaversion=C4::Context::KOHAVERSION;
870 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
871 if (C4::Context->preference('Version') < $kohaversion) {
872 # database in need of version update; assume that
873 # no API should be called while databsae is in
875 return ("maintenance", undef, undef);
878 # FIXME -- most of what follows is a copy-and-paste
879 # of code from checkauth. There is an obvious need
880 # for refactoring to separate the various parts of
881 # the authentication code, but as of 2007-11-19 this
882 # is deferred so as to not introduce bugs into the
883 # regular authentication code for Koha 3.0.
885 # see if we have a valid session cookie already
886 # however, if a userid parameter is present (i.e., from
887 # a form submission, assume that any current cookie
889 my $sessionID = undef;
890 unless ($query->param('userid')) {
891 $sessionID = $query->cookie("CGISESSID");
894 my $session = get_session($sessionID);
895 C4::Context->_new_userenv($sessionID);
897 C4::Context::set_userenv(
898 $session->param('number'), $session->param('id'),
899 $session->param('cardnumber'), $session->param('firstname'),
900 $session->param('surname'), $session->param('branch'),
901 $session->param('branchname'), $session->param('flags'),
902 $session->param('emailaddress'), $session->param('branchprinter')
905 my $ip = $session->param('ip');
906 my $lasttime = $session->param('lasttime');
907 my $userid = $session->param('id');
908 if ( $lasttime < time() - $timeout ) {
911 C4::Context->_unset_userenv($sessionID);
914 return ("expired", undef, undef);
915 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
918 C4::Context->_unset_userenv($sessionID);
921 return ("expired", undef, undef);
923 my $cookie = $query->cookie( CGISESSID => $session->id );
924 $session->param('lasttime',time());
925 my $flags = haspermission( $dbh, $userid, $flagsrequired );
927 return ("ok", $cookie, $sessionID);
930 C4::Context->_unset_userenv($sessionID);
933 return ("failed", undef, undef);
937 return ("expired", undef, undef);
941 my $userid = $query->param('userid');
942 my $password = $query->param('password');
943 unless ($userid and $password) {
944 # caller did something wrong, fail the authenticateion
945 return ("failed", undef, undef);
947 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
948 if ($return and haspermission( $dbh, $userid, $flagsrequired)) {
949 my $session = get_session("");
950 return ("failed", undef, undef) unless $session;
952 my $sessionID = $session->id;
953 C4::Context->_new_userenv($sessionID);
954 my $cookie = $query->cookie(CGISESSID => $sessionID);
955 if ( $return == 1 ) {
957 $borrowernumber, $firstname, $surname,
958 $userflags, $branchcode, $branchname,
959 $branchprinter, $emailaddress
963 "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=?"
965 $sth->execute($userid);
967 $borrowernumber, $firstname, $surname,
968 $userflags, $branchcode, $branchname,
969 $branchprinter, $emailaddress
970 ) = $sth->fetchrow if ( $sth->rows );
972 unless ($sth->rows ) {
973 my $sth = $dbh->prepare(
974 "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=?"
976 $sth->execute($cardnumber);
978 $borrowernumber, $firstname, $surname,
979 $userflags, $branchcode, $branchname,
980 $branchprinter, $emailaddress
981 ) = $sth->fetchrow if ( $sth->rows );
983 unless ( $sth->rows ) {
984 $sth->execute($userid);
986 $borrowernumber, $firstname, $surname, $userflags,
987 $branchcode, $branchname, $branchprinter, $emailaddress
988 ) = $sth->fetchrow if ( $sth->rows );
992 my $ip = $ENV{'REMOTE_ADDR'};
993 # if they specify at login, use that
994 if ($query->param('branch')) {
995 $branchcode = $query->param('branch');
996 $branchname = GetBranchName($branchcode);
998 my $branches = GetBranches();
1000 foreach my $br ( keys %$branches ) {
1001 # now we work with the treatment of ip
1002 my $domain = $branches->{$br}->{'branchip'};
1003 if ( $domain && $ip =~ /^$domain/ ) {
1004 $branchcode = $branches->{$br}->{'branchcode'};
1006 # new op dev : add the branchprinter and branchname in the cookie
1007 $branchprinter = $branches->{$br}->{'branchprinter'};
1008 $branchname = $branches->{$br}->{'branchname'};
1011 $session->param('number',$borrowernumber);
1012 $session->param('id',$userid);
1013 $session->param('cardnumber',$cardnumber);
1014 $session->param('firstname',$firstname);
1015 $session->param('surname',$surname);
1016 $session->param('branch',$branchcode);
1017 $session->param('branchname',$branchname);
1018 $session->param('flags',$userflags);
1019 $session->param('emailaddress',$emailaddress);
1020 $session->param('ip',$session->remote_addr());
1021 $session->param('lasttime',time());
1022 } elsif ( $return == 2 ) {
1023 #We suppose the user is the superlibrarian
1024 $session->param('number',0);
1025 $session->param('id',C4::Context->config('user'));
1026 $session->param('cardnumber',C4::Context->config('user'));
1027 $session->param('firstname',C4::Context->config('user'));
1028 $session->param('surname',C4::Context->config('user'));
1029 $session->param('branch','NO_LIBRARY_SET');
1030 $session->param('branchname','NO_LIBRARY_SET');
1031 $session->param('flags',1);
1032 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1033 $session->param('ip',$session->remote_addr());
1034 $session->param('lasttime',time());
1036 C4::Context::set_userenv(
1037 $session->param('number'), $session->param('id'),
1038 $session->param('cardnumber'), $session->param('firstname'),
1039 $session->param('surname'), $session->param('branch'),
1040 $session->param('branchname'), $session->param('flags'),
1041 $session->param('emailaddress'), $session->param('branchprinter')
1043 return ("ok", $cookie, $sessionID);
1045 return ("failed", undef, undef);
1050 =item check_cookie_auth
1052 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1054 Given a CGISESSID cookie set during a previous login to Koha, determine
1055 if the user has the privileges specified by C<$userflags>.
1057 C<check_cookie_auth> is meant for authenticating special services
1058 such as tools/upload-file.pl that are invoked by other pages that
1059 have been authenticated in the usual way.
1061 Possible return values in C<$status> are:
1065 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1067 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1069 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1071 =item "expired -- session cookie has expired; API user should resubmit userid and password
1077 sub check_cookie_auth {
1079 my $flagsrequired = shift;
1081 my $dbh = C4::Context->dbh;
1082 my $timeout = C4::Context->preference('timeout');
1083 $timeout = 600 unless $timeout;
1085 unless (C4::Context->preference('Version')) {
1086 # database has not been installed yet
1087 return ("maintenance", undef);
1089 my $kohaversion=C4::Context::KOHAVERSION;
1090 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1091 if (C4::Context->preference('Version') < $kohaversion) {
1092 # database in need of version update; assume that
1093 # no API should be called while databsae is in
1095 return ("maintenance", undef);
1098 # FIXME -- most of what follows is a copy-and-paste
1099 # of code from checkauth. There is an obvious need
1100 # for refactoring to separate the various parts of
1101 # the authentication code, but as of 2007-11-23 this
1102 # is deferred so as to not introduce bugs into the
1103 # regular authentication code for Koha 3.0.
1105 # see if we have a valid session cookie already
1106 # however, if a userid parameter is present (i.e., from
1107 # a form submission, assume that any current cookie
1109 unless (defined $cookie and $cookie) {
1110 return ("failed", undef);
1112 my $sessionID = $cookie;
1113 my $session = get_session($sessionID);
1114 C4::Context->_new_userenv($sessionID);
1116 C4::Context::set_userenv(
1117 $session->param('number'), $session->param('id'),
1118 $session->param('cardnumber'), $session->param('firstname'),
1119 $session->param('surname'), $session->param('branch'),
1120 $session->param('branchname'), $session->param('flags'),
1121 $session->param('emailaddress'), $session->param('branchprinter')
1124 my $ip = $session->param('ip');
1125 my $lasttime = $session->param('lasttime');
1126 my $userid = $session->param('id');
1127 if ( $lasttime < time() - $timeout ) {
1130 C4::Context->_unset_userenv($sessionID);
1133 return ("expired", undef);
1134 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1135 # IP address changed
1137 C4::Context->_unset_userenv($sessionID);
1140 return ("expired", undef);
1142 $session->param('lasttime',time());
1143 my $flags = haspermission( $dbh, $userid, $flagsrequired );
1145 return ("ok", $sessionID);
1148 C4::Context->_unset_userenv($sessionID);
1151 return ("failed", undef);
1155 return ("expired", undef);
1162 my $session = get_session($sessionID);
1164 Given a session ID, retrieve the CGI::Session object used to store
1165 the session's state. The session object can be used to store
1166 data that needs to be accessed by different scripts during a
1169 If the C<$sessionID> parameter is an empty string, a new session
1175 my $sessionID = shift;
1176 my $storage_method = C4::Context->preference('SessionStorage');
1177 my $dbh = C4::Context->dbh;
1179 if ($storage_method eq 'mysql'){
1180 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1182 elsif ($storage_method eq 'Pg') {
1183 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1186 # catch all defaults to tmp should work on all systems
1187 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1194 my ( $dbh, $userid, $password ) = @_;
1196 $debug and print "## checkpw - checking LDAP\n";
1197 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1198 ($retval) and return ($retval,$retcard);
1204 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1206 $sth->execute($userid);
1208 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1209 $surname, $branchcode, $flags )
1211 if ( md5_base64($password) eq $md5password ) {
1213 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1214 $firstname, $surname, $branchcode, $flags );
1215 return 1, $cardnumber;
1220 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1222 $sth->execute($userid);
1224 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1225 $surname, $branchcode, $flags )
1227 if ( md5_base64($password) eq $md5password ) {
1229 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1230 $firstname, $surname, $branchcode, $flags );
1234 if ( $userid && $userid eq C4::Context->config('user')
1235 && "$password" eq C4::Context->config('pass') )
1238 # Koha superuser account
1239 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1242 if ( $userid && $userid eq 'demo'
1243 && "$password" eq 'demo'
1244 && C4::Context->config('demo') )
1247 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1248 # some features won't be effective : modify systempref, modify MARC structure,
1256 $authflags = getuserflags($flags,$dbh);
1257 Translates integer flags into permissions strings hash.
1259 C<$flags> is the integer userflags value ( borrowers.userflags )
1260 C<$authflags> is a hashref of permissions
1269 $flags = 0 unless $flags;
1270 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1273 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1274 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1275 $userflags->{$flag} = 1;
1278 $userflags->{$flag} = 0;
1282 # get subpermissions and merge with top-level permissions
1283 my $user_subperms = get_user_subpermissions($userid);
1284 foreach my $module (keys %$user_subperms) {
1285 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1286 $userflags->{$module} = $user_subperms->{$module};
1292 =item get_user_subpermissions
1296 my $user_perm_hashref = get_user_subpermissions($userid);
1300 Given the userid (note, not the borrowernumber) of a staff user,
1301 return a hashref of hashrefs of the specific subpermissions
1302 accorded to the user. An example return is
1306 export_catalog => 1,
1307 import_patrons => 1,
1311 The top-level hash-key is a module or function code from
1312 userflags.flag, while the second-level key is a code
1315 The results of this function do not give a complete picture
1316 of the functions that a staff user can access; it is also
1317 necessary to check borrowers.flags.
1321 sub get_user_subpermissions {
1324 my $dbh = C4::Context->dbh;
1325 my $sth = $dbh->prepare("SELECT flag, code
1326 FROM user_permissions
1327 JOIN permissions USING (module_bit, code)
1328 JOIN userflags ON (module_bit = bit)
1329 JOIN borrowers USING (borrowernumber)
1331 $sth->execute($userid);
1333 my $user_perms = {};
1334 while (my $perm = $sth->fetchrow_hashref) {
1335 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1340 =item get_all_subpermissions
1344 my $perm_hashref = get_all_subpermissions();
1348 Returns a hashref of hashrefs defining all specific
1349 permissions currently defined. The return value
1350 has the same structure as that of C<get_user_subpermissions>,
1351 except that the innermost hash value is the description
1352 of the subpermission.
1356 sub get_all_subpermissions {
1357 my $dbh = C4::Context->dbh;
1358 my $sth = $dbh->prepare("SELECT flag, code, description
1360 JOIN userflags ON (module_bit = bit)");
1364 while (my $perm = $sth->fetchrow_hashref) {
1365 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1372 $flags = ($dbh,$member,$flagsrequired);
1374 C<$member> may be either userid or overloaded with $borrower hashref from GetMemberDetails.
1375 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1377 Returns member's flags or 0 if a permission is not met.
1382 my ( $dbh, $userid, $flagsrequired ) = @_;
1383 my ($flags,$intflags);
1384 $dbh=C4::Context->dbh unless($dbh);
1386 $intflags = $userid->{'flags'};
1388 my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1389 $sth->execute($userid);
1390 my ($intflags) = $sth->fetchrow;
1391 $flags = getuserflags( $intflags, $userid, $dbh );
1393 if ( $userid eq C4::Context->config('user') ) {
1394 # Super User Account from /etc/koha.conf
1395 $flags->{'superlibrarian'} = 1;
1397 if ( $userid eq 'demo' && C4::Context->config('demo') ) {
1398 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1399 $flags->{'superlibrarian'} = 1;
1401 return $flags if $flags->{superlibrarian};
1402 foreach my $module ( keys %$flagsrequired ) {
1403 if (C4::Context->preference('GranularPermissions')) {
1404 my $subperm = $flagsrequired->{$module};
1405 if ($subperm eq '*') {
1406 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1408 return 0 unless ( $flags->{$module} == 1 or
1409 ( ref($flags->{$module}) and
1410 exists $flags->{$module}->{$subperm} and
1411 $flags->{$module}->{$subperm} == 1
1416 return 0 unless ( $flags->{$module} );
1420 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1424 sub getborrowernumber {
1426 my $dbh = C4::Context->dbh;
1427 for my $field ( 'userid', 'cardnumber' ) {
1429 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1430 $sth->execute($userid);
1432 my ($bnumber) = $sth->fetchrow;
1439 END { } # module clean-up code here (global destructor)