3 # NOTE: This file uses 8-character tabs; do not change the tab size!
7 # Copyright 2000-2002 Katipo Communications
9 # This file is part of Koha.
11 # Koha is free software; you can redistribute it and/or modify it under the
12 # terms of the GNU General Public License as published by the Free Software
13 # Foundation; either version 2 of the License, or (at your option) any later
16 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
17 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
18 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License along with
21 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
22 # Suite 330, Boston, MA 02111-1307 USA
25 use Digest::MD5 qw(md5_base64);
30 use C4::Output; # to get the template
33 use C4::Branch; # GetBranches
34 use C4::VirtualShelves 3.02 qw(GetShelvesSummary);
37 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap);
40 $VERSION = 3.02; # set version for version checking
41 $debug = $ENV{DEBUG} || 0 ;
43 @EXPORT = qw(&checkauth &get_template_and_user);
44 @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &get_all_subpermissions &get_user_subpermissions);
45 %EXPORT_TAGS = (EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)]);
46 $ldap = C4::Context->config('useldapserver') || 0;
48 require C4::Auth_with_ldap; # no import
49 import C4::Auth_with_ldap qw(checkpw_ldap);
55 C4::Auth - Authenticates Koha users
65 my ($template, $borrowernumber, $cookie)
66 = get_template_and_user(
68 template_name => "opac-main.tmpl",
72 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
76 output_html_with_http_headers $query, $cookie, $template->output;
80 The main function of this module is to provide
81 authentification. However the get_template_and_user function has
82 been provided so that a users login information is passed along
83 automatically. This gets loaded into the template.
89 =item get_template_and_user
91 my ($template, $borrowernumber, $cookie)
92 = get_template_and_user(
94 template_name => "opac-main.tmpl",
98 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
102 This call passes the C<query>, C<flagsrequired> and C<authnotrequired>
103 to C<&checkauth> (in this module) to perform authentification.
104 See C<&checkauth> for an explanation of these parameters.
106 The C<template_name> is then used to find the correct template for
107 the page. The authenticated users details are loaded onto the
108 template in the HTML::Template LOOP variable C<USER_INFO>. Also the
109 C<sessionID> is passed to the template. This can be used in templates
110 if cookies are disabled. It needs to be put as and input to every
113 More information on the C<gettemplate> sub can be found in the
118 sub get_template_and_user {
121 gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'} );
122 my ( $user, $cookie, $sessionID, $flags ) = checkauth(
124 $in->{'authnotrequired'},
125 $in->{'flagsrequired'},
127 ) unless ($in->{'template_name'}=~/maintenance/);
130 my $insecure = C4::Context->preference('insecure');
131 if ($user or $insecure) {
133 # load the template variables for stylesheets and JavaScript
134 $template->param( css_libs => $in->{'css_libs'} );
135 $template->param( css_module => $in->{'css_module'} );
136 $template->param( css_page => $in->{'css_page'} );
137 $template->param( css_widgets => $in->{'css_widgets'} );
139 $template->param( js_libs => $in->{'js_libs'} );
140 $template->param( js_module => $in->{'js_module'} );
141 $template->param( js_page => $in->{'js_page'} );
142 $template->param( js_widgets => $in->{'js_widgets'} );
145 $template->param( loggedinusername => $user );
146 $template->param( sessionID => $sessionID );
148 my ($pubshelves, $barshelves) = C4::Context->get_shelves_userenv();
149 if (defined($pubshelves)) {
150 $template->param( pubshelves => scalar (@$pubshelves));
151 $template->param( pubshelvesloop => $pubshelves);
153 if (defined($barshelves)) {
154 $template->param( barshelves => scalar (@$barshelves));
155 $template->param( barshelvesloop => $barshelves);
158 $borrowernumber = getborrowernumber($user);
159 my ( $borr, $alternativeflags ) =
160 GetMemberDetails( $borrowernumber );
163 $template->param( "USER_INFO" => \@bordat );
165 my $all_perms = get_all_subpermissions();
167 my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow
168 editcatalogue updatecharges management tools editauthorities serials reports);
169 # We are going to use the $flags returned by checkauth
170 # to create the template's parameters that will indicate
171 # which menus the user can access.
172 if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) {
173 $template->param( CAN_user_circulate => 1 );
174 $template->param( CAN_user_catalogue => 1 );
175 $template->param( CAN_user_parameters => 1 );
176 $template->param( CAN_user_borrowers => 1 );
177 $template->param( CAN_user_permissions => 1 );
178 $template->param( CAN_user_reserveforothers => 1 );
179 $template->param( CAN_user_borrow => 1 );
180 $template->param( CAN_user_editcatalogue => 1 );
181 $template->param( CAN_user_updatecharges => 1 );
182 $template->param( CAN_user_acquisition => 1 );
183 $template->param( CAN_user_management => 1 );
184 $template->param( CAN_user_tools => 1 );
185 $template->param( CAN_user_editauthorities => 1 );
186 $template->param( CAN_user_serials => 1 );
187 $template->param( CAN_user_reports => 1 );
188 $template->param( CAN_user_staffaccess => 1 );
189 foreach my $module (keys %$all_perms) {
190 foreach my $subperm (keys %{ $all_perms->{$module} }) {
191 $template->param( "CAN_user_${module}_${subperm}" => 1 );
196 if (C4::Context->preference('GranularPermissions')) {
198 foreach my $module (keys %$all_perms) {
199 if ( $flags->{$module} == 1) {
200 foreach my $subperm (keys %{ $all_perms->{$module} }) {
201 $template->param( "CAN_user_${module}_${subperm}" => 1 );
203 } elsif ( ref($flags->{$module}) ) {
204 foreach my $subperm (keys %{ $flags->{$module} } ) {
205 $template->param( "CAN_user_${module}_${subperm}" => 1 );
211 foreach my $module (keys %$all_perms) {
212 foreach my $subperm (keys %{ $all_perms->{$module} }) {
213 $template->param( "CAN_user_${module}_${subperm}" => 1 );
219 foreach my $module (keys %$flags) {
220 if ( $flags->{$module} == 1 or ref($flags->{$module}) ) {
221 $template->param( "CAN_user_$module" => 1 );
222 if ($module eq "parameters") {
223 $template->param( CAN_user_management => 1 );
229 else { # if this is an anonymous session, setup to display public lists...
231 # load the template variables for stylesheets and JavaScript
232 $template->param( css_libs => $in->{'css_libs'} );
233 $template->param( css_module => $in->{'css_module'} );
234 $template->param( css_page => $in->{'css_page'} );
235 $template->param( css_widgets => $in->{'css_widgets'} );
237 $template->param( js_libs => $in->{'js_libs'} );
238 $template->param( js_module => $in->{'js_module'} );
239 $template->param( js_page => $in->{'js_page'} );
240 $template->param( js_widgets => $in->{'js_widgets'} );
242 $template->param( sessionID => $sessionID );
244 my ($pubshelves) = C4::Context->get_shelves_userenv(); # an anonymous user has no 'barshelves'...
245 if (defined(($pubshelves))) {
246 $template->param( pubshelves => scalar (@$pubshelves));
247 $template->param( pubshelvesloop => $pubshelves);
252 # these template parameters are set the same regardless of $in->{'type'}
254 "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
255 EnhancedMessagingPreferences => C4::Context->preference('EnhancedMessagingPreferences'),
256 GoogleJackets => C4::Context->preference("GoogleJackets"),
257 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
258 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
259 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
260 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
261 TagsEnabled => C4::Context->preference("TagsEnabled"),
262 hide_marc => C4::Context->preference("hide_marc"),
263 'item-level_itypes' => C4::Context->preference('item-level_itypes'),
264 patronimages => C4::Context->preference("patronimages"),
265 singleBranchMode => C4::Context->preference("singleBranchMode"),
268 if ( $in->{'type'} eq "intranet" ) {
270 AmazonContent => C4::Context->preference("AmazonContent"),
271 AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
272 AutoLocation => C4::Context->preference("AutoLocation"),
273 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
274 CircAutocompl => C4::Context->preference("CircAutocompl"),
275 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
276 IndependantBranches => C4::Context->preference("IndependantBranches"),
277 IntranetNav => C4::Context->preference("IntranetNav"),
278 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
279 LibraryName => C4::Context->preference("LibraryName"),
280 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
281 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
282 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
283 canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
284 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
285 intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
286 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
287 intranetuserjs => C4::Context->preference("intranetuserjs"),
288 noItemTypeImages => C4::Context->preference("noItemTypeImages"),
289 suggestion => C4::Context->preference("suggestion"),
290 virtualshelves => C4::Context->preference("virtualshelves"),
294 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
295 my $LibraryNameTitle = C4::Context->preference("LibraryName");
296 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
297 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
299 AmazonContent => "" . C4::Context->preference("AmazonContent"),
300 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
301 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
302 LibraryName => "" . C4::Context->preference("LibraryName"),
303 LibraryNameTitle => "" . $LibraryNameTitle,
304 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
305 OPACAmazonSimilarItems => "" . C4::Context->preference("OPACAmazonSimilarItems"),
306 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
307 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
308 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
309 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
310 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
311 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
312 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
313 OPACBaseURL => ($in->{'query'}->https() ? "https://" : "http://") .
314 $ENV{'SERVER_NAME'} .
315 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
316 OpacBrowser => C4::Context->preference("OpacBrowser"),
317 OpacCloud => C4::Context->preference("OpacCloud"),
318 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
319 OpacNav => "" . C4::Context->preference("OpacNav"),
320 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
321 OpacTopissue => C4::Context->preference("OpacTopissue"),
322 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
323 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
324 'Version' => C4::Context->preference('Version'),
325 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
326 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
327 hidelostitems => C4::Context->preference("hidelostitems"),
328 mylibraryfirst => C4::Context->preference("SearchMyLibraryFirst"),
329 opacbookbag => "" . C4::Context->preference("opacbookbag"),
330 opaccolorstylesheet => "". C4::Context->preference("opaccolorstylesheet"),
331 opaccredits => "" . C4::Context->preference("opaccredits"),
332 opacheader => "" . C4::Context->preference("opacheader"),
333 opaclanguagesdisplay => "". C4::Context->preference("opaclanguagesdisplay"),
334 opaclayoutstylesheet => "". C4::Context->preference("opaclayoutstylesheet"),
335 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
336 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
337 opacuserjs => C4::Context->preference("opacuserjs"),
338 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
339 reviewson => C4::Context->preference("reviewson"),
340 suggestion => "" . C4::Context->preference("suggestion"),
341 virtualshelves => "" . C4::Context->preference("virtualshelves"),
344 $template->param(listloop=>[{shelfname=>"Freelist", shelfnumber=>110}]);
345 return ( $template, $borrowernumber, $cookie, $flags);
350 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
352 Verifies that the user is authorized to run this script. If
353 the user is authorized, a (userid, cookie, session-id, flags)
354 quadruple is returned. If the user is not authorized but does
355 not have the required privilege (see $flagsrequired below), it
356 displays an error page and exits. Otherwise, it displays the
357 login page and exits.
359 Note that C<&checkauth> will return if and only if the user
360 is authorized, so it should be called early on, before any
361 unfinished operations (e.g., if you've opened a file, then
362 C<&checkauth> won't close it for you).
364 C<$query> is the CGI object for the script calling C<&checkauth>.
366 The C<$noauth> argument is optional. If it is set, then no
367 authorization is required for the script.
369 C<&checkauth> fetches user and session information from C<$query> and
370 ensures that the user is authorized to run scripts that require
373 The C<$flagsrequired> argument specifies the required privileges
374 the user must have if the username and password are correct.
375 It should be specified as a reference-to-hash; keys in the hash
376 should be the "flags" for the user, as specified in the Members
377 intranet module. Any key specified must correspond to a "flag"
378 in the userflags table. E.g., { circulate => 1 } would specify
379 that the user must have the "circulate" privilege in order to
380 proceed. To make sure that access control is correct, the
381 C<$flagsrequired> parameter must be specified correctly.
383 If the GranularPermissions system preference is ON, the
384 value of each key in the C<flagsrequired> hash takes on an additional
389 The user must have access to all subfunctions of the module
390 specified by the hash key.
394 The user must have access to at least one subfunction of the module
395 specified by the hash key.
397 =item specific permission, e.g., 'export_catalog'
399 The user must have access to the specific subfunction list, which
400 must correspond to a row in the permissions table.
402 The C<$type> argument specifies whether the template should be
403 retrieved from the opac or intranet directory tree. "opac" is
404 assumed if it is not specified; however, if C<$type> is specified,
405 "intranet" is assumed if it is not "opac".
407 If C<$query> does not have a valid session ID associated with it
408 (i.e., the user has not logged in) or if the session has expired,
409 C<&checkauth> presents the user with a login page (from the point of
410 view of the original script, C<&checkauth> does not return). Once the
411 user has authenticated, C<&checkauth> restarts the original script
412 (this time, C<&checkauth> returns).
414 The login page is provided using a HTML::Template, which is set in the
415 systempreferences table or at the top of this file. The variable C<$type>
416 selects which template to use, either the opac or the intranet
417 authentification template.
419 C<&checkauth> returns a user ID, a cookie, and a session ID. The
420 cookie should be sent back to the browser; it verifies that the user
425 sub _version_check ($$) {
429 # If Version syspref is unavailable, it means Koha is beeing installed,
430 # and so we must redirect to OPAC maintenance page or to the WebInstaller
431 #warn "about to check version";
432 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
433 if ($type ne 'opac') {
434 warn "Install required, redirecting to Installer";
435 print $query->redirect("/cgi-bin/koha/installer/install.pl");
438 warn "OPAC Install required, redirecting to maintenance";
439 print $query->redirect("/cgi-bin/koha/maintenance.pl");
444 # check that database and koha version are the same
445 # there is no DB version, it's a fresh install,
446 # go to web installer
447 # there is a DB version, compare it to the code version
448 my $kohaversion=C4::Context::KOHAVERSION;
449 # remove the 3 last . to have a Perl number
450 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
451 $debug and print STDERR "kohaversion : $kohaversion\n";
452 if ($version < $kohaversion){
453 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
454 if ($type ne 'opac'){
455 warn sprintf($warning, 'Installer');
456 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
458 warn sprintf("OPAC: " . $warning, 'maintenance');
459 print $query->redirect("/cgi-bin/koha/maintenance.pl");
467 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
468 printf L join("\n",@_);
474 $debug and warn "Checking Auth";
475 # $authnotrequired will be set for scripts which will run without authentication
476 my $authnotrequired = shift;
477 my $flagsrequired = shift;
479 $type = 'opac' unless $type;
481 my $dbh = C4::Context->dbh;
482 my $timeout = C4::Context->preference('timeout');
484 if ($timeout =~ /(\d+)[dD]/) {
485 $timeout = $1 * 86400;
487 $timeout = 600 unless $timeout;
489 _version_check($type,$query);
493 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
494 my $logout = $query->param('logout.x');
496 if ( $userid = $ENV{'REMOTE_USER'} ) {
497 # Using Basic Authentication, no cookies required
498 $cookie = $query->cookie(
499 -name => 'CGISESSID',
505 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
506 my $session = get_session($sessionID);
507 C4::Context->_new_userenv($sessionID);
508 my ($ip, $lasttime, $sessiontype);
510 C4::Context::set_userenv(
511 $session->param('number'), $session->param('id'),
512 $session->param('cardnumber'), $session->param('firstname'),
513 $session->param('surname'), $session->param('branch'),
514 $session->param('branchname'), $session->param('flags'),
515 $session->param('emailaddress'), $session->param('branchprinter')
517 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
518 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
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')
699 # Grab borrower's shelves and add to the session...
700 $barshelves = GetShelvesSummary($borrowernumber,2,10);
701 $session->param('barshelves', $barshelves);
702 C4::Context::set_shelves_userenv('bar',$barshelves);
704 # Grab the public shelves and add to the session...
705 $pubshelves = GetShelvesSummary(0,2,10);
706 $session->param('pubshelves', $pubshelves);
707 C4::Context::set_shelves_userenv('pub',$pubshelves);
711 $info{'invalid_username_or_password'} = 1;
712 C4::Context->_unset_userenv($sessionID);
715 } # END if ( $userid = $query->param('userid') )
716 elsif ($type eq "opac") {
717 # if we are here this is an anonymous session; add public lists to it and a few other items...
718 # anonymous sessions are created only for the OPAC
719 $debug and warn "Initiating an anonymous session...";
721 # Grab the public shelves and add to the session...
722 $pubshelves = GetShelvesSummary(0,2,10);
723 $session->param('pubshelves', $pubshelves);
724 C4::Context::set_shelves_userenv('pub',$pubshelves);
726 # setting a couple of other session vars...
727 $session->param('ip',$session->remote_addr());
728 $session->param('lasttime',time());
729 $session->param('sessiontype','anon');
731 } # END unless ($userid)
732 my $insecure = C4::Context->boolean_preference('insecure');
734 # finished authentification, now respond
735 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
739 $cookie = $query->cookie( CGISESSID => '' );
741 return ( $userid, $cookie, $sessionID, $flags );
746 # AUTH rejected, show the login/password template, after checking the DB.
750 # get the inputs from the incoming query
752 foreach my $name ( param $query) {
753 (next) if ( $name eq 'userid' || $name eq 'password' );
754 my $value = $query->param($name);
755 push @inputs, { name => $name, value => $value };
757 # get the branchloop, which we need for authentication
758 my $branches = GetBranches();
760 for my $branch_hash (sort keys %$branches) {
761 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
764 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
765 my $template = gettemplate( $template_name, $type, $query );
766 $template->param(branchloop => \@branch_loop,);
770 suggestion => C4::Context->preference("suggestion"),
771 virtualshelves => C4::Context->preference("virtualshelves"),
772 LibraryName => C4::Context->preference("LibraryName"),
773 opacuserlogin => C4::Context->preference("opacuserlogin"),
774 OpacNav => C4::Context->preference("OpacNav"),
775 opaccredits => C4::Context->preference("opaccredits"),
776 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
777 opacsmallimage => C4::Context->preference("opacsmallimage"),
778 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
779 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
780 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
781 opacuserjs => C4::Context->preference("opacuserjs"),
782 opacbookbag => "" . C4::Context->preference("opacbookbag"),
783 OpacCloud => C4::Context->preference("OpacCloud"),
784 OpacTopissue => C4::Context->preference("OpacTopissue"),
785 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
786 OpacBrowser => C4::Context->preference("OpacBrowser"),
787 opacheader => C4::Context->preference("opacheader"),
788 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
789 intranetcolorstylesheet =>
790 C4::Context->preference("intranetcolorstylesheet"),
791 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
792 IntranetNav => C4::Context->preference("IntranetNav"),
793 intranetuserjs => C4::Context->preference("intranetuserjs"),
794 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
795 IndependantBranches=> C4::Context->preference("IndependantBranches"),
796 AutoLocation => C4::Context->preference("AutoLocation"),
797 wrongip => $info{'wrongip'}
800 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
802 my $self_url = $query->url( -absolute => 1 );
805 LibraryName => C4::Context->preference("LibraryName"),
807 $template->param( \%info );
808 # $cookie = $query->cookie(CGISESSID => $session->id
810 print $query->header(
811 -type => 'text/html',
821 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
823 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
824 cookie, determine if the user has the privileges specified by C<$userflags>.
826 C<check_api_auth> is is meant for authenticating users of web services, and
827 consequently will always return and will not attempt to redirect the user
830 If a valid session cookie is already present, check_api_auth will return a status
831 of "ok", the cookie, and the Koha session ID.
833 If no session cookie is present, check_api_auth will check the 'userid' and 'password
834 parameters and create a session cookie and Koha session if the supplied credentials
837 Possible return values in C<$status> are:
841 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
843 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
845 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
847 =item "expired -- session cookie has expired; API user should resubmit userid and password
855 my $flagsrequired = shift;
857 my $dbh = C4::Context->dbh;
858 my $timeout = C4::Context->preference('timeout');
859 $timeout = 600 unless $timeout;
861 unless (C4::Context->preference('Version')) {
862 # database has not been installed yet
863 return ("maintenance", undef, undef);
865 my $kohaversion=C4::Context::KOHAVERSION;
866 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
867 if (C4::Context->preference('Version') < $kohaversion) {
868 # database in need of version update; assume that
869 # no API should be called while databsae is in
871 return ("maintenance", undef, undef);
874 # FIXME -- most of what follows is a copy-and-paste
875 # of code from checkauth. There is an obvious need
876 # for refactoring to separate the various parts of
877 # the authentication code, but as of 2007-11-19 this
878 # is deferred so as to not introduce bugs into the
879 # regular authentication code for Koha 3.0.
881 # see if we have a valid session cookie already
882 # however, if a userid parameter is present (i.e., from
883 # a form submission, assume that any current cookie
885 my $sessionID = undef;
886 unless ($query->param('userid')) {
887 $sessionID = $query->cookie("CGISESSID");
890 my $session = get_session($sessionID);
891 C4::Context->_new_userenv($sessionID);
893 C4::Context::set_userenv(
894 $session->param('number'), $session->param('id'),
895 $session->param('cardnumber'), $session->param('firstname'),
896 $session->param('surname'), $session->param('branch'),
897 $session->param('branchname'), $session->param('flags'),
898 $session->param('emailaddress'), $session->param('branchprinter')
901 my $ip = $session->param('ip');
902 my $lasttime = $session->param('lasttime');
903 my $userid = $session->param('id');
904 if ( $lasttime < time() - $timeout ) {
907 C4::Context->_unset_userenv($sessionID);
910 return ("expired", undef, undef);
911 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
914 C4::Context->_unset_userenv($sessionID);
917 return ("expired", undef, undef);
919 my $cookie = $query->cookie( CGISESSID => $session->id );
920 $session->param('lasttime',time());
921 my $flags = haspermission( $dbh, $userid, $flagsrequired );
923 return ("ok", $cookie, $sessionID);
926 C4::Context->_unset_userenv($sessionID);
929 return ("failed", undef, undef);
933 return ("expired", undef, undef);
937 my $userid = $query->param('userid');
938 my $password = $query->param('password');
939 unless ($userid and $password) {
940 # caller did something wrong, fail the authenticateion
941 return ("failed", undef, undef);
943 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
944 if ($return and haspermission( $dbh, $userid, $flagsrequired)) {
945 my $session = get_session("");
946 return ("failed", undef, undef) unless $session;
948 my $sessionID = $session->id;
949 C4::Context->_new_userenv($sessionID);
950 my $cookie = $query->cookie(CGISESSID => $sessionID);
951 if ( $return == 1 ) {
953 $borrowernumber, $firstname, $surname,
954 $userflags, $branchcode, $branchname,
955 $branchprinter, $emailaddress
959 "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=?"
961 $sth->execute($userid);
963 $borrowernumber, $firstname, $surname,
964 $userflags, $branchcode, $branchname,
965 $branchprinter, $emailaddress
966 ) = $sth->fetchrow if ( $sth->rows );
968 unless ($sth->rows ) {
969 my $sth = $dbh->prepare(
970 "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=?"
972 $sth->execute($cardnumber);
974 $borrowernumber, $firstname, $surname,
975 $userflags, $branchcode, $branchname,
976 $branchprinter, $emailaddress
977 ) = $sth->fetchrow if ( $sth->rows );
979 unless ( $sth->rows ) {
980 $sth->execute($userid);
982 $borrowernumber, $firstname, $surname, $userflags,
983 $branchcode, $branchname, $branchprinter, $emailaddress
984 ) = $sth->fetchrow if ( $sth->rows );
988 my $ip = $ENV{'REMOTE_ADDR'};
989 # if they specify at login, use that
990 if ($query->param('branch')) {
991 $branchcode = $query->param('branch');
992 $branchname = GetBranchName($branchcode);
994 my $branches = GetBranches();
996 foreach my $br ( keys %$branches ) {
997 # now we work with the treatment of ip
998 my $domain = $branches->{$br}->{'branchip'};
999 if ( $domain && $ip =~ /^$domain/ ) {
1000 $branchcode = $branches->{$br}->{'branchcode'};
1002 # new op dev : add the branchprinter and branchname in the cookie
1003 $branchprinter = $branches->{$br}->{'branchprinter'};
1004 $branchname = $branches->{$br}->{'branchname'};
1007 $session->param('number',$borrowernumber);
1008 $session->param('id',$userid);
1009 $session->param('cardnumber',$cardnumber);
1010 $session->param('firstname',$firstname);
1011 $session->param('surname',$surname);
1012 $session->param('branch',$branchcode);
1013 $session->param('branchname',$branchname);
1014 $session->param('flags',$userflags);
1015 $session->param('emailaddress',$emailaddress);
1016 $session->param('ip',$session->remote_addr());
1017 $session->param('lasttime',time());
1018 } elsif ( $return == 2 ) {
1019 #We suppose the user is the superlibrarian
1020 $session->param('number',0);
1021 $session->param('id',C4::Context->config('user'));
1022 $session->param('cardnumber',C4::Context->config('user'));
1023 $session->param('firstname',C4::Context->config('user'));
1024 $session->param('surname',C4::Context->config('user'));
1025 $session->param('branch','NO_LIBRARY_SET');
1026 $session->param('branchname','NO_LIBRARY_SET');
1027 $session->param('flags',1);
1028 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1029 $session->param('ip',$session->remote_addr());
1030 $session->param('lasttime',time());
1032 C4::Context::set_userenv(
1033 $session->param('number'), $session->param('id'),
1034 $session->param('cardnumber'), $session->param('firstname'),
1035 $session->param('surname'), $session->param('branch'),
1036 $session->param('branchname'), $session->param('flags'),
1037 $session->param('emailaddress'), $session->param('branchprinter')
1039 return ("ok", $cookie, $sessionID);
1041 return ("failed", undef, undef);
1046 =item check_cookie_auth
1048 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1050 Given a CGISESSID cookie set during a previous login to Koha, determine
1051 if the user has the privileges specified by C<$userflags>.
1053 C<check_cookie_auth> is meant for authenticating special services
1054 such as tools/upload-file.pl that are invoked by other pages that
1055 have been authenticated in the usual way.
1057 Possible return values in C<$status> are:
1061 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1063 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1065 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1067 =item "expired -- session cookie has expired; API user should resubmit userid and password
1073 sub check_cookie_auth {
1075 my $flagsrequired = shift;
1077 my $dbh = C4::Context->dbh;
1078 my $timeout = C4::Context->preference('timeout');
1079 $timeout = 600 unless $timeout;
1081 unless (C4::Context->preference('Version')) {
1082 # database has not been installed yet
1083 return ("maintenance", undef);
1085 my $kohaversion=C4::Context::KOHAVERSION;
1086 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1087 if (C4::Context->preference('Version') < $kohaversion) {
1088 # database in need of version update; assume that
1089 # no API should be called while databsae is in
1091 return ("maintenance", undef);
1094 # FIXME -- most of what follows is a copy-and-paste
1095 # of code from checkauth. There is an obvious need
1096 # for refactoring to separate the various parts of
1097 # the authentication code, but as of 2007-11-23 this
1098 # is deferred so as to not introduce bugs into the
1099 # regular authentication code for Koha 3.0.
1101 # see if we have a valid session cookie already
1102 # however, if a userid parameter is present (i.e., from
1103 # a form submission, assume that any current cookie
1105 unless (defined $cookie and $cookie) {
1106 return ("failed", undef);
1108 my $sessionID = $cookie;
1109 my $session = get_session($sessionID);
1110 C4::Context->_new_userenv($sessionID);
1112 C4::Context::set_userenv(
1113 $session->param('number'), $session->param('id'),
1114 $session->param('cardnumber'), $session->param('firstname'),
1115 $session->param('surname'), $session->param('branch'),
1116 $session->param('branchname'), $session->param('flags'),
1117 $session->param('emailaddress'), $session->param('branchprinter')
1120 my $ip = $session->param('ip');
1121 my $lasttime = $session->param('lasttime');
1122 my $userid = $session->param('id');
1123 if ( $lasttime < time() - $timeout ) {
1126 C4::Context->_unset_userenv($sessionID);
1129 return ("expired", undef);
1130 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1131 # IP address changed
1133 C4::Context->_unset_userenv($sessionID);
1136 return ("expired", undef);
1138 $session->param('lasttime',time());
1139 my $flags = haspermission( $dbh, $userid, $flagsrequired );
1141 return ("ok", $sessionID);
1144 C4::Context->_unset_userenv($sessionID);
1147 return ("failed", undef);
1151 return ("expired", undef);
1158 my $session = get_session($sessionID);
1160 Given a session ID, retrieve the CGI::Session object used to store
1161 the session's state. The session object can be used to store
1162 data that needs to be accessed by different scripts during a
1165 If the C<$sessionID> parameter is an empty string, a new session
1171 my $sessionID = shift;
1172 my $storage_method = C4::Context->preference('SessionStorage');
1173 my $dbh = C4::Context->dbh;
1175 if ($storage_method eq 'mysql'){
1176 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1178 elsif ($storage_method eq 'Pg') {
1179 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1182 # catch all defaults to tmp should work on all systems
1183 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1190 my ( $dbh, $userid, $password ) = @_;
1192 $debug and print "## checkpw - checking LDAP\n";
1193 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1194 ($retval) and return ($retval,$retcard);
1200 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1202 $sth->execute($userid);
1204 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1205 $surname, $branchcode, $flags )
1207 if ( md5_base64($password) eq $md5password ) {
1209 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1210 $firstname, $surname, $branchcode, $flags );
1211 return 1, $cardnumber;
1216 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1218 $sth->execute($userid);
1220 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1221 $surname, $branchcode, $flags )
1223 if ( md5_base64($password) eq $md5password ) {
1225 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1226 $firstname, $surname, $branchcode, $flags );
1230 if ( $userid && $userid eq C4::Context->config('user')
1231 && "$password" eq C4::Context->config('pass') )
1234 # Koha superuser account
1235 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1238 if ( $userid && $userid eq 'demo'
1239 && "$password" eq 'demo'
1240 && C4::Context->config('demo') )
1243 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1244 # some features won't be effective : modify systempref, modify MARC structure,
1252 $authflags = getuserflags($flags,$dbh);
1253 Translates integer flags into permissions strings hash.
1255 C<$flags> is the integer userflags value ( borrowers.userflags )
1256 C<$authflags> is a hashref of permissions
1265 $flags = 0 unless $flags;
1266 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1269 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1270 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1271 $userflags->{$flag} = 1;
1274 $userflags->{$flag} = 0;
1278 # get subpermissions and merge with top-level permissions
1279 my $user_subperms = get_user_subpermissions($userid);
1280 foreach my $module (keys %$user_subperms) {
1281 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1282 $userflags->{$module} = $user_subperms->{$module};
1288 =item get_user_subpermissions
1292 my $user_perm_hashref = get_user_subpermissions($userid);
1296 Given the userid (note, not the borrowernumber) of a staff user,
1297 return a hashref of hashrefs of the specific subpermissions
1298 accorded to the user. An example return is
1302 export_catalog => 1,
1303 import_patrons => 1,
1307 The top-level hash-key is a module or function code from
1308 userflags.flag, while the second-level key is a code
1311 The results of this function do not give a complete picture
1312 of the functions that a staff user can access; it is also
1313 necessary to check borrowers.flags.
1317 sub get_user_subpermissions {
1320 my $dbh = C4::Context->dbh;
1321 my $sth = $dbh->prepare("SELECT flag, code
1322 FROM user_permissions
1323 JOIN permissions USING (module_bit, code)
1324 JOIN userflags ON (module_bit = bit)
1325 JOIN borrowers USING (borrowernumber)
1327 $sth->execute($userid);
1329 my $user_perms = {};
1330 while (my $perm = $sth->fetchrow_hashref) {
1331 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1336 =item get_all_subpermissions
1340 my $perm_hashref = get_all_subpermissions();
1344 Returns a hashref of hashrefs defining all specific
1345 permissions currently defined. The return value
1346 has the same structure as that of C<get_user_subpermissions>,
1347 except that the innermost hash value is the description
1348 of the subpermission.
1352 sub get_all_subpermissions {
1353 my $dbh = C4::Context->dbh;
1354 my $sth = $dbh->prepare("SELECT flag, code, description
1356 JOIN userflags ON (module_bit = bit)");
1360 while (my $perm = $sth->fetchrow_hashref) {
1361 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1368 $flags = ($dbh,$member,$flagsrequired);
1370 C<$member> may be either userid or overloaded with $borrower hashref from GetMemberDetails.
1371 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1373 Returns member's flags or 0 if a permission is not met.
1378 my ( $dbh, $userid, $flagsrequired ) = @_;
1379 my ($flags,$intflags);
1380 $dbh=C4::Context->dbh unless($dbh);
1382 $intflags = $userid->{'flags'};
1384 my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1385 $sth->execute($userid);
1386 my ($intflags) = $sth->fetchrow;
1387 $flags = getuserflags( $intflags, $userid, $dbh );
1389 if ( $userid eq C4::Context->config('user') ) {
1390 # Super User Account from /etc/koha.conf
1391 $flags->{'superlibrarian'} = 1;
1393 if ( $userid eq 'demo' && C4::Context->config('demo') ) {
1394 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1395 $flags->{'superlibrarian'} = 1;
1397 return $flags if $flags->{superlibrarian};
1398 foreach my $module ( keys %$flagsrequired ) {
1399 if (C4::Context->preference('GranularPermissions')) {
1400 my $subperm = $flagsrequired->{$module};
1401 if ($subperm eq '*') {
1402 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1404 return 0 unless ( $flags->{$module} == 1 or
1405 ( ref($flags->{$module}) and
1406 exists $flags->{$module}->{$subperm} and
1407 $flags->{$module}->{$subperm} == 1
1412 return 0 unless ( $flags->{$module} );
1416 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1420 sub getborrowernumber {
1422 my $dbh = C4::Context->dbh;
1423 for my $field ( 'userid', 'cardnumber' ) {
1425 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1426 $sth->execute($userid);
1428 my ($bnumber) = $sth->fetchrow;
1435 END { } # module clean-up code here (global destructor)