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 if ($shelves = C4::Context->get_shelves_userenv()) {
149 $template->param( barshelves => scalar (@$shelves));
150 $template->param( barshelvesloop => $shelves);
153 $borrowernumber = getborrowernumber($user);
154 my ( $borr, $alternativeflags ) =
155 GetMemberDetails( $borrowernumber );
158 $template->param( "USER_INFO" => \@bordat );
160 my $all_perms = get_all_subpermissions();
162 my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow
163 editcatalogue updatecharges management tools editauthorities serials reports);
164 # We are going to use the $flags returned by checkauth
165 # to create the template's parameters that will indicate
166 # which menus the user can access.
167 if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) {
168 $template->param( CAN_user_circulate => 1 );
169 $template->param( CAN_user_catalogue => 1 );
170 $template->param( CAN_user_parameters => 1 );
171 $template->param( CAN_user_borrowers => 1 );
172 $template->param( CAN_user_permissions => 1 );
173 $template->param( CAN_user_reserveforothers => 1 );
174 $template->param( CAN_user_borrow => 1 );
175 $template->param( CAN_user_editcatalogue => 1 );
176 $template->param( CAN_user_updatecharges => 1 );
177 $template->param( CAN_user_acquisition => 1 );
178 $template->param( CAN_user_management => 1 );
179 $template->param( CAN_user_tools => 1 );
180 $template->param( CAN_user_editauthorities => 1 );
181 $template->param( CAN_user_serials => 1 );
182 $template->param( CAN_user_reports => 1 );
183 $template->param( CAN_user_staffaccess => 1 );
184 foreach my $module (keys %$all_perms) {
185 foreach my $subperm (keys %{ $all_perms->{$module} }) {
186 $template->param( "CAN_user_${module}_${subperm}" => 1 );
191 if (C4::Context->preference('GranularPermissions')) {
193 foreach my $module (keys %$all_perms) {
194 if ( $flags->{$module} == 1) {
195 foreach my $subperm (keys %{ $all_perms->{$module} }) {
196 $template->param( "CAN_user_${module}_${subperm}" => 1 );
198 } elsif ( ref($flags->{$module}) ) {
199 foreach my $subperm (keys %{ $flags->{$module} } ) {
200 $template->param( "CAN_user_${module}_${subperm}" => 1 );
206 foreach my $module (keys %$all_perms) {
207 foreach my $subperm (keys %{ $all_perms->{$module} }) {
208 $template->param( "CAN_user_${module}_${subperm}" => 1 );
214 foreach my $module (keys %$flags) {
215 if ( $flags->{$module} == 1 or ref($flags->{$module}) ) {
216 $template->param( "CAN_user_$module" => 1 );
217 if ($module eq "parameters") {
218 $template->param( CAN_user_management => 1 );
224 else { # if this is an anonymous session, setup to display public lists...
226 # load the template variables for stylesheets and JavaScript
227 $template->param( css_libs => $in->{'css_libs'} );
228 $template->param( css_module => $in->{'css_module'} );
229 $template->param( css_page => $in->{'css_page'} );
230 $template->param( css_widgets => $in->{'css_widgets'} );
232 $template->param( js_libs => $in->{'js_libs'} );
233 $template->param( js_module => $in->{'js_module'} );
234 $template->param( js_page => $in->{'js_page'} );
235 $template->param( js_widgets => $in->{'js_widgets'} );
237 $template->param( sessionID => $sessionID );
239 if ($shelves = C4::Context->get_shelves_userenv()) {
240 $template->param( barshelves => scalar (@$shelves));
241 $template->param( barshelvesloop => $shelves);
245 if ( $in->{'type'} eq "intranet" ) {
247 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
248 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
249 IntranetNav => C4::Context->preference("IntranetNav"),
250 intranetuserjs => C4::Context->preference("intranetuserjs"),
251 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
252 AmazonContent => C4::Context->preference("AmazonContent"),
253 LibraryName => C4::Context->preference("LibraryName"),
254 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
255 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
256 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
257 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
258 AutoLocation => C4::Context->preference("AutoLocation"),
259 hide_marc => C4::Context->preference("hide_marc"),
260 patronimages => C4::Context->preference("patronimages"),
261 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
262 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
263 suggestion => C4::Context->preference("suggestion"),
264 virtualshelves => C4::Context->preference("virtualshelves"),
265 LibraryName => C4::Context->preference("LibraryName"),
266 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
267 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
268 IndependantBranches => C4::Context->preference("IndependantBranches"),
269 CircAutocompl => C4::Context->preference("CircAutocompl"),
270 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
271 AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
272 'item-level_itypes' => C4::Context->preference('item-level_itypes'),
273 canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
274 intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
275 noItemTypeImages => C4::Context->preference("noItemTypeImages"),
276 singleBranchMode => C4::Context->preference("singleBranchMode"),
277 TagsEnabled => C4::Context->preference("TagsEnabled"),
278 GoogleJackets => C4::Context->preference("GoogleJackets"),
279 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
283 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
284 my $LibraryNameTitle = C4::Context->preference("LibraryName");
285 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
286 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
288 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
289 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
290 suggestion => "" . C4::Context->preference("suggestion"),
291 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
292 virtualshelves => "" . C4::Context->preference("virtualshelves"),
293 OpacNav => "" . C4::Context->preference("OpacNav"),
294 opacheader => "" . C4::Context->preference("opacheader"),
295 opaccredits => "" . C4::Context->preference("opaccredits"),
296 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
297 opaclargeimage => "" . C4::Context->preference("opaclargeimage"),
298 opaclayoutstylesheet => "". C4::Context->preference("opaclayoutstylesheet"),
299 opaccolorstylesheet => "". C4::Context->preference("opaccolorstylesheet"),
300 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
301 opaclanguagesdisplay => "". C4::Context->preference("opaclanguagesdisplay"),
302 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
303 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
304 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
305 opacbookbag => "" . C4::Context->preference("opacbookbag"),
306 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
307 AmazonContent => "" . C4::Context->preference("AmazonContent"),
308 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
309 OPACAmazonSimilarItems => "" . C4::Context->preference("OPACAmazonSimilarItems"),
310 LibraryName => "" . C4::Context->preference("LibraryName"),
311 LibraryNameTitle => "" . $LibraryNameTitle,
312 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
313 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
314 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
315 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
316 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
317 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
318 opacuserjs => C4::Context->preference("opacuserjs"),
319 OpacCloud => C4::Context->preference("OpacCloud"),
320 OpacTopissue => C4::Context->preference("OpacTopissue"),
321 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
322 OpacBrowser => C4::Context->preference("OpacBrowser"),
323 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
324 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
325 reviewson => C4::Context->preference("reviewson"),
326 hide_marc => C4::Context->preference("hide_marc"),
327 patronimages => C4::Context->preference("patronimages"),
328 hidelostitems => C4::Context->preference("hidelostitems"),
329 mylibraryfirst => C4::Context->preference("SearchMyLibraryFirst"),
330 "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
331 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
332 'item-level_itypes' => C4::Context->preference('item-level_itypes'),
333 'Version' => C4::Context->preference('Version'),
334 singleBranchMode => C4::Context->preference("singleBranchMode"),
335 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
336 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
337 TagsEnabled => C4::Context->preference("TagsEnabled"),
338 GoogleJackets => C4::Context->preference("GoogleJackets"),
339 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
340 OPACBaseURL=> ($in->{'query'}->https() ? "https://" : "http://") .
341 $ENV{'SERVER_NAME'} .
342 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
345 $template->param(listloop=>[{shelfname=>"Freelist", shelfnumber=>110}]);
346 return ( $template, $borrowernumber, $cookie, $flags);
351 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
353 Verifies that the user is authorized to run this script. If
354 the user is authorized, a (userid, cookie, session-id, flags)
355 quadruple is returned. If the user is not authorized but does
356 not have the required privilege (see $flagsrequired below), it
357 displays an error page and exits. Otherwise, it displays the
358 login page and exits.
360 Note that C<&checkauth> will return if and only if the user
361 is authorized, so it should be called early on, before any
362 unfinished operations (e.g., if you've opened a file, then
363 C<&checkauth> won't close it for you).
365 C<$query> is the CGI object for the script calling C<&checkauth>.
367 The C<$noauth> argument is optional. If it is set, then no
368 authorization is required for the script.
370 C<&checkauth> fetches user and session information from C<$query> and
371 ensures that the user is authorized to run scripts that require
374 The C<$flagsrequired> argument specifies the required privileges
375 the user must have if the username and password are correct.
376 It should be specified as a reference-to-hash; keys in the hash
377 should be the "flags" for the user, as specified in the Members
378 intranet module. Any key specified must correspond to a "flag"
379 in the userflags table. E.g., { circulate => 1 } would specify
380 that the user must have the "circulate" privilege in order to
381 proceed. To make sure that access control is correct, the
382 C<$flagsrequired> parameter must be specified correctly.
384 If the GranularPermissions system preference is ON, the
385 value of each key in the C<flagsrequired> hash takes on an additional
390 The user must have access to all subfunctions of the module
391 specified by the hash key.
395 The user must have access to at least one subfunction of the module
396 specified by the hash key.
398 =item specific permission, e.g., 'export_catalog'
400 The user must have access to the specific subfunction list, which
401 must correspond to a row in the permissions table.
403 The C<$type> argument specifies whether the template should be
404 retrieved from the opac or intranet directory tree. "opac" is
405 assumed if it is not specified; however, if C<$type> is specified,
406 "intranet" is assumed if it is not "opac".
408 If C<$query> does not have a valid session ID associated with it
409 (i.e., the user has not logged in) or if the session has expired,
410 C<&checkauth> presents the user with a login page (from the point of
411 view of the original script, C<&checkauth> does not return). Once the
412 user has authenticated, C<&checkauth> restarts the original script
413 (this time, C<&checkauth> returns).
415 The login page is provided using a HTML::Template, which is set in the
416 systempreferences table or at the top of this file. The variable C<$type>
417 selects which template to use, either the opac or the intranet
418 authentification template.
420 C<&checkauth> returns a user ID, a cookie, and a session ID. The
421 cookie should be sent back to the browser; it verifies that the user
426 sub _version_check ($$) {
430 # If Version syspref is unavailable, it means Koha is beeing installed,
431 # and so we must redirect to OPAC maintenance page or to the WebInstaller
432 #warn "about to check version";
433 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
434 if ($type ne 'opac') {
435 warn "Install required, redirecting to Installer";
436 print $query->redirect("/cgi-bin/koha/installer/install.pl");
439 warn "OPAC Install required, redirecting to maintenance";
440 print $query->redirect("/cgi-bin/koha/maintenance.pl");
445 # check that database and koha version are the same
446 # there is no DB version, it's a fresh install,
447 # go to web installer
448 # there is a DB version, compare it to the code version
449 my $kohaversion=C4::Context::KOHAVERSION;
450 # remove the 3 last . to have a Perl number
451 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
452 $debug and print STDERR "kohaversion : $kohaversion\n";
453 if ($version < $kohaversion){
454 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
455 if ($type ne 'opac'){
456 warn sprintf($warning, 'Installer');
457 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
459 warn sprintf("OPAC: " . $warning, 'maintenance');
460 print $query->redirect("/cgi-bin/koha/maintenance.pl");
468 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
469 printf L join("\n",@_);
475 $debug and warn "Checking Auth";
476 # $authnotrequired will be set for scripts which will run without authentication
477 my $authnotrequired = shift;
478 my $flagsrequired = shift;
480 $type = 'opac' unless $type;
482 my $dbh = C4::Context->dbh;
483 my $timeout = C4::Context->preference('timeout');
485 if ($timeout =~ /(\d+)[dD]/) {
486 $timeout = $1 * 86400;
488 $timeout = 600 unless $timeout;
490 _version_check($type,$query);
494 my ( $userid, $cookie, $sessionID, $flags, $shelves );
495 my $logout = $query->param('logout.x');
497 if ( $userid = $ENV{'REMOTE_USER'} ) {
498 # Using Basic Authentication, no cookies required
499 $cookie = $query->cookie(
500 -name => 'CGISESSID',
506 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
507 my $session = get_session($sessionID);
508 C4::Context->_new_userenv($sessionID);
509 my ($ip, $lasttime, $sessiontype);
511 C4::Context::set_userenv(
512 $session->param('number'), $session->param('id'),
513 $session->param('cardnumber'), $session->param('firstname'),
514 $session->param('surname'), $session->param('branch'),
515 $session->param('branchname'), $session->param('flags'),
516 $session->param('emailaddress'), $session->param('branchprinter')
518 C4::Context::set_shelves_userenv($session->param('shelves'));
519 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
520 $ip = $session->param('ip');
521 $lasttime = $session->param('lasttime');
522 $userid = $session->param('id');
523 $sessiontype = $session->param('sessiontype');
526 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
527 #if a user enters an id ne to the id in the current session, we need to log them in...
528 #first we need to clear the anonymous session...
529 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
532 C4::Context->_unset_userenv($sessionID);
537 # voluntary logout the user
540 C4::Context->_unset_userenv($sessionID);
541 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,localtime);
545 elsif ( $lasttime < time() - $timeout ) {
547 $info{'timed_out'} = 1;
549 C4::Context->_unset_userenv($sessionID);
550 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,localtime);
554 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
555 # Different ip than originally logged in from
556 $info{'oldip'} = $ip;
557 $info{'newip'} = $ENV{'REMOTE_ADDR'};
558 $info{'different_ip'} = 1;
560 C4::Context->_unset_userenv($sessionID);
561 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,localtime, $info{'newip'});
566 $cookie = $query->cookie( CGISESSID => $session->id );
567 $session->param('lasttime',time());
568 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...
569 $flags = haspermission( $dbh, $userid, $flagsrequired );
573 $info{'nopermission'} = 1;
578 unless ($userid || $sessionID) {
579 #we initiate a session prior to checking for a username to allow for anonymous sessions...
580 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
581 my $sessionID = $session->id;
582 C4::Context->_new_userenv($sessionID);
583 $cookie = $query->cookie(CGISESSID => $sessionID);
584 if ( $userid = $query->param('userid') ) {
585 my $password = $query->param('password');
586 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
588 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},localtime);
589 if ( $flags = haspermission( $dbh, $userid, $flagsrequired ) ) {
593 $info{'nopermission'} = 1;
594 C4::Context->_unset_userenv($sessionID);
597 my ($borrowernumber, $firstname, $surname, $userflags,
598 $branchcode, $branchname, $branchprinter, $emailaddress);
600 if ( $return == 1 ) {
602 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
603 branches.branchname as branchname,
604 branches.branchprinter as branchprinter,
607 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
609 my $sth = $dbh->prepare("$select where userid=?");
610 $sth->execute($userid);
611 unless ($sth->rows) {
612 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
613 $sth = $dbh->prepare("$select where cardnumber=?");
614 $sth->execute($cardnumber);
615 unless ($sth->rows) {
616 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
617 $sth->execute($userid);
618 unless ($sth->rows) {
619 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
624 ($borrowernumber, $firstname, $surname, $userflags,
625 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
626 $debug and print STDERR "AUTH_3 results: " .
627 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
629 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
632 # launch a sequence to check if we have a ip for the branch, i
633 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
635 my $ip = $ENV{'REMOTE_ADDR'};
636 # if they specify at login, use that
637 if ($query->param('branch')) {
638 $branchcode = $query->param('branch');
639 $branchname = GetBranchName($branchcode);
641 my $branches = GetBranches();
642 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
643 # we have to check they are coming from the right ip range
644 my $domain = $branches->{$branchcode}->{'branchip'};
645 if ($ip !~ /^$domain/){
647 $info{'wrongip'} = 1;
652 foreach my $br ( keys %$branches ) {
653 # now we work with the treatment of ip
654 my $domain = $branches->{$br}->{'branchip'};
655 if ( $domain && $ip =~ /^$domain/ ) {
656 $branchcode = $branches->{$br}->{'branchcode'};
658 # new op dev : add the branchprinter and branchname in the cookie
659 $branchprinter = $branches->{$br}->{'branchprinter'};
660 $branchname = $branches->{$br}->{'branchname'};
663 $session->param('number',$borrowernumber);
664 $session->param('id',$userid);
665 $session->param('cardnumber',$cardnumber);
666 $session->param('firstname',$firstname);
667 $session->param('surname',$surname);
668 $session->param('branch',$branchcode);
669 $session->param('branchname',$branchname);
670 $session->param('flags',$userflags);
671 $session->param('emailaddress',$emailaddress);
672 $session->param('ip',$session->remote_addr());
673 $session->param('lasttime',time());
674 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
676 elsif ( $return == 2 ) {
677 #We suppose the user is the superlibrarian
679 $session->param('number',0);
680 $session->param('id',C4::Context->config('user'));
681 $session->param('cardnumber',C4::Context->config('user'));
682 $session->param('firstname',C4::Context->config('user'));
683 $session->param('surname',C4::Context->config('user'));
684 $session->param('branch','NO_LIBRARY_SET');
685 $session->param('branchname','NO_LIBRARY_SET');
686 $session->param('flags',1);
687 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
688 $session->param('ip',$session->remote_addr());
689 $session->param('lasttime',time());
691 C4::Context::set_userenv(
692 $session->param('number'), $session->param('id'),
693 $session->param('cardnumber'), $session->param('firstname'),
694 $session->param('surname'), $session->param('branch'),
695 $session->param('branchname'), $session->param('flags'),
696 $session->param('emailaddress'), $session->param('branchprinter')
698 $shelves = GetShelvesSummary($borrowernumber,2,10);
699 $session->param('shelves', $shelves);
700 C4::Context::set_shelves_userenv($shelves);
704 $info{'invalid_username_or_password'} = 1;
705 C4::Context->_unset_userenv($sessionID);
708 } # END if ( $userid = $query->param('userid') )
709 elsif ($type eq "opac") {
710 # if we are here this is an anonymous session; add public lists to it and a few other items...
711 # anonymous sessions are created only for the OPAC
712 $debug and warn "Initiating an anonymous session...";
713 $shelves = GetShelvesSummary(0,2,10);
714 $session->param('shelves', $shelves);
715 C4::Context::set_shelves_userenv($shelves);
716 # setting a couple of other session vars...
717 $session->param('ip',$session->remote_addr());
718 $session->param('lasttime',time());
719 $session->param('sessiontype','anon');
721 } # END unless ($userid)
722 my $insecure = C4::Context->boolean_preference('insecure');
724 # finished authentification, now respond
725 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
729 $cookie = $query->cookie( CGISESSID => '' );
731 return ( $userid, $cookie, $sessionID, $flags );
736 # AUTH rejected, show the login/password template, after checking the DB.
740 # get the inputs from the incoming query
742 foreach my $name ( param $query) {
743 (next) if ( $name eq 'userid' || $name eq 'password' );
744 my $value = $query->param($name);
745 push @inputs, { name => $name, value => $value };
747 # get the branchloop, which we need for authentication
748 my $branches = GetBranches();
750 for my $branch_hash (sort keys %$branches) {
751 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
754 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
755 my $template = gettemplate( $template_name, $type, $query );
756 $template->param(branchloop => \@branch_loop,);
760 suggestion => C4::Context->preference("suggestion"),
761 virtualshelves => C4::Context->preference("virtualshelves"),
762 opaclargeimage => C4::Context->preference("opaclargeimage"),
763 LibraryName => C4::Context->preference("LibraryName"),
764 opacuserlogin => C4::Context->preference("opacuserlogin"),
765 OpacNav => C4::Context->preference("OpacNav"),
766 opaccredits => C4::Context->preference("opaccredits"),
767 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
768 opacsmallimage => C4::Context->preference("opacsmallimage"),
769 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
770 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
771 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
772 opacuserjs => C4::Context->preference("opacuserjs"),
773 opacbookbag => "" . C4::Context->preference("opacbookbag"),
774 OpacCloud => C4::Context->preference("OpacCloud"),
775 OpacTopissue => C4::Context->preference("OpacTopissue"),
776 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
777 OpacBrowser => C4::Context->preference("OpacBrowser"),
778 opacheader => C4::Context->preference("opacheader"),
779 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
780 intranetcolorstylesheet =>
781 C4::Context->preference("intranetcolorstylesheet"),
782 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
783 IntranetNav => C4::Context->preference("IntranetNav"),
784 intranetuserjs => C4::Context->preference("intranetuserjs"),
785 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
786 IndependantBranches=> C4::Context->preference("IndependantBranches"),
787 AutoLocation => C4::Context->preference("AutoLocation"),
788 wrongip => $info{'wrongip'}
791 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
793 my $self_url = $query->url( -absolute => 1 );
796 LibraryName => C4::Context->preference("LibraryName"),
798 $template->param( \%info );
799 # $cookie = $query->cookie(CGISESSID => $session->id
801 print $query->header(
802 -type => 'text/html',
812 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
814 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
815 cookie, determine if the user has the privileges specified by C<$userflags>.
817 C<check_api_auth> is is meant for authenticating users of web services, and
818 consequently will always return and will not attempt to redirect the user
821 If a valid session cookie is already present, check_api_auth will return a status
822 of "ok", the cookie, and the Koha session ID.
824 If no session cookie is present, check_api_auth will check the 'userid' and 'password
825 parameters and create a session cookie and Koha session if the supplied credentials
828 Possible return values in C<$status> are:
832 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
834 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
836 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
838 =item "expired -- session cookie has expired; API user should resubmit userid and password
846 my $flagsrequired = shift;
848 my $dbh = C4::Context->dbh;
849 my $timeout = C4::Context->preference('timeout');
850 $timeout = 600 unless $timeout;
852 unless (C4::Context->preference('Version')) {
853 # database has not been installed yet
854 return ("maintenance", undef, undef);
856 my $kohaversion=C4::Context::KOHAVERSION;
857 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
858 if (C4::Context->preference('Version') < $kohaversion) {
859 # database in need of version update; assume that
860 # no API should be called while databsae is in
862 return ("maintenance", undef, undef);
865 # FIXME -- most of what follows is a copy-and-paste
866 # of code from checkauth. There is an obvious need
867 # for refactoring to separate the various parts of
868 # the authentication code, but as of 2007-11-19 this
869 # is deferred so as to not introduce bugs into the
870 # regular authentication code for Koha 3.0.
872 # see if we have a valid session cookie already
873 # however, if a userid parameter is present (i.e., from
874 # a form submission, assume that any current cookie
876 my $sessionID = undef;
877 unless ($query->param('userid')) {
878 $sessionID = $query->cookie("CGISESSID");
881 my $session = get_session($sessionID);
882 C4::Context->_new_userenv($sessionID);
884 C4::Context::set_userenv(
885 $session->param('number'), $session->param('id'),
886 $session->param('cardnumber'), $session->param('firstname'),
887 $session->param('surname'), $session->param('branch'),
888 $session->param('branchname'), $session->param('flags'),
889 $session->param('emailaddress'), $session->param('branchprinter')
892 my $ip = $session->param('ip');
893 my $lasttime = $session->param('lasttime');
894 my $userid = $session->param('id');
895 if ( $lasttime < time() - $timeout ) {
898 C4::Context->_unset_userenv($sessionID);
901 return ("expired", undef, undef);
902 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
905 C4::Context->_unset_userenv($sessionID);
908 return ("expired", undef, undef);
910 my $cookie = $query->cookie( CGISESSID => $session->id );
911 $session->param('lasttime',time());
912 my $flags = haspermission( $dbh, $userid, $flagsrequired );
914 return ("ok", $cookie, $sessionID);
917 C4::Context->_unset_userenv($sessionID);
920 return ("failed", undef, undef);
924 return ("expired", undef, undef);
928 my $userid = $query->param('userid');
929 my $password = $query->param('password');
930 unless ($userid and $password) {
931 # caller did something wrong, fail the authenticateion
932 return ("failed", undef, undef);
934 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
935 if ($return and haspermission( $dbh, $userid, $flagsrequired)) {
936 my $session = get_session("");
937 return ("failed", undef, undef) unless $session;
939 my $sessionID = $session->id;
940 C4::Context->_new_userenv($sessionID);
941 my $cookie = $query->cookie(CGISESSID => $sessionID);
942 if ( $return == 1 ) {
944 $borrowernumber, $firstname, $surname,
945 $userflags, $branchcode, $branchname,
946 $branchprinter, $emailaddress
950 "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=?"
952 $sth->execute($userid);
954 $borrowernumber, $firstname, $surname,
955 $userflags, $branchcode, $branchname,
956 $branchprinter, $emailaddress
957 ) = $sth->fetchrow if ( $sth->rows );
959 unless ($sth->rows ) {
960 my $sth = $dbh->prepare(
961 "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=?"
963 $sth->execute($cardnumber);
965 $borrowernumber, $firstname, $surname,
966 $userflags, $branchcode, $branchname,
967 $branchprinter, $emailaddress
968 ) = $sth->fetchrow if ( $sth->rows );
970 unless ( $sth->rows ) {
971 $sth->execute($userid);
973 $borrowernumber, $firstname, $surname, $userflags,
974 $branchcode, $branchname, $branchprinter, $emailaddress
975 ) = $sth->fetchrow if ( $sth->rows );
979 my $ip = $ENV{'REMOTE_ADDR'};
980 # if they specify at login, use that
981 if ($query->param('branch')) {
982 $branchcode = $query->param('branch');
983 $branchname = GetBranchName($branchcode);
985 my $branches = GetBranches();
987 foreach my $br ( keys %$branches ) {
988 # now we work with the treatment of ip
989 my $domain = $branches->{$br}->{'branchip'};
990 if ( $domain && $ip =~ /^$domain/ ) {
991 $branchcode = $branches->{$br}->{'branchcode'};
993 # new op dev : add the branchprinter and branchname in the cookie
994 $branchprinter = $branches->{$br}->{'branchprinter'};
995 $branchname = $branches->{$br}->{'branchname'};
998 $session->param('number',$borrowernumber);
999 $session->param('id',$userid);
1000 $session->param('cardnumber',$cardnumber);
1001 $session->param('firstname',$firstname);
1002 $session->param('surname',$surname);
1003 $session->param('branch',$branchcode);
1004 $session->param('branchname',$branchname);
1005 $session->param('flags',$userflags);
1006 $session->param('emailaddress',$emailaddress);
1007 $session->param('ip',$session->remote_addr());
1008 $session->param('lasttime',time());
1009 } elsif ( $return == 2 ) {
1010 #We suppose the user is the superlibrarian
1011 $session->param('number',0);
1012 $session->param('id',C4::Context->config('user'));
1013 $session->param('cardnumber',C4::Context->config('user'));
1014 $session->param('firstname',C4::Context->config('user'));
1015 $session->param('surname',C4::Context->config('user'));
1016 $session->param('branch','NO_LIBRARY_SET');
1017 $session->param('branchname','NO_LIBRARY_SET');
1018 $session->param('flags',1);
1019 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1020 $session->param('ip',$session->remote_addr());
1021 $session->param('lasttime',time());
1023 C4::Context::set_userenv(
1024 $session->param('number'), $session->param('id'),
1025 $session->param('cardnumber'), $session->param('firstname'),
1026 $session->param('surname'), $session->param('branch'),
1027 $session->param('branchname'), $session->param('flags'),
1028 $session->param('emailaddress'), $session->param('branchprinter')
1030 return ("ok", $cookie, $sessionID);
1032 return ("failed", undef, undef);
1037 =item check_cookie_auth
1039 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1041 Given a CGISESSID cookie set during a previous login to Koha, determine
1042 if the user has the privileges specified by C<$userflags>.
1044 C<check_cookie_auth> is meant for authenticating special services
1045 such as tools/upload-file.pl that are invoked by other pages that
1046 have been authenticated in the usual way.
1048 Possible return values in C<$status> are:
1052 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1054 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1056 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1058 =item "expired -- session cookie has expired; API user should resubmit userid and password
1064 sub check_cookie_auth {
1066 my $flagsrequired = shift;
1068 my $dbh = C4::Context->dbh;
1069 my $timeout = C4::Context->preference('timeout');
1070 $timeout = 600 unless $timeout;
1072 unless (C4::Context->preference('Version')) {
1073 # database has not been installed yet
1074 return ("maintenance", undef);
1076 my $kohaversion=C4::Context::KOHAVERSION;
1077 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1078 if (C4::Context->preference('Version') < $kohaversion) {
1079 # database in need of version update; assume that
1080 # no API should be called while databsae is in
1082 return ("maintenance", undef);
1085 # FIXME -- most of what follows is a copy-and-paste
1086 # of code from checkauth. There is an obvious need
1087 # for refactoring to separate the various parts of
1088 # the authentication code, but as of 2007-11-23 this
1089 # is deferred so as to not introduce bugs into the
1090 # regular authentication code for Koha 3.0.
1092 # see if we have a valid session cookie already
1093 # however, if a userid parameter is present (i.e., from
1094 # a form submission, assume that any current cookie
1096 unless (defined $cookie and $cookie) {
1097 return ("failed", undef);
1099 my $sessionID = $cookie;
1100 my $session = get_session($sessionID);
1101 C4::Context->_new_userenv($sessionID);
1103 C4::Context::set_userenv(
1104 $session->param('number'), $session->param('id'),
1105 $session->param('cardnumber'), $session->param('firstname'),
1106 $session->param('surname'), $session->param('branch'),
1107 $session->param('branchname'), $session->param('flags'),
1108 $session->param('emailaddress'), $session->param('branchprinter')
1111 my $ip = $session->param('ip');
1112 my $lasttime = $session->param('lasttime');
1113 my $userid = $session->param('id');
1114 if ( $lasttime < time() - $timeout ) {
1117 C4::Context->_unset_userenv($sessionID);
1120 return ("expired", undef);
1121 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1122 # IP address changed
1124 C4::Context->_unset_userenv($sessionID);
1127 return ("expired", undef);
1129 $session->param('lasttime',time());
1130 my $flags = haspermission( $dbh, $userid, $flagsrequired );
1132 return ("ok", $sessionID);
1135 C4::Context->_unset_userenv($sessionID);
1138 return ("failed", undef);
1142 return ("expired", undef);
1149 my $session = get_session($sessionID);
1151 Given a session ID, retrieve the CGI::Session object used to store
1152 the session's state. The session object can be used to store
1153 data that needs to be accessed by different scripts during a
1156 If the C<$sessionID> parameter is an empty string, a new session
1162 my $sessionID = shift;
1163 my $storage_method = C4::Context->preference('SessionStorage');
1164 my $dbh = C4::Context->dbh;
1166 if ($storage_method eq 'mysql'){
1167 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1169 elsif ($storage_method eq 'Pg') {
1170 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1173 # catch all defaults to tmp should work on all systems
1174 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1181 my ( $dbh, $userid, $password ) = @_;
1183 $debug and print "## checkpw - checking LDAP\n";
1184 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1185 ($retval) and return ($retval,$retcard);
1191 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1193 $sth->execute($userid);
1195 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1196 $surname, $branchcode, $flags )
1198 if ( md5_base64($password) eq $md5password ) {
1200 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1201 $firstname, $surname, $branchcode, $flags );
1202 return 1, $cardnumber;
1207 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1209 $sth->execute($userid);
1211 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1212 $surname, $branchcode, $flags )
1214 if ( md5_base64($password) eq $md5password ) {
1216 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1217 $firstname, $surname, $branchcode, $flags );
1221 if ( $userid && $userid eq C4::Context->config('user')
1222 && "$password" eq C4::Context->config('pass') )
1225 # Koha superuser account
1226 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1229 if ( $userid && $userid eq 'demo'
1230 && "$password" eq 'demo'
1231 && C4::Context->config('demo') )
1234 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1235 # some features won't be effective : modify systempref, modify MARC structure,
1243 $authflags = getuserflags($flags,$dbh);
1244 Translates integer flags into permissions strings hash.
1246 C<$flags> is the integer userflags value ( borrowers.userflags )
1247 C<$authflags> is a hashref of permissions
1256 $flags = 0 unless $flags;
1257 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1260 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1261 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1262 $userflags->{$flag} = 1;
1265 $userflags->{$flag} = 0;
1269 # get subpermissions and merge with top-level permissions
1270 my $user_subperms = get_user_subpermissions($userid);
1271 foreach my $module (keys %$user_subperms) {
1272 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1273 $userflags->{$module} = $user_subperms->{$module};
1279 =item get_user_subpermissions
1283 my $user_perm_hashref = get_user_subpermissions($userid);
1287 Given the userid (note, not the borrowernumber) of a staff user,
1288 return a hashref of hashrefs of the specific subpermissions
1289 accorded to the user. An example return is
1293 export_catalog => 1,
1294 import_patrons => 1,
1298 The top-level hash-key is a module or function code from
1299 userflags.flag, while the second-level key is a code
1302 The results of this function do not give a complete picture
1303 of the functions that a staff user can access; it is also
1304 necessary to check borrowers.flags.
1308 sub get_user_subpermissions {
1311 my $dbh = C4::Context->dbh;
1312 my $sth = $dbh->prepare("SELECT flag, code
1313 FROM user_permissions
1314 JOIN permissions USING (module_bit, code)
1315 JOIN userflags ON (module_bit = bit)
1316 JOIN borrowers USING (borrowernumber)
1318 $sth->execute($userid);
1320 my $user_perms = {};
1321 while (my $perm = $sth->fetchrow_hashref) {
1322 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1327 =item get_all_subpermissions
1331 my $perm_hashref = get_all_subpermissions();
1335 Returns a hashref of hashrefs defining all specific
1336 permissions currently defined. The return value
1337 has the same structure as that of C<get_user_subpermissions>,
1338 except that the innermost hash value is the description
1339 of the subpermission.
1343 sub get_all_subpermissions {
1344 my $dbh = C4::Context->dbh;
1345 my $sth = $dbh->prepare("SELECT flag, code, description
1347 JOIN userflags ON (module_bit = bit)");
1351 while (my $perm = $sth->fetchrow_hashref) {
1352 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1359 $flags = ($dbh,$member,$flagsrequired);
1361 C<$member> may be either userid or overloaded with $borrower hashref from GetMemberDetails.
1362 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1364 Returns member's flags or 0 if a permission is not met.
1369 my ( $dbh, $userid, $flagsrequired ) = @_;
1370 my ($flags,$intflags);
1371 $dbh=C4::Context->dbh unless($dbh);
1373 $intflags = $userid->{'flags'};
1375 my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1376 $sth->execute($userid);
1377 my ($intflags) = $sth->fetchrow;
1378 $flags = getuserflags( $intflags, $userid, $dbh );
1380 if ( $userid eq C4::Context->config('user') ) {
1381 # Super User Account from /etc/koha.conf
1382 $flags->{'superlibrarian'} = 1;
1384 if ( $userid eq 'demo' && C4::Context->config('demo') ) {
1385 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1386 $flags->{'superlibrarian'} = 1;
1388 return $flags if $flags->{superlibrarian};
1389 foreach my $module ( keys %$flagsrequired ) {
1390 if (C4::Context->preference('GranularPermissions')) {
1391 my $subperm = $flagsrequired->{$module};
1392 if ($subperm eq '*') {
1393 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1395 return 0 unless ( $flags->{$module} == 1 or
1396 ( ref($flags->{$module}) and
1397 exists $flags->{$module}->{$subperm} and
1398 $flags->{$module}->{$subperm} == 1
1403 return 0 unless ( $flags->{$module} );
1407 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1411 sub getborrowernumber {
1413 my $dbh = C4::Context->dbh;
1414 for my $field ( 'userid', 'cardnumber' ) {
1416 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1417 $sth->execute($userid);
1419 my ($bnumber) = $sth->fetchrow;
1426 END { } # module clean-up code here (global destructor)