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);
149 if (($pubshelves, $barshelves) = C4::Context->get_shelves_userenv()) {
150 $template->param( barshelves => scalar (@$barshelves));
151 $template->param( pubshelves => scalar (@$pubshelves));
152 $template->param( barshelvesloop => $barshelves);
153 $template->param( pubshelvesloop => $pubshelves);
156 $borrowernumber = getborrowernumber($user);
157 my ( $borr, $alternativeflags ) =
158 GetMemberDetails( $borrowernumber );
161 $template->param( "USER_INFO" => \@bordat );
163 my $all_perms = get_all_subpermissions();
165 my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow
166 editcatalogue updatecharges management tools editauthorities serials reports);
167 # We are going to use the $flags returned by checkauth
168 # to create the template's parameters that will indicate
169 # which menus the user can access.
170 if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) {
171 $template->param( CAN_user_circulate => 1 );
172 $template->param( CAN_user_catalogue => 1 );
173 $template->param( CAN_user_parameters => 1 );
174 $template->param( CAN_user_borrowers => 1 );
175 $template->param( CAN_user_permissions => 1 );
176 $template->param( CAN_user_reserveforothers => 1 );
177 $template->param( CAN_user_borrow => 1 );
178 $template->param( CAN_user_editcatalogue => 1 );
179 $template->param( CAN_user_updatecharges => 1 );
180 $template->param( CAN_user_acquisition => 1 );
181 $template->param( CAN_user_management => 1 );
182 $template->param( CAN_user_tools => 1 );
183 $template->param( CAN_user_editauthorities => 1 );
184 $template->param( CAN_user_serials => 1 );
185 $template->param( CAN_user_reports => 1 );
186 $template->param( CAN_user_staffaccess => 1 );
187 foreach my $module (keys %$all_perms) {
188 foreach my $subperm (keys %{ $all_perms->{$module} }) {
189 $template->param( "CAN_user_${module}_${subperm}" => 1 );
194 if (C4::Context->preference('GranularPermissions')) {
196 foreach my $module (keys %$all_perms) {
197 if ( $flags->{$module} == 1) {
198 foreach my $subperm (keys %{ $all_perms->{$module} }) {
199 $template->param( "CAN_user_${module}_${subperm}" => 1 );
201 } elsif ( ref($flags->{$module}) ) {
202 foreach my $subperm (keys %{ $flags->{$module} } ) {
203 $template->param( "CAN_user_${module}_${subperm}" => 1 );
209 foreach my $module (keys %$all_perms) {
210 foreach my $subperm (keys %{ $all_perms->{$module} }) {
211 $template->param( "CAN_user_${module}_${subperm}" => 1 );
217 foreach my $module (keys %$flags) {
218 if ( $flags->{$module} == 1 or ref($flags->{$module}) ) {
219 $template->param( "CAN_user_$module" => 1 );
220 if ($module eq "parameters") {
221 $template->param( CAN_user_management => 1 );
227 else { # if this is an anonymous session, setup to display public lists...
229 # load the template variables for stylesheets and JavaScript
230 $template->param( css_libs => $in->{'css_libs'} );
231 $template->param( css_module => $in->{'css_module'} );
232 $template->param( css_page => $in->{'css_page'} );
233 $template->param( css_widgets => $in->{'css_widgets'} );
235 $template->param( js_libs => $in->{'js_libs'} );
236 $template->param( js_module => $in->{'js_module'} );
237 $template->param( js_page => $in->{'js_page'} );
238 $template->param( js_widgets => $in->{'js_widgets'} );
240 $template->param( sessionID => $sessionID );
242 my ($pubshelves); # an anonymous user has no 'barshelves'...
243 if (($pubshelves) = C4::Context->get_shelves_userenv()) {
244 $template->param( pubshelves => scalar (@$pubshelves));
245 $template->param( pubshelvesloop => $pubshelves);
250 # these template parameters are set the same regardless of $in->{'type'}
252 "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
253 GoogleJackets => C4::Context->preference("GoogleJackets"),
254 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
255 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
256 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
257 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
258 TagsEnabled => C4::Context->preference("TagsEnabled"),
259 hide_marc => C4::Context->preference("hide_marc"),
260 'item-level_itypes' => C4::Context->preference('item-level_itypes'),
261 patronimages => C4::Context->preference("patronimages"),
262 singleBranchMode => C4::Context->preference("singleBranchMode"),
265 if ( $in->{'type'} eq "intranet" ) {
267 AmazonContent => C4::Context->preference("AmazonContent"),
268 AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
269 AutoLocation => C4::Context->preference("AutoLocation"),
270 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
271 CircAutocompl => C4::Context->preference("CircAutocompl"),
272 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
273 IndependantBranches => C4::Context->preference("IndependantBranches"),
274 IntranetNav => C4::Context->preference("IntranetNav"),
275 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
276 LibraryName => C4::Context->preference("LibraryName"),
277 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
278 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
279 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
280 canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
281 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
282 intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
283 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
284 intranetuserjs => C4::Context->preference("intranetuserjs"),
285 noItemTypeImages => C4::Context->preference("noItemTypeImages"),
286 suggestion => C4::Context->preference("suggestion"),
287 virtualshelves => C4::Context->preference("virtualshelves"),
291 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
292 my $LibraryNameTitle = C4::Context->preference("LibraryName");
293 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
294 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
296 AmazonContent => "" . C4::Context->preference("AmazonContent"),
297 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
298 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
299 LibraryName => "" . C4::Context->preference("LibraryName"),
300 LibraryNameTitle => "" . $LibraryNameTitle,
301 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
302 OPACAmazonSimilarItems => "" . C4::Context->preference("OPACAmazonSimilarItems"),
303 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
304 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
305 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
306 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
307 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
308 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
309 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
310 OPACBaseURL => ($in->{'query'}->https() ? "https://" : "http://") .
311 $ENV{'SERVER_NAME'} .
312 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
313 OpacBrowser => C4::Context->preference("OpacBrowser"),
314 OpacCloud => C4::Context->preference("OpacCloud"),
315 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
316 OpacNav => "" . C4::Context->preference("OpacNav"),
317 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
318 OpacTopissue => C4::Context->preference("OpacTopissue"),
319 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
320 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
321 'Version' => C4::Context->preference('Version'),
322 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
323 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
324 hidelostitems => C4::Context->preference("hidelostitems"),
325 mylibraryfirst => C4::Context->preference("SearchMyLibraryFirst"),
326 opacbookbag => "" . C4::Context->preference("opacbookbag"),
327 opaccolorstylesheet => "". C4::Context->preference("opaccolorstylesheet"),
328 opaccredits => "" . C4::Context->preference("opaccredits"),
329 opacheader => "" . C4::Context->preference("opacheader"),
330 opaclanguagesdisplay => "". C4::Context->preference("opaclanguagesdisplay"),
331 opaclargeimage => "" . C4::Context->preference("opaclargeimage"),
332 opaclayoutstylesheet => "". C4::Context->preference("opaclayoutstylesheet"),
333 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
334 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
335 opacuserjs => C4::Context->preference("opacuserjs"),
336 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
337 reviewson => C4::Context->preference("reviewson"),
338 suggestion => "" . C4::Context->preference("suggestion"),
339 virtualshelves => "" . C4::Context->preference("virtualshelves"),
342 $template->param(listloop=>[{shelfname=>"Freelist", shelfnumber=>110}]);
343 return ( $template, $borrowernumber, $cookie, $flags);
348 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
350 Verifies that the user is authorized to run this script. If
351 the user is authorized, a (userid, cookie, session-id, flags)
352 quadruple is returned. If the user is not authorized but does
353 not have the required privilege (see $flagsrequired below), it
354 displays an error page and exits. Otherwise, it displays the
355 login page and exits.
357 Note that C<&checkauth> will return if and only if the user
358 is authorized, so it should be called early on, before any
359 unfinished operations (e.g., if you've opened a file, then
360 C<&checkauth> won't close it for you).
362 C<$query> is the CGI object for the script calling C<&checkauth>.
364 The C<$noauth> argument is optional. If it is set, then no
365 authorization is required for the script.
367 C<&checkauth> fetches user and session information from C<$query> and
368 ensures that the user is authorized to run scripts that require
371 The C<$flagsrequired> argument specifies the required privileges
372 the user must have if the username and password are correct.
373 It should be specified as a reference-to-hash; keys in the hash
374 should be the "flags" for the user, as specified in the Members
375 intranet module. Any key specified must correspond to a "flag"
376 in the userflags table. E.g., { circulate => 1 } would specify
377 that the user must have the "circulate" privilege in order to
378 proceed. To make sure that access control is correct, the
379 C<$flagsrequired> parameter must be specified correctly.
381 If the GranularPermissions system preference is ON, the
382 value of each key in the C<flagsrequired> hash takes on an additional
387 The user must have access to all subfunctions of the module
388 specified by the hash key.
392 The user must have access to at least one subfunction of the module
393 specified by the hash key.
395 =item specific permission, e.g., 'export_catalog'
397 The user must have access to the specific subfunction list, which
398 must correspond to a row in the permissions table.
400 The C<$type> argument specifies whether the template should be
401 retrieved from the opac or intranet directory tree. "opac" is
402 assumed if it is not specified; however, if C<$type> is specified,
403 "intranet" is assumed if it is not "opac".
405 If C<$query> does not have a valid session ID associated with it
406 (i.e., the user has not logged in) or if the session has expired,
407 C<&checkauth> presents the user with a login page (from the point of
408 view of the original script, C<&checkauth> does not return). Once the
409 user has authenticated, C<&checkauth> restarts the original script
410 (this time, C<&checkauth> returns).
412 The login page is provided using a HTML::Template, which is set in the
413 systempreferences table or at the top of this file. The variable C<$type>
414 selects which template to use, either the opac or the intranet
415 authentification template.
417 C<&checkauth> returns a user ID, a cookie, and a session ID. The
418 cookie should be sent back to the browser; it verifies that the user
423 sub _version_check ($$) {
427 # If Version syspref is unavailable, it means Koha is beeing installed,
428 # and so we must redirect to OPAC maintenance page or to the WebInstaller
429 #warn "about to check version";
430 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
431 if ($type ne 'opac') {
432 warn "Install required, redirecting to Installer";
433 print $query->redirect("/cgi-bin/koha/installer/install.pl");
436 warn "OPAC Install required, redirecting to maintenance";
437 print $query->redirect("/cgi-bin/koha/maintenance.pl");
442 # check that database and koha version are the same
443 # there is no DB version, it's a fresh install,
444 # go to web installer
445 # there is a DB version, compare it to the code version
446 my $kohaversion=C4::Context::KOHAVERSION;
447 # remove the 3 last . to have a Perl number
448 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
449 $debug and print STDERR "kohaversion : $kohaversion\n";
450 if ($version < $kohaversion){
451 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
452 if ($type ne 'opac'){
453 warn sprintf($warning, 'Installer');
454 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
456 warn sprintf("OPAC: " . $warning, 'maintenance');
457 print $query->redirect("/cgi-bin/koha/maintenance.pl");
465 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
466 printf L join("\n",@_);
472 $debug and warn "Checking Auth";
473 # $authnotrequired will be set for scripts which will run without authentication
474 my $authnotrequired = shift;
475 my $flagsrequired = shift;
477 $type = 'opac' unless $type;
479 my $dbh = C4::Context->dbh;
480 my $timeout = C4::Context->preference('timeout');
482 if ($timeout =~ /(\d+)[dD]/) {
483 $timeout = $1 * 86400;
485 $timeout = 600 unless $timeout;
487 _version_check($type,$query);
491 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
492 my $logout = $query->param('logout.x');
494 if ( $userid = $ENV{'REMOTE_USER'} ) {
495 # Using Basic Authentication, no cookies required
496 $cookie = $query->cookie(
497 -name => 'CGISESSID',
503 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
504 my $session = get_session($sessionID);
505 C4::Context->_new_userenv($sessionID);
506 my ($ip, $lasttime, $sessiontype);
508 C4::Context::set_userenv(
509 $session->param('number'), $session->param('id'),
510 $session->param('cardnumber'), $session->param('firstname'),
511 $session->param('surname'), $session->param('branch'),
512 $session->param('branchname'), $session->param('flags'),
513 $session->param('emailaddress'), $session->param('branchprinter')
515 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
516 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
517 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
518 $ip = $session->param('ip');
519 $lasttime = $session->param('lasttime');
520 $userid = $session->param('id');
521 $sessiontype = $session->param('sessiontype');
524 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
525 #if a user enters an id ne to the id in the current session, we need to log them in...
526 #first we need to clear the anonymous session...
527 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
530 C4::Context->_unset_userenv($sessionID);
535 # voluntary logout the user
538 C4::Context->_unset_userenv($sessionID);
539 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,localtime);
543 elsif ( $lasttime < time() - $timeout ) {
545 $info{'timed_out'} = 1;
547 C4::Context->_unset_userenv($sessionID);
548 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,localtime);
552 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
553 # Different ip than originally logged in from
554 $info{'oldip'} = $ip;
555 $info{'newip'} = $ENV{'REMOTE_ADDR'};
556 $info{'different_ip'} = 1;
558 C4::Context->_unset_userenv($sessionID);
559 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,localtime, $info{'newip'});
564 $cookie = $query->cookie( CGISESSID => $session->id );
565 $session->param('lasttime',time());
566 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...
567 $flags = haspermission( $dbh, $userid, $flagsrequired );
571 $info{'nopermission'} = 1;
576 unless ($userid || $sessionID) {
577 #we initiate a session prior to checking for a username to allow for anonymous sessions...
578 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
579 my $sessionID = $session->id;
580 C4::Context->_new_userenv($sessionID);
581 $cookie = $query->cookie(CGISESSID => $sessionID);
582 if ( $userid = $query->param('userid') ) {
583 my $password = $query->param('password');
584 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
586 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},localtime);
587 if ( $flags = haspermission( $dbh, $userid, $flagsrequired ) ) {
591 $info{'nopermission'} = 1;
592 C4::Context->_unset_userenv($sessionID);
595 my ($borrowernumber, $firstname, $surname, $userflags,
596 $branchcode, $branchname, $branchprinter, $emailaddress);
598 if ( $return == 1 ) {
600 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
601 branches.branchname as branchname,
602 branches.branchprinter as branchprinter,
605 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
607 my $sth = $dbh->prepare("$select where userid=?");
608 $sth->execute($userid);
609 unless ($sth->rows) {
610 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
611 $sth = $dbh->prepare("$select where cardnumber=?");
612 $sth->execute($cardnumber);
613 unless ($sth->rows) {
614 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
615 $sth->execute($userid);
616 unless ($sth->rows) {
617 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
622 ($borrowernumber, $firstname, $surname, $userflags,
623 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
624 $debug and print STDERR "AUTH_3 results: " .
625 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
627 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
630 # launch a sequence to check if we have a ip for the branch, i
631 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
633 my $ip = $ENV{'REMOTE_ADDR'};
634 # if they specify at login, use that
635 if ($query->param('branch')) {
636 $branchcode = $query->param('branch');
637 $branchname = GetBranchName($branchcode);
639 my $branches = GetBranches();
640 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
641 # we have to check they are coming from the right ip range
642 my $domain = $branches->{$branchcode}->{'branchip'};
643 if ($ip !~ /^$domain/){
645 $info{'wrongip'} = 1;
650 foreach my $br ( keys %$branches ) {
651 # now we work with the treatment of ip
652 my $domain = $branches->{$br}->{'branchip'};
653 if ( $domain && $ip =~ /^$domain/ ) {
654 $branchcode = $branches->{$br}->{'branchcode'};
656 # new op dev : add the branchprinter and branchname in the cookie
657 $branchprinter = $branches->{$br}->{'branchprinter'};
658 $branchname = $branches->{$br}->{'branchname'};
661 $session->param('number',$borrowernumber);
662 $session->param('id',$userid);
663 $session->param('cardnumber',$cardnumber);
664 $session->param('firstname',$firstname);
665 $session->param('surname',$surname);
666 $session->param('branch',$branchcode);
667 $session->param('branchname',$branchname);
668 $session->param('flags',$userflags);
669 $session->param('emailaddress',$emailaddress);
670 $session->param('ip',$session->remote_addr());
671 $session->param('lasttime',time());
672 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
674 elsif ( $return == 2 ) {
675 #We suppose the user is the superlibrarian
677 $session->param('number',0);
678 $session->param('id',C4::Context->config('user'));
679 $session->param('cardnumber',C4::Context->config('user'));
680 $session->param('firstname',C4::Context->config('user'));
681 $session->param('surname',C4::Context->config('user'));
682 $session->param('branch','NO_LIBRARY_SET');
683 $session->param('branchname','NO_LIBRARY_SET');
684 $session->param('flags',1);
685 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
686 $session->param('ip',$session->remote_addr());
687 $session->param('lasttime',time());
689 C4::Context::set_userenv(
690 $session->param('number'), $session->param('id'),
691 $session->param('cardnumber'), $session->param('firstname'),
692 $session->param('surname'), $session->param('branch'),
693 $session->param('branchname'), $session->param('flags'),
694 $session->param('emailaddress'), $session->param('branchprinter')
697 # Grab borrower's shelves and add to the session...
698 $barshelves = GetShelvesSummary($borrowernumber,2,10);
699 $session->param('barshelves', $barshelves);
700 C4::Context::set_shelves_userenv('bar',$barshelves);
702 # Grab the public shelves and add to the session...
703 $pubshelves = GetShelvesSummary(0,2,10);
704 $session->param('pubshelves', $pubshelves);
705 C4::Context::set_shelves_userenv('pub',$pubshelves);
709 $info{'invalid_username_or_password'} = 1;
710 C4::Context->_unset_userenv($sessionID);
713 } # END if ( $userid = $query->param('userid') )
714 elsif ($type eq "opac") {
715 # if we are here this is an anonymous session; add public lists to it and a few other items...
716 # anonymous sessions are created only for the OPAC
717 $debug and warn "Initiating an anonymous session...";
719 # Grab the public shelves and add to the session...
720 $pubshelves = GetShelvesSummary(0,2,10);
721 $session->param('pubshelves', $pubshelves);
722 C4::Context::set_shelves_userenv('pub',$pubshelves);
724 # setting a couple of other session vars...
725 $session->param('ip',$session->remote_addr());
726 $session->param('lasttime',time());
727 $session->param('sessiontype','anon');
729 } # END unless ($userid)
730 my $insecure = C4::Context->boolean_preference('insecure');
732 # finished authentification, now respond
733 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
737 $cookie = $query->cookie( CGISESSID => '' );
739 return ( $userid, $cookie, $sessionID, $flags );
744 # AUTH rejected, show the login/password template, after checking the DB.
748 # get the inputs from the incoming query
750 foreach my $name ( param $query) {
751 (next) if ( $name eq 'userid' || $name eq 'password' );
752 my $value = $query->param($name);
753 push @inputs, { name => $name, value => $value };
755 # get the branchloop, which we need for authentication
756 my $branches = GetBranches();
758 for my $branch_hash (sort keys %$branches) {
759 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
762 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
763 my $template = gettemplate( $template_name, $type, $query );
764 $template->param(branchloop => \@branch_loop,);
768 suggestion => C4::Context->preference("suggestion"),
769 virtualshelves => C4::Context->preference("virtualshelves"),
770 opaclargeimage => C4::Context->preference("opaclargeimage"),
771 LibraryName => C4::Context->preference("LibraryName"),
772 opacuserlogin => C4::Context->preference("opacuserlogin"),
773 OpacNav => C4::Context->preference("OpacNav"),
774 opaccredits => C4::Context->preference("opaccredits"),
775 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
776 opacsmallimage => C4::Context->preference("opacsmallimage"),
777 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
778 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
779 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
780 opacuserjs => C4::Context->preference("opacuserjs"),
781 opacbookbag => "" . C4::Context->preference("opacbookbag"),
782 OpacCloud => C4::Context->preference("OpacCloud"),
783 OpacTopissue => C4::Context->preference("OpacTopissue"),
784 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
785 OpacBrowser => C4::Context->preference("OpacBrowser"),
786 opacheader => C4::Context->preference("opacheader"),
787 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
788 intranetcolorstylesheet =>
789 C4::Context->preference("intranetcolorstylesheet"),
790 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
791 IntranetNav => C4::Context->preference("IntranetNav"),
792 intranetuserjs => C4::Context->preference("intranetuserjs"),
793 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
794 IndependantBranches=> C4::Context->preference("IndependantBranches"),
795 AutoLocation => C4::Context->preference("AutoLocation"),
796 wrongip => $info{'wrongip'}
799 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
801 my $self_url = $query->url( -absolute => 1 );
804 LibraryName => C4::Context->preference("LibraryName"),
806 $template->param( \%info );
807 # $cookie = $query->cookie(CGISESSID => $session->id
809 print $query->header(
810 -type => 'text/html',
820 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
822 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
823 cookie, determine if the user has the privileges specified by C<$userflags>.
825 C<check_api_auth> is is meant for authenticating users of web services, and
826 consequently will always return and will not attempt to redirect the user
829 If a valid session cookie is already present, check_api_auth will return a status
830 of "ok", the cookie, and the Koha session ID.
832 If no session cookie is present, check_api_auth will check the 'userid' and 'password
833 parameters and create a session cookie and Koha session if the supplied credentials
836 Possible return values in C<$status> are:
840 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
842 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
844 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
846 =item "expired -- session cookie has expired; API user should resubmit userid and password
854 my $flagsrequired = shift;
856 my $dbh = C4::Context->dbh;
857 my $timeout = C4::Context->preference('timeout');
858 $timeout = 600 unless $timeout;
860 unless (C4::Context->preference('Version')) {
861 # database has not been installed yet
862 return ("maintenance", undef, undef);
864 my $kohaversion=C4::Context::KOHAVERSION;
865 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
866 if (C4::Context->preference('Version') < $kohaversion) {
867 # database in need of version update; assume that
868 # no API should be called while databsae is in
870 return ("maintenance", undef, undef);
873 # FIXME -- most of what follows is a copy-and-paste
874 # of code from checkauth. There is an obvious need
875 # for refactoring to separate the various parts of
876 # the authentication code, but as of 2007-11-19 this
877 # is deferred so as to not introduce bugs into the
878 # regular authentication code for Koha 3.0.
880 # see if we have a valid session cookie already
881 # however, if a userid parameter is present (i.e., from
882 # a form submission, assume that any current cookie
884 my $sessionID = undef;
885 unless ($query->param('userid')) {
886 $sessionID = $query->cookie("CGISESSID");
889 my $session = get_session($sessionID);
890 C4::Context->_new_userenv($sessionID);
892 C4::Context::set_userenv(
893 $session->param('number'), $session->param('id'),
894 $session->param('cardnumber'), $session->param('firstname'),
895 $session->param('surname'), $session->param('branch'),
896 $session->param('branchname'), $session->param('flags'),
897 $session->param('emailaddress'), $session->param('branchprinter')
900 my $ip = $session->param('ip');
901 my $lasttime = $session->param('lasttime');
902 my $userid = $session->param('id');
903 if ( $lasttime < time() - $timeout ) {
906 C4::Context->_unset_userenv($sessionID);
909 return ("expired", undef, undef);
910 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
913 C4::Context->_unset_userenv($sessionID);
916 return ("expired", undef, undef);
918 my $cookie = $query->cookie( CGISESSID => $session->id );
919 $session->param('lasttime',time());
920 my $flags = haspermission( $dbh, $userid, $flagsrequired );
922 return ("ok", $cookie, $sessionID);
925 C4::Context->_unset_userenv($sessionID);
928 return ("failed", undef, undef);
932 return ("expired", undef, undef);
936 my $userid = $query->param('userid');
937 my $password = $query->param('password');
938 unless ($userid and $password) {
939 # caller did something wrong, fail the authenticateion
940 return ("failed", undef, undef);
942 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
943 if ($return and haspermission( $dbh, $userid, $flagsrequired)) {
944 my $session = get_session("");
945 return ("failed", undef, undef) unless $session;
947 my $sessionID = $session->id;
948 C4::Context->_new_userenv($sessionID);
949 my $cookie = $query->cookie(CGISESSID => $sessionID);
950 if ( $return == 1 ) {
952 $borrowernumber, $firstname, $surname,
953 $userflags, $branchcode, $branchname,
954 $branchprinter, $emailaddress
958 "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=?"
960 $sth->execute($userid);
962 $borrowernumber, $firstname, $surname,
963 $userflags, $branchcode, $branchname,
964 $branchprinter, $emailaddress
965 ) = $sth->fetchrow if ( $sth->rows );
967 unless ($sth->rows ) {
968 my $sth = $dbh->prepare(
969 "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=?"
971 $sth->execute($cardnumber);
973 $borrowernumber, $firstname, $surname,
974 $userflags, $branchcode, $branchname,
975 $branchprinter, $emailaddress
976 ) = $sth->fetchrow if ( $sth->rows );
978 unless ( $sth->rows ) {
979 $sth->execute($userid);
981 $borrowernumber, $firstname, $surname, $userflags,
982 $branchcode, $branchname, $branchprinter, $emailaddress
983 ) = $sth->fetchrow if ( $sth->rows );
987 my $ip = $ENV{'REMOTE_ADDR'};
988 # if they specify at login, use that
989 if ($query->param('branch')) {
990 $branchcode = $query->param('branch');
991 $branchname = GetBranchName($branchcode);
993 my $branches = GetBranches();
995 foreach my $br ( keys %$branches ) {
996 # now we work with the treatment of ip
997 my $domain = $branches->{$br}->{'branchip'};
998 if ( $domain && $ip =~ /^$domain/ ) {
999 $branchcode = $branches->{$br}->{'branchcode'};
1001 # new op dev : add the branchprinter and branchname in the cookie
1002 $branchprinter = $branches->{$br}->{'branchprinter'};
1003 $branchname = $branches->{$br}->{'branchname'};
1006 $session->param('number',$borrowernumber);
1007 $session->param('id',$userid);
1008 $session->param('cardnumber',$cardnumber);
1009 $session->param('firstname',$firstname);
1010 $session->param('surname',$surname);
1011 $session->param('branch',$branchcode);
1012 $session->param('branchname',$branchname);
1013 $session->param('flags',$userflags);
1014 $session->param('emailaddress',$emailaddress);
1015 $session->param('ip',$session->remote_addr());
1016 $session->param('lasttime',time());
1017 } elsif ( $return == 2 ) {
1018 #We suppose the user is the superlibrarian
1019 $session->param('number',0);
1020 $session->param('id',C4::Context->config('user'));
1021 $session->param('cardnumber',C4::Context->config('user'));
1022 $session->param('firstname',C4::Context->config('user'));
1023 $session->param('surname',C4::Context->config('user'));
1024 $session->param('branch','NO_LIBRARY_SET');
1025 $session->param('branchname','NO_LIBRARY_SET');
1026 $session->param('flags',1);
1027 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1028 $session->param('ip',$session->remote_addr());
1029 $session->param('lasttime',time());
1031 C4::Context::set_userenv(
1032 $session->param('number'), $session->param('id'),
1033 $session->param('cardnumber'), $session->param('firstname'),
1034 $session->param('surname'), $session->param('branch'),
1035 $session->param('branchname'), $session->param('flags'),
1036 $session->param('emailaddress'), $session->param('branchprinter')
1038 return ("ok", $cookie, $sessionID);
1040 return ("failed", undef, undef);
1045 =item check_cookie_auth
1047 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1049 Given a CGISESSID cookie set during a previous login to Koha, determine
1050 if the user has the privileges specified by C<$userflags>.
1052 C<check_cookie_auth> is meant for authenticating special services
1053 such as tools/upload-file.pl that are invoked by other pages that
1054 have been authenticated in the usual way.
1056 Possible return values in C<$status> are:
1060 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1062 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1064 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1066 =item "expired -- session cookie has expired; API user should resubmit userid and password
1072 sub check_cookie_auth {
1074 my $flagsrequired = shift;
1076 my $dbh = C4::Context->dbh;
1077 my $timeout = C4::Context->preference('timeout');
1078 $timeout = 600 unless $timeout;
1080 unless (C4::Context->preference('Version')) {
1081 # database has not been installed yet
1082 return ("maintenance", undef);
1084 my $kohaversion=C4::Context::KOHAVERSION;
1085 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1086 if (C4::Context->preference('Version') < $kohaversion) {
1087 # database in need of version update; assume that
1088 # no API should be called while databsae is in
1090 return ("maintenance", undef);
1093 # FIXME -- most of what follows is a copy-and-paste
1094 # of code from checkauth. There is an obvious need
1095 # for refactoring to separate the various parts of
1096 # the authentication code, but as of 2007-11-23 this
1097 # is deferred so as to not introduce bugs into the
1098 # regular authentication code for Koha 3.0.
1100 # see if we have a valid session cookie already
1101 # however, if a userid parameter is present (i.e., from
1102 # a form submission, assume that any current cookie
1104 unless (defined $cookie and $cookie) {
1105 return ("failed", undef);
1107 my $sessionID = $cookie;
1108 my $session = get_session($sessionID);
1109 C4::Context->_new_userenv($sessionID);
1111 C4::Context::set_userenv(
1112 $session->param('number'), $session->param('id'),
1113 $session->param('cardnumber'), $session->param('firstname'),
1114 $session->param('surname'), $session->param('branch'),
1115 $session->param('branchname'), $session->param('flags'),
1116 $session->param('emailaddress'), $session->param('branchprinter')
1119 my $ip = $session->param('ip');
1120 my $lasttime = $session->param('lasttime');
1121 my $userid = $session->param('id');
1122 if ( $lasttime < time() - $timeout ) {
1125 C4::Context->_unset_userenv($sessionID);
1128 return ("expired", undef);
1129 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1130 # IP address changed
1132 C4::Context->_unset_userenv($sessionID);
1135 return ("expired", undef);
1137 $session->param('lasttime',time());
1138 my $flags = haspermission( $dbh, $userid, $flagsrequired );
1140 return ("ok", $sessionID);
1143 C4::Context->_unset_userenv($sessionID);
1146 return ("failed", undef);
1150 return ("expired", undef);
1157 my $session = get_session($sessionID);
1159 Given a session ID, retrieve the CGI::Session object used to store
1160 the session's state. The session object can be used to store
1161 data that needs to be accessed by different scripts during a
1164 If the C<$sessionID> parameter is an empty string, a new session
1170 my $sessionID = shift;
1171 my $storage_method = C4::Context->preference('SessionStorage');
1172 my $dbh = C4::Context->dbh;
1174 if ($storage_method eq 'mysql'){
1175 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1177 elsif ($storage_method eq 'Pg') {
1178 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1181 # catch all defaults to tmp should work on all systems
1182 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1189 my ( $dbh, $userid, $password ) = @_;
1191 $debug and print "## checkpw - checking LDAP\n";
1192 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1193 ($retval) and return ($retval,$retcard);
1199 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1201 $sth->execute($userid);
1203 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1204 $surname, $branchcode, $flags )
1206 if ( md5_base64($password) eq $md5password ) {
1208 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1209 $firstname, $surname, $branchcode, $flags );
1210 return 1, $cardnumber;
1215 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1217 $sth->execute($userid);
1219 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1220 $surname, $branchcode, $flags )
1222 if ( md5_base64($password) eq $md5password ) {
1224 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1225 $firstname, $surname, $branchcode, $flags );
1229 if ( $userid && $userid eq C4::Context->config('user')
1230 && "$password" eq C4::Context->config('pass') )
1233 # Koha superuser account
1234 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1237 if ( $userid && $userid eq 'demo'
1238 && "$password" eq 'demo'
1239 && C4::Context->config('demo') )
1242 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1243 # some features won't be effective : modify systempref, modify MARC structure,
1251 $authflags = getuserflags($flags,$dbh);
1252 Translates integer flags into permissions strings hash.
1254 C<$flags> is the integer userflags value ( borrowers.userflags )
1255 C<$authflags> is a hashref of permissions
1264 $flags = 0 unless $flags;
1265 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1268 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1269 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1270 $userflags->{$flag} = 1;
1273 $userflags->{$flag} = 0;
1277 # get subpermissions and merge with top-level permissions
1278 my $user_subperms = get_user_subpermissions($userid);
1279 foreach my $module (keys %$user_subperms) {
1280 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1281 $userflags->{$module} = $user_subperms->{$module};
1287 =item get_user_subpermissions
1291 my $user_perm_hashref = get_user_subpermissions($userid);
1295 Given the userid (note, not the borrowernumber) of a staff user,
1296 return a hashref of hashrefs of the specific subpermissions
1297 accorded to the user. An example return is
1301 export_catalog => 1,
1302 import_patrons => 1,
1306 The top-level hash-key is a module or function code from
1307 userflags.flag, while the second-level key is a code
1310 The results of this function do not give a complete picture
1311 of the functions that a staff user can access; it is also
1312 necessary to check borrowers.flags.
1316 sub get_user_subpermissions {
1319 my $dbh = C4::Context->dbh;
1320 my $sth = $dbh->prepare("SELECT flag, code
1321 FROM user_permissions
1322 JOIN permissions USING (module_bit, code)
1323 JOIN userflags ON (module_bit = bit)
1324 JOIN borrowers USING (borrowernumber)
1326 $sth->execute($userid);
1328 my $user_perms = {};
1329 while (my $perm = $sth->fetchrow_hashref) {
1330 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1335 =item get_all_subpermissions
1339 my $perm_hashref = get_all_subpermissions();
1343 Returns a hashref of hashrefs defining all specific
1344 permissions currently defined. The return value
1345 has the same structure as that of C<get_user_subpermissions>,
1346 except that the innermost hash value is the description
1347 of the subpermission.
1351 sub get_all_subpermissions {
1352 my $dbh = C4::Context->dbh;
1353 my $sth = $dbh->prepare("SELECT flag, code, description
1355 JOIN userflags ON (module_bit = bit)");
1359 while (my $perm = $sth->fetchrow_hashref) {
1360 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1367 $flags = ($dbh,$member,$flagsrequired);
1369 C<$member> may be either userid or overloaded with $borrower hashref from GetMemberDetails.
1370 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1372 Returns member's flags or 0 if a permission is not met.
1377 my ( $dbh, $userid, $flagsrequired ) = @_;
1378 my ($flags,$intflags);
1379 $dbh=C4::Context->dbh unless($dbh);
1381 $intflags = $userid->{'flags'};
1383 my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1384 $sth->execute($userid);
1385 my ($intflags) = $sth->fetchrow;
1386 $flags = getuserflags( $intflags, $userid, $dbh );
1388 if ( $userid eq C4::Context->config('user') ) {
1389 # Super User Account from /etc/koha.conf
1390 $flags->{'superlibrarian'} = 1;
1392 if ( $userid eq 'demo' && C4::Context->config('demo') ) {
1393 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1394 $flags->{'superlibrarian'} = 1;
1396 return $flags if $flags->{superlibrarian};
1397 foreach my $module ( keys %$flagsrequired ) {
1398 if (C4::Context->preference('GranularPermissions')) {
1399 my $subperm = $flagsrequired->{$module};
1400 if ($subperm eq '*') {
1401 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1403 return 0 unless ( $flags->{$module} == 1 or
1404 ( ref($flags->{$module}) and
1405 exists $flags->{$module}->{$subperm} and
1406 $flags->{$module}->{$subperm} == 1
1411 return 0 unless ( $flags->{$module} );
1415 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1419 sub getborrowernumber {
1421 my $dbh = C4::Context->dbh;
1422 for my $field ( 'userid', 'cardnumber' ) {
1424 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1425 $sth->execute($userid);
1427 my ($bnumber) = $sth->fetchrow;
1434 END { } # module clean-up code here (global destructor)