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 if ( $in->{'type'} eq "intranet" ) {
252 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
253 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
254 IntranetNav => C4::Context->preference("IntranetNav"),
255 intranetuserjs => C4::Context->preference("intranetuserjs"),
256 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
257 AmazonContent => C4::Context->preference("AmazonContent"),
258 LibraryName => C4::Context->preference("LibraryName"),
259 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
260 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
261 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
262 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
263 AutoLocation => C4::Context->preference("AutoLocation"),
264 hide_marc => C4::Context->preference("hide_marc"),
265 patronimages => C4::Context->preference("patronimages"),
266 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
267 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
268 suggestion => C4::Context->preference("suggestion"),
269 virtualshelves => C4::Context->preference("virtualshelves"),
270 LibraryName => C4::Context->preference("LibraryName"),
271 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
272 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
273 IndependantBranches => C4::Context->preference("IndependantBranches"),
274 CircAutocompl => C4::Context->preference("CircAutocompl"),
275 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
276 AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
277 'item-level_itypes' => C4::Context->preference('item-level_itypes'),
278 canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
279 intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
280 noItemTypeImages => C4::Context->preference("noItemTypeImages"),
281 singleBranchMode => C4::Context->preference("singleBranchMode"),
282 TagsEnabled => C4::Context->preference("TagsEnabled"),
283 GoogleJackets => C4::Context->preference("GoogleJackets"),
284 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
288 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
289 my $LibraryNameTitle = C4::Context->preference("LibraryName");
290 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
291 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
293 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
294 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
295 suggestion => "" . C4::Context->preference("suggestion"),
296 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
297 virtualshelves => "" . C4::Context->preference("virtualshelves"),
298 OpacNav => "" . C4::Context->preference("OpacNav"),
299 opacheader => "" . C4::Context->preference("opacheader"),
300 opaccredits => "" . C4::Context->preference("opaccredits"),
301 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
302 opaclargeimage => "" . C4::Context->preference("opaclargeimage"),
303 opaclayoutstylesheet => "". C4::Context->preference("opaclayoutstylesheet"),
304 opaccolorstylesheet => "". C4::Context->preference("opaccolorstylesheet"),
305 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
306 opaclanguagesdisplay => "". C4::Context->preference("opaclanguagesdisplay"),
307 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
308 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
309 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
310 opacbookbag => "" . C4::Context->preference("opacbookbag"),
311 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
312 AmazonContent => "" . C4::Context->preference("AmazonContent"),
313 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
314 OPACAmazonSimilarItems => "" . C4::Context->preference("OPACAmazonSimilarItems"),
315 LibraryName => "" . C4::Context->preference("LibraryName"),
316 LibraryNameTitle => "" . $LibraryNameTitle,
317 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
318 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
319 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
320 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
321 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
322 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
323 opacuserjs => C4::Context->preference("opacuserjs"),
324 OpacCloud => C4::Context->preference("OpacCloud"),
325 OpacTopissue => C4::Context->preference("OpacTopissue"),
326 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
327 OpacBrowser => C4::Context->preference("OpacBrowser"),
328 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
329 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
330 reviewson => C4::Context->preference("reviewson"),
331 hide_marc => C4::Context->preference("hide_marc"),
332 patronimages => C4::Context->preference("patronimages"),
333 hidelostitems => C4::Context->preference("hidelostitems"),
334 mylibraryfirst => C4::Context->preference("SearchMyLibraryFirst"),
335 "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
336 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
337 'item-level_itypes' => C4::Context->preference('item-level_itypes'),
338 'Version' => C4::Context->preference('Version'),
339 singleBranchMode => C4::Context->preference("singleBranchMode"),
340 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
341 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
342 TagsEnabled => C4::Context->preference("TagsEnabled"),
343 GoogleJackets => C4::Context->preference("GoogleJackets"),
344 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
345 OPACBaseURL=> ($in->{'query'}->https() ? "https://" : "http://") .
346 $ENV{'SERVER_NAME'} .
347 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
350 $template->param(listloop=>[{shelfname=>"Freelist", shelfnumber=>110}]);
351 return ( $template, $borrowernumber, $cookie, $flags);
356 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
358 Verifies that the user is authorized to run this script. If
359 the user is authorized, a (userid, cookie, session-id, flags)
360 quadruple is returned. If the user is not authorized but does
361 not have the required privilege (see $flagsrequired below), it
362 displays an error page and exits. Otherwise, it displays the
363 login page and exits.
365 Note that C<&checkauth> will return if and only if the user
366 is authorized, so it should be called early on, before any
367 unfinished operations (e.g., if you've opened a file, then
368 C<&checkauth> won't close it for you).
370 C<$query> is the CGI object for the script calling C<&checkauth>.
372 The C<$noauth> argument is optional. If it is set, then no
373 authorization is required for the script.
375 C<&checkauth> fetches user and session information from C<$query> and
376 ensures that the user is authorized to run scripts that require
379 The C<$flagsrequired> argument specifies the required privileges
380 the user must have if the username and password are correct.
381 It should be specified as a reference-to-hash; keys in the hash
382 should be the "flags" for the user, as specified in the Members
383 intranet module. Any key specified must correspond to a "flag"
384 in the userflags table. E.g., { circulate => 1 } would specify
385 that the user must have the "circulate" privilege in order to
386 proceed. To make sure that access control is correct, the
387 C<$flagsrequired> parameter must be specified correctly.
389 If the GranularPermissions system preference is ON, the
390 value of each key in the C<flagsrequired> hash takes on an additional
395 The user must have access to all subfunctions of the module
396 specified by the hash key.
400 The user must have access to at least one subfunction of the module
401 specified by the hash key.
403 =item specific permission, e.g., 'export_catalog'
405 The user must have access to the specific subfunction list, which
406 must correspond to a row in the permissions table.
408 The C<$type> argument specifies whether the template should be
409 retrieved from the opac or intranet directory tree. "opac" is
410 assumed if it is not specified; however, if C<$type> is specified,
411 "intranet" is assumed if it is not "opac".
413 If C<$query> does not have a valid session ID associated with it
414 (i.e., the user has not logged in) or if the session has expired,
415 C<&checkauth> presents the user with a login page (from the point of
416 view of the original script, C<&checkauth> does not return). Once the
417 user has authenticated, C<&checkauth> restarts the original script
418 (this time, C<&checkauth> returns).
420 The login page is provided using a HTML::Template, which is set in the
421 systempreferences table or at the top of this file. The variable C<$type>
422 selects which template to use, either the opac or the intranet
423 authentification template.
425 C<&checkauth> returns a user ID, a cookie, and a session ID. The
426 cookie should be sent back to the browser; it verifies that the user
431 sub _version_check ($$) {
435 # If Version syspref is unavailable, it means Koha is beeing installed,
436 # and so we must redirect to OPAC maintenance page or to the WebInstaller
437 #warn "about to check version";
438 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
439 if ($type ne 'opac') {
440 warn "Install required, redirecting to Installer";
441 print $query->redirect("/cgi-bin/koha/installer/install.pl");
444 warn "OPAC Install required, redirecting to maintenance";
445 print $query->redirect("/cgi-bin/koha/maintenance.pl");
450 # check that database and koha version are the same
451 # there is no DB version, it's a fresh install,
452 # go to web installer
453 # there is a DB version, compare it to the code version
454 my $kohaversion=C4::Context::KOHAVERSION;
455 # remove the 3 last . to have a Perl number
456 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
457 $debug and print STDERR "kohaversion : $kohaversion\n";
458 if ($version < $kohaversion){
459 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
460 if ($type ne 'opac'){
461 warn sprintf($warning, 'Installer');
462 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
464 warn sprintf("OPAC: " . $warning, 'maintenance');
465 print $query->redirect("/cgi-bin/koha/maintenance.pl");
473 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
474 printf L join("\n",@_);
480 $debug and warn "Checking Auth";
481 # $authnotrequired will be set for scripts which will run without authentication
482 my $authnotrequired = shift;
483 my $flagsrequired = shift;
485 $type = 'opac' unless $type;
487 my $dbh = C4::Context->dbh;
488 my $timeout = C4::Context->preference('timeout');
490 if ($timeout =~ /(\d+)[dD]/) {
491 $timeout = $1 * 86400;
493 $timeout = 600 unless $timeout;
495 _version_check($type,$query);
499 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
500 my $logout = $query->param('logout.x');
502 if ( $userid = $ENV{'REMOTE_USER'} ) {
503 # Using Basic Authentication, no cookies required
504 $cookie = $query->cookie(
505 -name => 'CGISESSID',
511 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
512 my $session = get_session($sessionID);
513 C4::Context->_new_userenv($sessionID);
514 my ($ip, $lasttime, $sessiontype);
516 C4::Context::set_userenv(
517 $session->param('number'), $session->param('id'),
518 $session->param('cardnumber'), $session->param('firstname'),
519 $session->param('surname'), $session->param('branch'),
520 $session->param('branchname'), $session->param('flags'),
521 $session->param('emailaddress'), $session->param('branchprinter')
523 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
524 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
525 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
526 $ip = $session->param('ip');
527 $lasttime = $session->param('lasttime');
528 $userid = $session->param('id');
529 $sessiontype = $session->param('sessiontype');
532 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
533 #if a user enters an id ne to the id in the current session, we need to log them in...
534 #first we need to clear the anonymous session...
535 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
538 C4::Context->_unset_userenv($sessionID);
543 # voluntary logout the user
546 C4::Context->_unset_userenv($sessionID);
547 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,localtime);
551 elsif ( $lasttime < time() - $timeout ) {
553 $info{'timed_out'} = 1;
555 C4::Context->_unset_userenv($sessionID);
556 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,localtime);
560 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
561 # Different ip than originally logged in from
562 $info{'oldip'} = $ip;
563 $info{'newip'} = $ENV{'REMOTE_ADDR'};
564 $info{'different_ip'} = 1;
566 C4::Context->_unset_userenv($sessionID);
567 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,localtime, $info{'newip'});
572 $cookie = $query->cookie( CGISESSID => $session->id );
573 $session->param('lasttime',time());
574 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...
575 $flags = haspermission( $dbh, $userid, $flagsrequired );
579 $info{'nopermission'} = 1;
584 unless ($userid || $sessionID) {
585 #we initiate a session prior to checking for a username to allow for anonymous sessions...
586 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
587 my $sessionID = $session->id;
588 C4::Context->_new_userenv($sessionID);
589 $cookie = $query->cookie(CGISESSID => $sessionID);
590 if ( $userid = $query->param('userid') ) {
591 my $password = $query->param('password');
592 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
594 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},localtime);
595 if ( $flags = haspermission( $dbh, $userid, $flagsrequired ) ) {
599 $info{'nopermission'} = 1;
600 C4::Context->_unset_userenv($sessionID);
603 my ($borrowernumber, $firstname, $surname, $userflags,
604 $branchcode, $branchname, $branchprinter, $emailaddress);
606 if ( $return == 1 ) {
608 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
609 branches.branchname as branchname,
610 branches.branchprinter as branchprinter,
613 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
615 my $sth = $dbh->prepare("$select where userid=?");
616 $sth->execute($userid);
617 unless ($sth->rows) {
618 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
619 $sth = $dbh->prepare("$select where cardnumber=?");
620 $sth->execute($cardnumber);
621 unless ($sth->rows) {
622 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
623 $sth->execute($userid);
624 unless ($sth->rows) {
625 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
630 ($borrowernumber, $firstname, $surname, $userflags,
631 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
632 $debug and print STDERR "AUTH_3 results: " .
633 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
635 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
638 # launch a sequence to check if we have a ip for the branch, i
639 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
641 my $ip = $ENV{'REMOTE_ADDR'};
642 # if they specify at login, use that
643 if ($query->param('branch')) {
644 $branchcode = $query->param('branch');
645 $branchname = GetBranchName($branchcode);
647 my $branches = GetBranches();
648 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
649 # we have to check they are coming from the right ip range
650 my $domain = $branches->{$branchcode}->{'branchip'};
651 if ($ip !~ /^$domain/){
653 $info{'wrongip'} = 1;
658 foreach my $br ( keys %$branches ) {
659 # now we work with the treatment of ip
660 my $domain = $branches->{$br}->{'branchip'};
661 if ( $domain && $ip =~ /^$domain/ ) {
662 $branchcode = $branches->{$br}->{'branchcode'};
664 # new op dev : add the branchprinter and branchname in the cookie
665 $branchprinter = $branches->{$br}->{'branchprinter'};
666 $branchname = $branches->{$br}->{'branchname'};
669 $session->param('number',$borrowernumber);
670 $session->param('id',$userid);
671 $session->param('cardnumber',$cardnumber);
672 $session->param('firstname',$firstname);
673 $session->param('surname',$surname);
674 $session->param('branch',$branchcode);
675 $session->param('branchname',$branchname);
676 $session->param('flags',$userflags);
677 $session->param('emailaddress',$emailaddress);
678 $session->param('ip',$session->remote_addr());
679 $session->param('lasttime',time());
680 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
682 elsif ( $return == 2 ) {
683 #We suppose the user is the superlibrarian
685 $session->param('number',0);
686 $session->param('id',C4::Context->config('user'));
687 $session->param('cardnumber',C4::Context->config('user'));
688 $session->param('firstname',C4::Context->config('user'));
689 $session->param('surname',C4::Context->config('user'));
690 $session->param('branch','NO_LIBRARY_SET');
691 $session->param('branchname','NO_LIBRARY_SET');
692 $session->param('flags',1);
693 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
694 $session->param('ip',$session->remote_addr());
695 $session->param('lasttime',time());
697 C4::Context::set_userenv(
698 $session->param('number'), $session->param('id'),
699 $session->param('cardnumber'), $session->param('firstname'),
700 $session->param('surname'), $session->param('branch'),
701 $session->param('branchname'), $session->param('flags'),
702 $session->param('emailaddress'), $session->param('branchprinter')
705 # Grab borrower's shelves and add to the session...
706 $barshelves = GetShelvesSummary($borrowernumber,2,10);
707 $session->param('barshelves', $barshelves);
708 C4::Context::set_shelves_userenv('bar',$barshelves);
710 # Grab the public shelves and add to the session...
711 $pubshelves = GetShelvesSummary(0,2,10);
712 $session->param('pubshelves', $pubshelves);
713 C4::Context::set_shelves_userenv('pub',$pubshelves);
717 $info{'invalid_username_or_password'} = 1;
718 C4::Context->_unset_userenv($sessionID);
721 } # END if ( $userid = $query->param('userid') )
722 elsif ($type eq "opac") {
723 # if we are here this is an anonymous session; add public lists to it and a few other items...
724 # anonymous sessions are created only for the OPAC
725 $debug and warn "Initiating an anonymous session...";
727 # Grab the public shelves and add to the session...
728 $pubshelves = GetShelvesSummary(0,2,10);
729 $session->param('pubshelves', $pubshelves);
730 C4::Context::set_shelves_userenv('pub',$pubshelves);
732 # setting a couple of other session vars...
733 $session->param('ip',$session->remote_addr());
734 $session->param('lasttime',time());
735 $session->param('sessiontype','anon');
737 } # END unless ($userid)
738 my $insecure = C4::Context->boolean_preference('insecure');
740 # finished authentification, now respond
741 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
745 $cookie = $query->cookie( CGISESSID => '' );
747 return ( $userid, $cookie, $sessionID, $flags );
752 # AUTH rejected, show the login/password template, after checking the DB.
756 # get the inputs from the incoming query
758 foreach my $name ( param $query) {
759 (next) if ( $name eq 'userid' || $name eq 'password' );
760 my $value = $query->param($name);
761 push @inputs, { name => $name, value => $value };
763 # get the branchloop, which we need for authentication
764 my $branches = GetBranches();
766 for my $branch_hash (sort keys %$branches) {
767 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
770 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
771 my $template = gettemplate( $template_name, $type, $query );
772 $template->param(branchloop => \@branch_loop,);
776 suggestion => C4::Context->preference("suggestion"),
777 virtualshelves => C4::Context->preference("virtualshelves"),
778 opaclargeimage => C4::Context->preference("opaclargeimage"),
779 LibraryName => C4::Context->preference("LibraryName"),
780 opacuserlogin => C4::Context->preference("opacuserlogin"),
781 OpacNav => C4::Context->preference("OpacNav"),
782 opaccredits => C4::Context->preference("opaccredits"),
783 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
784 opacsmallimage => C4::Context->preference("opacsmallimage"),
785 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
786 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
787 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
788 opacuserjs => C4::Context->preference("opacuserjs"),
789 opacbookbag => "" . C4::Context->preference("opacbookbag"),
790 OpacCloud => C4::Context->preference("OpacCloud"),
791 OpacTopissue => C4::Context->preference("OpacTopissue"),
792 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
793 OpacBrowser => C4::Context->preference("OpacBrowser"),
794 opacheader => C4::Context->preference("opacheader"),
795 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
796 intranetcolorstylesheet =>
797 C4::Context->preference("intranetcolorstylesheet"),
798 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
799 IntranetNav => C4::Context->preference("IntranetNav"),
800 intranetuserjs => C4::Context->preference("intranetuserjs"),
801 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
802 IndependantBranches=> C4::Context->preference("IndependantBranches"),
803 AutoLocation => C4::Context->preference("AutoLocation"),
804 wrongip => $info{'wrongip'}
807 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
809 my $self_url = $query->url( -absolute => 1 );
812 LibraryName => C4::Context->preference("LibraryName"),
814 $template->param( \%info );
815 # $cookie = $query->cookie(CGISESSID => $session->id
817 print $query->header(
818 -type => 'text/html',
828 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
830 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
831 cookie, determine if the user has the privileges specified by C<$userflags>.
833 C<check_api_auth> is is meant for authenticating users of web services, and
834 consequently will always return and will not attempt to redirect the user
837 If a valid session cookie is already present, check_api_auth will return a status
838 of "ok", the cookie, and the Koha session ID.
840 If no session cookie is present, check_api_auth will check the 'userid' and 'password
841 parameters and create a session cookie and Koha session if the supplied credentials
844 Possible return values in C<$status> are:
848 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
850 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
852 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
854 =item "expired -- session cookie has expired; API user should resubmit userid and password
862 my $flagsrequired = shift;
864 my $dbh = C4::Context->dbh;
865 my $timeout = C4::Context->preference('timeout');
866 $timeout = 600 unless $timeout;
868 unless (C4::Context->preference('Version')) {
869 # database has not been installed yet
870 return ("maintenance", undef, undef);
872 my $kohaversion=C4::Context::KOHAVERSION;
873 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
874 if (C4::Context->preference('Version') < $kohaversion) {
875 # database in need of version update; assume that
876 # no API should be called while databsae is in
878 return ("maintenance", undef, undef);
881 # FIXME -- most of what follows is a copy-and-paste
882 # of code from checkauth. There is an obvious need
883 # for refactoring to separate the various parts of
884 # the authentication code, but as of 2007-11-19 this
885 # is deferred so as to not introduce bugs into the
886 # regular authentication code for Koha 3.0.
888 # see if we have a valid session cookie already
889 # however, if a userid parameter is present (i.e., from
890 # a form submission, assume that any current cookie
892 my $sessionID = undef;
893 unless ($query->param('userid')) {
894 $sessionID = $query->cookie("CGISESSID");
897 my $session = get_session($sessionID);
898 C4::Context->_new_userenv($sessionID);
900 C4::Context::set_userenv(
901 $session->param('number'), $session->param('id'),
902 $session->param('cardnumber'), $session->param('firstname'),
903 $session->param('surname'), $session->param('branch'),
904 $session->param('branchname'), $session->param('flags'),
905 $session->param('emailaddress'), $session->param('branchprinter')
908 my $ip = $session->param('ip');
909 my $lasttime = $session->param('lasttime');
910 my $userid = $session->param('id');
911 if ( $lasttime < time() - $timeout ) {
914 C4::Context->_unset_userenv($sessionID);
917 return ("expired", undef, undef);
918 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
921 C4::Context->_unset_userenv($sessionID);
924 return ("expired", undef, undef);
926 my $cookie = $query->cookie( CGISESSID => $session->id );
927 $session->param('lasttime',time());
928 my $flags = haspermission( $dbh, $userid, $flagsrequired );
930 return ("ok", $cookie, $sessionID);
933 C4::Context->_unset_userenv($sessionID);
936 return ("failed", undef, undef);
940 return ("expired", undef, undef);
944 my $userid = $query->param('userid');
945 my $password = $query->param('password');
946 unless ($userid and $password) {
947 # caller did something wrong, fail the authenticateion
948 return ("failed", undef, undef);
950 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
951 if ($return and haspermission( $dbh, $userid, $flagsrequired)) {
952 my $session = get_session("");
953 return ("failed", undef, undef) unless $session;
955 my $sessionID = $session->id;
956 C4::Context->_new_userenv($sessionID);
957 my $cookie = $query->cookie(CGISESSID => $sessionID);
958 if ( $return == 1 ) {
960 $borrowernumber, $firstname, $surname,
961 $userflags, $branchcode, $branchname,
962 $branchprinter, $emailaddress
966 "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=?"
968 $sth->execute($userid);
970 $borrowernumber, $firstname, $surname,
971 $userflags, $branchcode, $branchname,
972 $branchprinter, $emailaddress
973 ) = $sth->fetchrow if ( $sth->rows );
975 unless ($sth->rows ) {
976 my $sth = $dbh->prepare(
977 "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=?"
979 $sth->execute($cardnumber);
981 $borrowernumber, $firstname, $surname,
982 $userflags, $branchcode, $branchname,
983 $branchprinter, $emailaddress
984 ) = $sth->fetchrow if ( $sth->rows );
986 unless ( $sth->rows ) {
987 $sth->execute($userid);
989 $borrowernumber, $firstname, $surname, $userflags,
990 $branchcode, $branchname, $branchprinter, $emailaddress
991 ) = $sth->fetchrow if ( $sth->rows );
995 my $ip = $ENV{'REMOTE_ADDR'};
996 # if they specify at login, use that
997 if ($query->param('branch')) {
998 $branchcode = $query->param('branch');
999 $branchname = GetBranchName($branchcode);
1001 my $branches = GetBranches();
1003 foreach my $br ( keys %$branches ) {
1004 # now we work with the treatment of ip
1005 my $domain = $branches->{$br}->{'branchip'};
1006 if ( $domain && $ip =~ /^$domain/ ) {
1007 $branchcode = $branches->{$br}->{'branchcode'};
1009 # new op dev : add the branchprinter and branchname in the cookie
1010 $branchprinter = $branches->{$br}->{'branchprinter'};
1011 $branchname = $branches->{$br}->{'branchname'};
1014 $session->param('number',$borrowernumber);
1015 $session->param('id',$userid);
1016 $session->param('cardnumber',$cardnumber);
1017 $session->param('firstname',$firstname);
1018 $session->param('surname',$surname);
1019 $session->param('branch',$branchcode);
1020 $session->param('branchname',$branchname);
1021 $session->param('flags',$userflags);
1022 $session->param('emailaddress',$emailaddress);
1023 $session->param('ip',$session->remote_addr());
1024 $session->param('lasttime',time());
1025 } elsif ( $return == 2 ) {
1026 #We suppose the user is the superlibrarian
1027 $session->param('number',0);
1028 $session->param('id',C4::Context->config('user'));
1029 $session->param('cardnumber',C4::Context->config('user'));
1030 $session->param('firstname',C4::Context->config('user'));
1031 $session->param('surname',C4::Context->config('user'));
1032 $session->param('branch','NO_LIBRARY_SET');
1033 $session->param('branchname','NO_LIBRARY_SET');
1034 $session->param('flags',1);
1035 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1036 $session->param('ip',$session->remote_addr());
1037 $session->param('lasttime',time());
1039 C4::Context::set_userenv(
1040 $session->param('number'), $session->param('id'),
1041 $session->param('cardnumber'), $session->param('firstname'),
1042 $session->param('surname'), $session->param('branch'),
1043 $session->param('branchname'), $session->param('flags'),
1044 $session->param('emailaddress'), $session->param('branchprinter')
1046 return ("ok", $cookie, $sessionID);
1048 return ("failed", undef, undef);
1053 =item check_cookie_auth
1055 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1057 Given a CGISESSID cookie set during a previous login to Koha, determine
1058 if the user has the privileges specified by C<$userflags>.
1060 C<check_cookie_auth> is meant for authenticating special services
1061 such as tools/upload-file.pl that are invoked by other pages that
1062 have been authenticated in the usual way.
1064 Possible return values in C<$status> are:
1068 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1070 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1072 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1074 =item "expired -- session cookie has expired; API user should resubmit userid and password
1080 sub check_cookie_auth {
1082 my $flagsrequired = shift;
1084 my $dbh = C4::Context->dbh;
1085 my $timeout = C4::Context->preference('timeout');
1086 $timeout = 600 unless $timeout;
1088 unless (C4::Context->preference('Version')) {
1089 # database has not been installed yet
1090 return ("maintenance", undef);
1092 my $kohaversion=C4::Context::KOHAVERSION;
1093 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1094 if (C4::Context->preference('Version') < $kohaversion) {
1095 # database in need of version update; assume that
1096 # no API should be called while databsae is in
1098 return ("maintenance", undef);
1101 # FIXME -- most of what follows is a copy-and-paste
1102 # of code from checkauth. There is an obvious need
1103 # for refactoring to separate the various parts of
1104 # the authentication code, but as of 2007-11-23 this
1105 # is deferred so as to not introduce bugs into the
1106 # regular authentication code for Koha 3.0.
1108 # see if we have a valid session cookie already
1109 # however, if a userid parameter is present (i.e., from
1110 # a form submission, assume that any current cookie
1112 unless (defined $cookie and $cookie) {
1113 return ("failed", undef);
1115 my $sessionID = $cookie;
1116 my $session = get_session($sessionID);
1117 C4::Context->_new_userenv($sessionID);
1119 C4::Context::set_userenv(
1120 $session->param('number'), $session->param('id'),
1121 $session->param('cardnumber'), $session->param('firstname'),
1122 $session->param('surname'), $session->param('branch'),
1123 $session->param('branchname'), $session->param('flags'),
1124 $session->param('emailaddress'), $session->param('branchprinter')
1127 my $ip = $session->param('ip');
1128 my $lasttime = $session->param('lasttime');
1129 my $userid = $session->param('id');
1130 if ( $lasttime < time() - $timeout ) {
1133 C4::Context->_unset_userenv($sessionID);
1136 return ("expired", undef);
1137 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1138 # IP address changed
1140 C4::Context->_unset_userenv($sessionID);
1143 return ("expired", undef);
1145 $session->param('lasttime',time());
1146 my $flags = haspermission( $dbh, $userid, $flagsrequired );
1148 return ("ok", $sessionID);
1151 C4::Context->_unset_userenv($sessionID);
1154 return ("failed", undef);
1158 return ("expired", undef);
1165 my $session = get_session($sessionID);
1167 Given a session ID, retrieve the CGI::Session object used to store
1168 the session's state. The session object can be used to store
1169 data that needs to be accessed by different scripts during a
1172 If the C<$sessionID> parameter is an empty string, a new session
1178 my $sessionID = shift;
1179 my $storage_method = C4::Context->preference('SessionStorage');
1180 my $dbh = C4::Context->dbh;
1182 if ($storage_method eq 'mysql'){
1183 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1185 elsif ($storage_method eq 'Pg') {
1186 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1189 # catch all defaults to tmp should work on all systems
1190 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1197 my ( $dbh, $userid, $password ) = @_;
1199 $debug and print "## checkpw - checking LDAP\n";
1200 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1201 ($retval) and return ($retval,$retcard);
1207 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1209 $sth->execute($userid);
1211 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1212 $surname, $branchcode, $flags )
1214 if ( md5_base64($password) eq $md5password ) {
1216 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1217 $firstname, $surname, $branchcode, $flags );
1218 return 1, $cardnumber;
1223 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1225 $sth->execute($userid);
1227 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1228 $surname, $branchcode, $flags )
1230 if ( md5_base64($password) eq $md5password ) {
1232 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1233 $firstname, $surname, $branchcode, $flags );
1237 if ( $userid && $userid eq C4::Context->config('user')
1238 && "$password" eq C4::Context->config('pass') )
1241 # Koha superuser account
1242 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1245 if ( $userid && $userid eq 'demo'
1246 && "$password" eq 'demo'
1247 && C4::Context->config('demo') )
1250 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1251 # some features won't be effective : modify systempref, modify MARC structure,
1259 $authflags = getuserflags($flags,$dbh);
1260 Translates integer flags into permissions strings hash.
1262 C<$flags> is the integer userflags value ( borrowers.userflags )
1263 C<$authflags> is a hashref of permissions
1272 $flags = 0 unless $flags;
1273 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1276 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1277 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1278 $userflags->{$flag} = 1;
1281 $userflags->{$flag} = 0;
1285 # get subpermissions and merge with top-level permissions
1286 my $user_subperms = get_user_subpermissions($userid);
1287 foreach my $module (keys %$user_subperms) {
1288 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1289 $userflags->{$module} = $user_subperms->{$module};
1295 =item get_user_subpermissions
1299 my $user_perm_hashref = get_user_subpermissions($userid);
1303 Given the userid (note, not the borrowernumber) of a staff user,
1304 return a hashref of hashrefs of the specific subpermissions
1305 accorded to the user. An example return is
1309 export_catalog => 1,
1310 import_patrons => 1,
1314 The top-level hash-key is a module or function code from
1315 userflags.flag, while the second-level key is a code
1318 The results of this function do not give a complete picture
1319 of the functions that a staff user can access; it is also
1320 necessary to check borrowers.flags.
1324 sub get_user_subpermissions {
1327 my $dbh = C4::Context->dbh;
1328 my $sth = $dbh->prepare("SELECT flag, code
1329 FROM user_permissions
1330 JOIN permissions USING (module_bit, code)
1331 JOIN userflags ON (module_bit = bit)
1332 JOIN borrowers USING (borrowernumber)
1334 $sth->execute($userid);
1336 my $user_perms = {};
1337 while (my $perm = $sth->fetchrow_hashref) {
1338 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1343 =item get_all_subpermissions
1347 my $perm_hashref = get_all_subpermissions();
1351 Returns a hashref of hashrefs defining all specific
1352 permissions currently defined. The return value
1353 has the same structure as that of C<get_user_subpermissions>,
1354 except that the innermost hash value is the description
1355 of the subpermission.
1359 sub get_all_subpermissions {
1360 my $dbh = C4::Context->dbh;
1361 my $sth = $dbh->prepare("SELECT flag, code, description
1363 JOIN userflags ON (module_bit = bit)");
1367 while (my $perm = $sth->fetchrow_hashref) {
1368 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1375 $flags = ($dbh,$member,$flagsrequired);
1377 C<$member> may be either userid or overloaded with $borrower hashref from GetMemberDetails.
1378 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1380 Returns member's flags or 0 if a permission is not met.
1385 my ( $dbh, $userid, $flagsrequired ) = @_;
1386 my ($flags,$intflags);
1387 $dbh=C4::Context->dbh unless($dbh);
1389 $intflags = $userid->{'flags'};
1391 my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1392 $sth->execute($userid);
1393 my ($intflags) = $sth->fetchrow;
1394 $flags = getuserflags( $intflags, $userid, $dbh );
1396 if ( $userid eq C4::Context->config('user') ) {
1397 # Super User Account from /etc/koha.conf
1398 $flags->{'superlibrarian'} = 1;
1400 if ( $userid eq 'demo' && C4::Context->config('demo') ) {
1401 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1402 $flags->{'superlibrarian'} = 1;
1404 return $flags if $flags->{superlibrarian};
1405 foreach my $module ( keys %$flagsrequired ) {
1406 if (C4::Context->preference('GranularPermissions')) {
1407 my $subperm = $flagsrequired->{$module};
1408 if ($subperm eq '*') {
1409 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1411 return 0 unless ( $flags->{$module} == 1 or
1412 ( ref($flags->{$module}) and
1413 exists $flags->{$module}->{$subperm} and
1414 $flags->{$module}->{$subperm} == 1
1419 return 0 unless ( $flags->{$module} );
1423 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1427 sub getborrowernumber {
1429 my $dbh = C4::Context->dbh;
1430 for my $field ( 'userid', 'cardnumber' ) {
1432 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1433 $sth->execute($userid);
1435 my ($bnumber) = $sth->fetchrow;
1442 END { } # module clean-up code here (global destructor)