3 # Copyright 2000-2002 Katipo Communications
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA 02111-1307 USA
21 use Digest::MD5 qw(md5_base64);
26 use C4::Output; # to get the template
29 use C4::Branch; # GetBranches
30 use C4::VirtualShelves;
33 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap);
36 $VERSION = 3.02; # set version for version checking
37 $debug = $ENV{DEBUG} || 0 ;
39 @EXPORT = qw(&checkauth &get_template_and_user);
40 @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &get_all_subpermissions &get_user_subpermissions);
41 %EXPORT_TAGS = (EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)]);
42 $ldap = C4::Context->config('useldapserver') || 0;
44 require C4::Auth_with_ldap; # no import
45 import C4::Auth_with_ldap qw(checkpw_ldap);
51 C4::Auth - Authenticates Koha users
61 my ($template, $borrowernumber, $cookie)
62 = get_template_and_user(
64 template_name => "opac-main.tmpl",
68 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
72 output_html_with_http_headers $query, $cookie, $template->output;
76 The main function of this module is to provide
77 authentification. However the get_template_and_user function has
78 been provided so that a users login information is passed along
79 automatically. This gets loaded into the template.
85 =item get_template_and_user
87 my ($template, $borrowernumber, $cookie)
88 = get_template_and_user(
90 template_name => "opac-main.tmpl",
94 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
98 This call passes the C<query>, C<flagsrequired> and C<authnotrequired>
99 to C<&checkauth> (in this module) to perform authentification.
100 See C<&checkauth> for an explanation of these parameters.
102 The C<template_name> is then used to find the correct template for
103 the page. The authenticated users details are loaded onto the
104 template in the HTML::Template LOOP variable C<USER_INFO>. Also the
105 C<sessionID> is passed to the template. This can be used in templates
106 if cookies are disabled. It needs to be put as and input to every
109 More information on the C<gettemplate> sub can be found in the
114 sub get_template_and_user {
117 gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'} );
118 my ( $user, $cookie, $sessionID, $flags ) = checkauth(
120 $in->{'authnotrequired'},
121 $in->{'flagsrequired'},
123 ) unless ($in->{'template_name'}=~/maintenance/);
126 my $insecure = C4::Context->preference('insecure');
127 if ($user or $insecure) {
129 # load the template variables for stylesheets and JavaScript
130 $template->param( css_libs => $in->{'css_libs'} );
131 $template->param( css_module => $in->{'css_module'} );
132 $template->param( css_page => $in->{'css_page'} );
133 $template->param( css_widgets => $in->{'css_widgets'} );
135 $template->param( js_libs => $in->{'js_libs'} );
136 $template->param( js_module => $in->{'js_module'} );
137 $template->param( js_page => $in->{'js_page'} );
138 $template->param( js_widgets => $in->{'js_widgets'} );
141 $template->param( loggedinusername => $user );
142 $template->param( sessionID => $sessionID );
144 my ($total, $pubshelves, $barshelves) = C4::Context->get_shelves_userenv();
145 if (defined($pubshelves)) {
146 $template->param( pubshelves => scalar (@$pubshelves),
147 pubshelvesloop => $pubshelves,
149 $template->param( pubtotal => $total->{'pubtotal'}, ) if ($total->{'pubtotal'} > scalar (@$pubshelves));
151 if (defined($barshelves)) {
152 $template->param( barshelves => scalar (@$barshelves),
153 barshelvesloop => $barshelves,
155 $template->param( bartotal => $total->{'bartotal'}, ) if ($total->{'bartotal'} > scalar (@$barshelves));
158 $borrowernumber = getborrowernumber($user);
159 my ( $borr ) = GetMemberDetails( $borrowernumber );
162 $template->param( "USER_INFO" => \@bordat );
164 my $all_perms = get_all_subpermissions();
166 my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow
167 editcatalogue updatecharges management tools editauthorities serials reports);
168 # We are going to use the $flags returned by checkauth
169 # to create the template's parameters that will indicate
170 # which menus the user can access.
171 if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) {
172 $template->param( CAN_user_circulate => 1 );
173 $template->param( CAN_user_catalogue => 1 );
174 $template->param( CAN_user_parameters => 1 );
175 $template->param( CAN_user_borrowers => 1 );
176 $template->param( CAN_user_permissions => 1 );
177 $template->param( CAN_user_reserveforothers => 1 );
178 $template->param( CAN_user_borrow => 1 );
179 $template->param( CAN_user_editcatalogue => 1 );
180 $template->param( CAN_user_updatecharges => 1 );
181 $template->param( CAN_user_acquisition => 1 );
182 $template->param( CAN_user_management => 1 );
183 $template->param( CAN_user_tools => 1 );
184 $template->param( CAN_user_editauthorities => 1 );
185 $template->param( CAN_user_serials => 1 );
186 $template->param( CAN_user_reports => 1 );
187 $template->param( CAN_user_staffaccess => 1 );
188 foreach my $module (keys %$all_perms) {
189 foreach my $subperm (keys %{ $all_perms->{$module} }) {
190 $template->param( "CAN_user_${module}_${subperm}" => 1 );
195 if (C4::Context->preference('GranularPermissions')) {
197 foreach my $module (keys %$all_perms) {
198 if ( $flags->{$module} == 1) {
199 foreach my $subperm (keys %{ $all_perms->{$module} }) {
200 $template->param( "CAN_user_${module}_${subperm}" => 1 );
202 } elsif ( ref($flags->{$module}) ) {
203 foreach my $subperm (keys %{ $flags->{$module} } ) {
204 $template->param( "CAN_user_${module}_${subperm}" => 1 );
210 foreach my $module (keys %$all_perms) {
211 foreach my $subperm (keys %{ $all_perms->{$module} }) {
212 $template->param( "CAN_user_${module}_${subperm}" => 1 );
218 foreach my $module (keys %$flags) {
219 if ( $flags->{$module} == 1 or ref($flags->{$module}) ) {
220 $template->param( "CAN_user_$module" => 1 );
221 if ($module eq "parameters") {
222 $template->param( CAN_user_management => 1 );
228 else { # if this is an anonymous session, setup to display public lists...
230 # load the template variables for stylesheets and JavaScript
231 $template->param( css_libs => $in->{'css_libs'} );
232 $template->param( css_module => $in->{'css_module'} );
233 $template->param( css_page => $in->{'css_page'} );
234 $template->param( css_widgets => $in->{'css_widgets'} );
236 $template->param( js_libs => $in->{'js_libs'} );
237 $template->param( js_module => $in->{'js_module'} );
238 $template->param( js_page => $in->{'js_page'} );
239 $template->param( js_widgets => $in->{'js_widgets'} );
241 $template->param( sessionID => $sessionID );
243 my ($total, $pubshelves) = C4::Context->get_shelves_userenv(); # an anonymous user has no 'barshelves'...
244 if (defined(($pubshelves))) {
245 $template->param( pubshelves => scalar (@$pubshelves),
246 pubshelvesloop => $pubshelves,
248 $template->param( pubtotal => $total->{'pubtotal'}, ) if ($total->{'pubtotal'} > scalar (@$pubshelves));
253 # these template parameters are set the same regardless of $in->{'type'}
255 "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
256 EnhancedMessagingPreferences => C4::Context->preference('EnhancedMessagingPreferences'),
257 GoogleJackets => C4::Context->preference("GoogleJackets"),
258 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
259 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
260 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
261 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
262 TagsEnabled => C4::Context->preference("TagsEnabled"),
263 hide_marc => C4::Context->preference("hide_marc"),
264 dateformat => C4::Context->preference("dateformat"),
265 'item-level_itypes' => C4::Context->preference('item-level_itypes'),
266 patronimages => C4::Context->preference("patronimages"),
267 singleBranchMode => C4::Context->preference("singleBranchMode"),
270 if ( $in->{'type'} eq "intranet" ) {
272 AmazonContent => C4::Context->preference("AmazonContent"),
273 AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
274 AutoLocation => C4::Context->preference("AutoLocation"),
275 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
276 CircAutocompl => C4::Context->preference("CircAutocompl"),
277 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
278 IndependantBranches => C4::Context->preference("IndependantBranches"),
279 IntranetNav => C4::Context->preference("IntranetNav"),
280 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
281 LibraryName => C4::Context->preference("LibraryName"),
282 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
283 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
284 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
285 canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
286 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
287 intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
288 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
289 intranetuserjs => C4::Context->preference("intranetuserjs"),
290 noItemTypeImages => C4::Context->preference("noItemTypeImages"),
291 suggestion => C4::Context->preference("suggestion"),
292 virtualshelves => C4::Context->preference("virtualshelves"),
296 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
297 my $LibraryNameTitle = C4::Context->preference("LibraryName");
298 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
299 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
301 OPACAmazonContent => "" . C4::Context->preference("OPACAmazonContent"),
302 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
303 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
304 LibraryName => "" . C4::Context->preference("LibraryName"),
305 LibraryNameTitle => "" . $LibraryNameTitle,
306 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
307 OPACAmazonSimilarItems => "" . C4::Context->preference("OPACAmazonSimilarItems"),
308 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
309 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
310 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
311 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
312 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
313 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
314 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
315 OPACBaseURL => ($in->{'query'}->https() ? "https://" : "http://") .
316 $ENV{'SERVER_NAME'} .
317 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
318 OpacBrowser => C4::Context->preference("OpacBrowser"),
319 OpacCloud => C4::Context->preference("OpacCloud"),
320 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
321 OpacNav => "" . C4::Context->preference("OpacNav"),
322 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
323 OpacTopissue => C4::Context->preference("OpacTopissue"),
324 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
325 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
326 'Version' => C4::Context->preference('Version'),
327 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
328 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
329 hidelostitems => C4::Context->preference("hidelostitems"),
330 mylibraryfirst => C4::Context->preference("SearchMyLibraryFirst"),
331 opacbookbag => "" . C4::Context->preference("opacbookbag"),
332 opaccolorstylesheet => "". C4::Context->preference("opaccolorstylesheet"),
333 opaccredits => "" . C4::Context->preference("opaccredits"),
334 opacheader => "" . C4::Context->preference("opacheader"),
335 opaclanguagesdisplay => "". C4::Context->preference("opaclanguagesdisplay"),
336 opaclayoutstylesheet => "". C4::Context->preference("opaclayoutstylesheet"),
337 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
338 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
339 opacuserjs => C4::Context->preference("opacuserjs"),
340 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
341 reviewson => C4::Context->preference("reviewson"),
342 suggestion => "" . C4::Context->preference("suggestion"),
343 virtualshelves => "" . C4::Context->preference("virtualshelves"),
346 return ( $template, $borrowernumber, $cookie, $flags);
351 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
353 Verifies that the user is authorized to run this script. If
354 the user is authorized, a (userid, cookie, session-id, flags)
355 quadruple is returned. If the user is not authorized due to
356 insufficent privileges (see $flagsrequired below), it
357 displays an error page and exits. Otherwise, it displays the
358 login page and exits.
360 Note that C<&checkauth> will return if and only if the user
361 is authorized, so it should be called early on, before any
362 unfinished operations (e.g., if you've opened a file, then
363 C<&checkauth> won't close it for you).
365 C<$query> is the CGI object for the script calling C<&checkauth>.
367 The C<$noauth> argument is optional. If it is set, then no
368 authorization is required for the script.
370 C<&checkauth> fetches user and session information from C<$query> and
371 ensures that the user is authorized to run scripts that require
374 The C<$flagsrequired> argument specifies the required privileges
375 the user must have if the username and password are correct.
376 It should be specified as a reference-to-hash; keys in the hash
377 should be the "flags" for the user, as specified in the Members
378 intranet module. Any key specified must correspond to a "flag"
379 in the userflags table. E.g., { circulate => 1 } would specify
380 that the user must have the "circulate" privilege in order to
381 proceed. To make sure that access control is correct, the
382 C<$flagsrequired> parameter must be specified correctly.
384 If the GranularPermissions system preference is ON, the
385 value of each key in the C<flagsrequired> hash takes on an additional
390 The user must have access to all subfunctions of the module
391 specified by the hash key.
395 The user must have access to at least one subfunction of the module
396 specified by the hash key.
398 =item specific permission, e.g., 'export_catalog'
400 The user must have access to the specific subfunction list, which
401 must correspond to a row in the permissions table.
403 The C<$type> argument specifies whether the template should be
404 retrieved from the opac or intranet directory tree. "opac" is
405 assumed if it is not specified; however, if C<$type> is specified,
406 "intranet" is assumed if it is not "opac".
408 If C<$query> does not have a valid session ID associated with it
409 (i.e., the user has not logged in) or if the session has expired,
410 C<&checkauth> presents the user with a login page (from the point of
411 view of the original script, C<&checkauth> does not return). Once the
412 user has authenticated, C<&checkauth> restarts the original script
413 (this time, C<&checkauth> returns).
415 The login page is provided using a HTML::Template, which is set in the
416 systempreferences table or at the top of this file. The variable C<$type>
417 selects which template to use, either the opac or the intranet
418 authentification template.
420 C<&checkauth> returns a user ID, a cookie, and a session ID. The
421 cookie should be sent back to the browser; it verifies that the user
426 sub _version_check ($$) {
430 # If Version syspref is unavailable, it means Koha is beeing installed,
431 # and so we must redirect to OPAC maintenance page or to the WebInstaller
432 # also, if OpacMaintenance is ON, OPAC should redirect to maintenance
433 if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') {
434 warn "OPAC Install required, redirecting to maintenance";
435 print $query->redirect("/cgi-bin/koha/maintenance.pl");
437 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
438 if ($type ne 'opac') {
439 warn "Install required, redirecting to Installer";
440 print $query->redirect("/cgi-bin/koha/installer/install.pl");
443 warn "OPAC Install required, redirecting to maintenance";
444 print $query->redirect("/cgi-bin/koha/maintenance.pl");
449 # check that database and koha version are the same
450 # there is no DB version, it's a fresh install,
451 # go to web installer
452 # there is a DB version, compare it to the code version
453 my $kohaversion=C4::Context::KOHAVERSION;
454 # remove the 3 last . to have a Perl number
455 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
456 $debug and print STDERR "kohaversion : $kohaversion\n";
457 if ($version < $kohaversion){
458 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
459 if ($type ne 'opac'){
460 warn sprintf($warning, 'Installer');
461 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
463 warn sprintf("OPAC: " . $warning, 'maintenance');
464 print $query->redirect("/cgi-bin/koha/maintenance.pl");
472 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
473 printf L join("\n",@_);
479 $debug and warn "Checking Auth";
480 # $authnotrequired will be set for scripts which will run without authentication
481 my $authnotrequired = shift;
482 my $flagsrequired = shift;
484 $type = 'opac' unless $type;
486 my $dbh = C4::Context->dbh;
487 my $timeout = C4::Context->preference('timeout');
489 if ($timeout =~ /(\d+)[dD]/) {
490 $timeout = $1 * 86400;
492 $timeout = 600 unless $timeout;
494 _version_check($type,$query);
498 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
499 my $logout = $query->param('logout.x');
501 if ( $userid = $ENV{'REMOTE_USER'} ) {
502 # Using Basic Authentication, no cookies required
503 $cookie = $query->cookie(
504 -name => 'CGISESSID',
510 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
511 my $session = get_session($sessionID);
512 C4::Context->_new_userenv($sessionID);
513 my ($ip, $lasttime, $sessiontype);
515 C4::Context::set_userenv(
516 $session->param('number'), $session->param('id'),
517 $session->param('cardnumber'), $session->param('firstname'),
518 $session->param('surname'), $session->param('branch'),
519 $session->param('branchname'), $session->param('flags'),
520 $session->param('emailaddress'), $session->param('branchprinter')
522 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
523 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
524 C4::Context::set_shelves_userenv('tot',$session->param('totshelves'));
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($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($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";
637 # launch a sequence to check if we have a ip for the branch, i
638 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
640 my $ip = $ENV{'REMOTE_ADDR'};
641 # if they specify at login, use that
642 if ($query->param('branch')) {
643 $branchcode = $query->param('branch');
644 $branchname = GetBranchName($branchcode);
646 my $branches = GetBranches();
647 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
648 # we have to check they are coming from the right ip range
649 my $domain = $branches->{$branchcode}->{'branchip'};
650 if ($ip !~ /^$domain/){
652 $info{'wrongip'} = 1;
657 foreach my $br ( keys %$branches ) {
658 # now we work with the treatment of ip
659 my $domain = $branches->{$br}->{'branchip'};
660 if ( $domain && $ip =~ /^$domain/ ) {
661 $branchcode = $branches->{$br}->{'branchcode'};
663 # new op dev : add the branchprinter and branchname in the cookie
664 $branchprinter = $branches->{$br}->{'branchprinter'};
665 $branchname = $branches->{$br}->{'branchname'};
668 $session->param('number',$borrowernumber);
669 $session->param('id',$userid);
670 $session->param('cardnumber',$cardnumber);
671 $session->param('firstname',$firstname);
672 $session->param('surname',$surname);
673 $session->param('branch',$branchcode);
674 $session->param('branchname',$branchname);
675 $session->param('flags',$userflags);
676 $session->param('emailaddress',$emailaddress);
677 $session->param('ip',$session->remote_addr());
678 $session->param('lasttime',time());
679 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
681 elsif ( $return == 2 ) {
682 #We suppose the user is the superlibrarian
684 $session->param('number',0);
685 $session->param('id',C4::Context->config('user'));
686 $session->param('cardnumber',C4::Context->config('user'));
687 $session->param('firstname',C4::Context->config('user'));
688 $session->param('surname',C4::Context->config('user'));
689 $session->param('branch','NO_LIBRARY_SET');
690 $session->param('branchname','NO_LIBRARY_SET');
691 $session->param('flags',1);
692 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
693 $session->param('ip',$session->remote_addr());
694 $session->param('lasttime',time());
696 C4::Context::set_userenv(
697 $session->param('number'), $session->param('id'),
698 $session->param('cardnumber'), $session->param('firstname'),
699 $session->param('surname'), $session->param('branch'),
700 $session->param('branchname'), $session->param('flags'),
701 $session->param('emailaddress'), $session->param('branchprinter')
704 # Grab borrower's shelves and public shelves and add them to the session
705 # $row_count determines how many records are returned from the db query
706 # and the number of lists to be displayed of each type in the 'Lists' button drop down
707 my $row_count = 10; # FIXME:This probably should be a syspref
708 my ($total, $totshelves, $barshelves, $pubshelves);
709 ($barshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(1, $row_count, $borrowernumber);
710 $total->{'bartotal'} = $totshelves;
711 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
712 $total->{'pubtotal'} = $totshelves;
713 $session->param('barshelves', $barshelves->[0]);
714 $session->param('pubshelves', $pubshelves->[0]);
715 $session->param('totshelves', $total);
717 C4::Context::set_shelves_userenv('bar',$barshelves->[0]);
718 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
719 C4::Context::set_shelves_userenv('tot',$total);
723 $info{'invalid_username_or_password'} = 1;
724 C4::Context->_unset_userenv($sessionID);
727 } # END if ( $userid = $query->param('userid') )
728 elsif ($type eq "opac") {
729 # if we are here this is an anonymous session; add public lists to it and a few other items...
730 # anonymous sessions are created only for the OPAC
731 $debug and warn "Initiating an anonymous session...";
733 # Grab the public shelves and add to the session...
734 my $row_count = 20; # FIXME:This probably should be a syspref
735 my ($total, $totshelves, $pubshelves);
736 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
737 $total->{'pubtotal'} = $totshelves;
738 $session->param('pubshelves', $pubshelves->[0]);
739 $session->param('totshelves', $total);
740 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
741 C4::Context::set_shelves_userenv('tot',$total);
743 # setting a couple of other session vars...
744 $session->param('ip',$session->remote_addr());
745 $session->param('lasttime',time());
746 $session->param('sessiontype','anon');
748 } # END unless ($userid)
749 my $insecure = C4::Context->boolean_preference('insecure');
751 # finished authentification, now respond
752 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
756 $cookie = $query->cookie( CGISESSID => '' );
758 return ( $userid, $cookie, $sessionID, $flags );
763 # AUTH rejected, show the login/password template, after checking the DB.
767 # get the inputs from the incoming query
769 foreach my $name ( param $query) {
770 (next) if ( $name eq 'userid' || $name eq 'password' );
771 my $value = $query->param($name);
772 push @inputs, { name => $name, value => $value };
774 # get the branchloop, which we need for authentication
775 my $branches = GetBranches();
777 for my $branch_hash (sort keys %$branches) {
778 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
781 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
782 my $template = gettemplate( $template_name, $type, $query );
783 $template->param(branchloop => \@branch_loop,);
787 suggestion => C4::Context->preference("suggestion"),
788 virtualshelves => C4::Context->preference("virtualshelves"),
789 LibraryName => C4::Context->preference("LibraryName"),
790 opacuserlogin => C4::Context->preference("opacuserlogin"),
791 OpacNav => C4::Context->preference("OpacNav"),
792 opaccredits => C4::Context->preference("opaccredits"),
793 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
794 opacsmallimage => C4::Context->preference("opacsmallimage"),
795 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
796 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
797 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
798 opacuserjs => C4::Context->preference("opacuserjs"),
799 opacbookbag => "" . C4::Context->preference("opacbookbag"),
800 OpacCloud => C4::Context->preference("OpacCloud"),
801 OpacTopissue => C4::Context->preference("OpacTopissue"),
802 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
803 OpacBrowser => C4::Context->preference("OpacBrowser"),
804 opacheader => C4::Context->preference("opacheader"),
805 TagsEnabled => C4::Context->preference("TagsEnabled"),
806 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
807 intranetcolorstylesheet =>
808 C4::Context->preference("intranetcolorstylesheet"),
809 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
810 IntranetNav => C4::Context->preference("IntranetNav"),
811 intranetuserjs => C4::Context->preference("intranetuserjs"),
812 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
813 IndependantBranches=> C4::Context->preference("IndependantBranches"),
814 AutoLocation => C4::Context->preference("AutoLocation"),
815 wrongip => $info{'wrongip'}
818 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
820 my $self_url = $query->url( -absolute => 1 );
823 LibraryName => C4::Context->preference("LibraryName"),
825 $template->param( \%info );
826 # $cookie = $query->cookie(CGISESSID => $session->id
828 print $query->header(
829 -type => 'text/html',
839 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
841 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
842 cookie, determine if the user has the privileges specified by C<$userflags>.
844 C<check_api_auth> is is meant for authenticating users of web services, and
845 consequently will always return and will not attempt to redirect the user
848 If a valid session cookie is already present, check_api_auth will return a status
849 of "ok", the cookie, and the Koha session ID.
851 If no session cookie is present, check_api_auth will check the 'userid' and 'password
852 parameters and create a session cookie and Koha session if the supplied credentials
855 Possible return values in C<$status> are:
859 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
861 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
863 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
865 =item "expired -- session cookie has expired; API user should resubmit userid and password
873 my $flagsrequired = shift;
875 my $dbh = C4::Context->dbh;
876 my $timeout = C4::Context->preference('timeout');
877 $timeout = 600 unless $timeout;
879 unless (C4::Context->preference('Version')) {
880 # database has not been installed yet
881 return ("maintenance", undef, undef);
883 my $kohaversion=C4::Context::KOHAVERSION;
884 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
885 if (C4::Context->preference('Version') < $kohaversion) {
886 # database in need of version update; assume that
887 # no API should be called while databsae is in
889 return ("maintenance", undef, undef);
892 # FIXME -- most of what follows is a copy-and-paste
893 # of code from checkauth. There is an obvious need
894 # for refactoring to separate the various parts of
895 # the authentication code, but as of 2007-11-19 this
896 # is deferred so as to not introduce bugs into the
897 # regular authentication code for Koha 3.0.
899 # see if we have a valid session cookie already
900 # however, if a userid parameter is present (i.e., from
901 # a form submission, assume that any current cookie
903 my $sessionID = undef;
904 unless ($query->param('userid')) {
905 $sessionID = $query->cookie("CGISESSID");
908 my $session = get_session($sessionID);
909 C4::Context->_new_userenv($sessionID);
911 C4::Context::set_userenv(
912 $session->param('number'), $session->param('id'),
913 $session->param('cardnumber'), $session->param('firstname'),
914 $session->param('surname'), $session->param('branch'),
915 $session->param('branchname'), $session->param('flags'),
916 $session->param('emailaddress'), $session->param('branchprinter')
919 my $ip = $session->param('ip');
920 my $lasttime = $session->param('lasttime');
921 my $userid = $session->param('id');
922 if ( $lasttime < time() - $timeout ) {
925 C4::Context->_unset_userenv($sessionID);
928 return ("expired", undef, undef);
929 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
932 C4::Context->_unset_userenv($sessionID);
935 return ("expired", undef, undef);
937 my $cookie = $query->cookie( CGISESSID => $session->id );
938 $session->param('lasttime',time());
939 my $flags = haspermission($userid, $flagsrequired);
941 return ("ok", $cookie, $sessionID);
944 C4::Context->_unset_userenv($sessionID);
947 return ("failed", undef, undef);
951 return ("expired", undef, undef);
955 my $userid = $query->param('userid');
956 my $password = $query->param('password');
957 unless ($userid and $password) {
958 # caller did something wrong, fail the authenticateion
959 return ("failed", undef, undef);
961 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
962 if ($return and haspermission($userid, $flagsrequired)) {
963 my $session = get_session("");
964 return ("failed", undef, undef) unless $session;
966 my $sessionID = $session->id;
967 C4::Context->_new_userenv($sessionID);
968 my $cookie = $query->cookie(CGISESSID => $sessionID);
969 if ( $return == 1 ) {
971 $borrowernumber, $firstname, $surname,
972 $userflags, $branchcode, $branchname,
973 $branchprinter, $emailaddress
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 userid=?"
979 $sth->execute($userid);
981 $borrowernumber, $firstname, $surname,
982 $userflags, $branchcode, $branchname,
983 $branchprinter, $emailaddress
984 ) = $sth->fetchrow if ( $sth->rows );
986 unless ($sth->rows ) {
987 my $sth = $dbh->prepare(
988 "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=?"
990 $sth->execute($cardnumber);
992 $borrowernumber, $firstname, $surname,
993 $userflags, $branchcode, $branchname,
994 $branchprinter, $emailaddress
995 ) = $sth->fetchrow if ( $sth->rows );
997 unless ( $sth->rows ) {
998 $sth->execute($userid);
1000 $borrowernumber, $firstname, $surname, $userflags,
1001 $branchcode, $branchname, $branchprinter, $emailaddress
1002 ) = $sth->fetchrow if ( $sth->rows );
1006 my $ip = $ENV{'REMOTE_ADDR'};
1007 # if they specify at login, use that
1008 if ($query->param('branch')) {
1009 $branchcode = $query->param('branch');
1010 $branchname = GetBranchName($branchcode);
1012 my $branches = GetBranches();
1014 foreach my $br ( keys %$branches ) {
1015 # now we work with the treatment of ip
1016 my $domain = $branches->{$br}->{'branchip'};
1017 if ( $domain && $ip =~ /^$domain/ ) {
1018 $branchcode = $branches->{$br}->{'branchcode'};
1020 # new op dev : add the branchprinter and branchname in the cookie
1021 $branchprinter = $branches->{$br}->{'branchprinter'};
1022 $branchname = $branches->{$br}->{'branchname'};
1025 $session->param('number',$borrowernumber);
1026 $session->param('id',$userid);
1027 $session->param('cardnumber',$cardnumber);
1028 $session->param('firstname',$firstname);
1029 $session->param('surname',$surname);
1030 $session->param('branch',$branchcode);
1031 $session->param('branchname',$branchname);
1032 $session->param('flags',$userflags);
1033 $session->param('emailaddress',$emailaddress);
1034 $session->param('ip',$session->remote_addr());
1035 $session->param('lasttime',time());
1036 } elsif ( $return == 2 ) {
1037 #We suppose the user is the superlibrarian
1038 $session->param('number',0);
1039 $session->param('id',C4::Context->config('user'));
1040 $session->param('cardnumber',C4::Context->config('user'));
1041 $session->param('firstname',C4::Context->config('user'));
1042 $session->param('surname',C4::Context->config('user'));
1043 $session->param('branch','NO_LIBRARY_SET');
1044 $session->param('branchname','NO_LIBRARY_SET');
1045 $session->param('flags',1);
1046 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1047 $session->param('ip',$session->remote_addr());
1048 $session->param('lasttime',time());
1050 C4::Context::set_userenv(
1051 $session->param('number'), $session->param('id'),
1052 $session->param('cardnumber'), $session->param('firstname'),
1053 $session->param('surname'), $session->param('branch'),
1054 $session->param('branchname'), $session->param('flags'),
1055 $session->param('emailaddress'), $session->param('branchprinter')
1057 return ("ok", $cookie, $sessionID);
1059 return ("failed", undef, undef);
1064 =item check_cookie_auth
1066 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1068 Given a CGISESSID cookie set during a previous login to Koha, determine
1069 if the user has the privileges specified by C<$userflags>.
1071 C<check_cookie_auth> is meant for authenticating special services
1072 such as tools/upload-file.pl that are invoked by other pages that
1073 have been authenticated in the usual way.
1075 Possible return values in C<$status> are:
1079 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1081 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1083 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1085 =item "expired -- session cookie has expired; API user should resubmit userid and password
1091 sub check_cookie_auth {
1093 my $flagsrequired = shift;
1095 my $dbh = C4::Context->dbh;
1096 my $timeout = C4::Context->preference('timeout');
1097 $timeout = 600 unless $timeout;
1099 unless (C4::Context->preference('Version')) {
1100 # database has not been installed yet
1101 return ("maintenance", undef);
1103 my $kohaversion=C4::Context::KOHAVERSION;
1104 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1105 if (C4::Context->preference('Version') < $kohaversion) {
1106 # database in need of version update; assume that
1107 # no API should be called while databsae is in
1109 return ("maintenance", undef);
1112 # FIXME -- most of what follows is a copy-and-paste
1113 # of code from checkauth. There is an obvious need
1114 # for refactoring to separate the various parts of
1115 # the authentication code, but as of 2007-11-23 this
1116 # is deferred so as to not introduce bugs into the
1117 # regular authentication code for Koha 3.0.
1119 # see if we have a valid session cookie already
1120 # however, if a userid parameter is present (i.e., from
1121 # a form submission, assume that any current cookie
1123 unless (defined $cookie and $cookie) {
1124 return ("failed", undef);
1126 my $sessionID = $cookie;
1127 my $session = get_session($sessionID);
1128 C4::Context->_new_userenv($sessionID);
1130 C4::Context::set_userenv(
1131 $session->param('number'), $session->param('id'),
1132 $session->param('cardnumber'), $session->param('firstname'),
1133 $session->param('surname'), $session->param('branch'),
1134 $session->param('branchname'), $session->param('flags'),
1135 $session->param('emailaddress'), $session->param('branchprinter')
1138 my $ip = $session->param('ip');
1139 my $lasttime = $session->param('lasttime');
1140 my $userid = $session->param('id');
1141 if ( $lasttime < time() - $timeout ) {
1144 C4::Context->_unset_userenv($sessionID);
1147 return ("expired", undef);
1148 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1149 # IP address changed
1151 C4::Context->_unset_userenv($sessionID);
1154 return ("expired", undef);
1156 $session->param('lasttime',time());
1157 my $flags = haspermission($userid, $flagsrequired);
1159 return ("ok", $sessionID);
1162 C4::Context->_unset_userenv($sessionID);
1165 return ("failed", undef);
1169 return ("expired", undef);
1176 my $session = get_session($sessionID);
1178 Given a session ID, retrieve the CGI::Session object used to store
1179 the session's state. The session object can be used to store
1180 data that needs to be accessed by different scripts during a
1183 If the C<$sessionID> parameter is an empty string, a new session
1189 my $sessionID = shift;
1190 my $storage_method = C4::Context->preference('SessionStorage');
1191 my $dbh = C4::Context->dbh;
1193 if ($storage_method eq 'mysql'){
1194 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1196 elsif ($storage_method eq 'Pg') {
1197 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1200 # catch all defaults to tmp should work on all systems
1201 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1208 my ( $dbh, $userid, $password ) = @_;
1210 $debug and print STDERR "## checkpw - checking LDAP\n";
1211 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1212 ($retval) and return ($retval,$retcard);
1218 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1220 $sth->execute($userid);
1222 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1223 $surname, $branchcode, $flags )
1225 if ( md5_base64($password) eq $md5password ) {
1227 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1228 $firstname, $surname, $branchcode, $flags );
1229 return 1, $cardnumber;
1234 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1236 $sth->execute($userid);
1238 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1239 $surname, $branchcode, $flags )
1241 if ( md5_base64($password) eq $md5password ) {
1243 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1244 $firstname, $surname, $branchcode, $flags );
1248 if ( $userid && $userid eq C4::Context->config('user')
1249 && "$password" eq C4::Context->config('pass') )
1252 # Koha superuser account
1253 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1256 if ( $userid && $userid eq 'demo'
1257 && "$password" eq 'demo'
1258 && C4::Context->config('demo') )
1261 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1262 # some features won't be effective : modify systempref, modify MARC structure,
1270 my $authflags = getuserflags($flags, $userid, [$dbh]);
1272 Translates integer flags into permissions strings hash.
1274 C<$flags> is the integer userflags value ( borrowers.userflags )
1275 C<$userid> is the members.userid, used for building subpermissions
1276 C<$authflags> is a hashref of permissions
1283 my $dbh = @_ ? shift : C4::Context->dbh;
1285 $flags = 0 unless $flags;
1286 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1289 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1290 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1291 $userflags->{$flag} = 1;
1294 $userflags->{$flag} = 0;
1298 # get subpermissions and merge with top-level permissions
1299 my $user_subperms = get_user_subpermissions($userid);
1300 foreach my $module (keys %$user_subperms) {
1301 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1302 $userflags->{$module} = $user_subperms->{$module};
1308 =item get_user_subpermissions
1312 my $user_perm_hashref = get_user_subpermissions($userid);
1316 Given the userid (note, not the borrowernumber) of a staff user,
1317 return a hashref of hashrefs of the specific subpermissions
1318 accorded to the user. An example return is
1322 export_catalog => 1,
1323 import_patrons => 1,
1327 The top-level hash-key is a module or function code from
1328 userflags.flag, while the second-level key is a code
1331 The results of this function do not give a complete picture
1332 of the functions that a staff user can access; it is also
1333 necessary to check borrowers.flags.
1337 sub get_user_subpermissions {
1340 my $dbh = C4::Context->dbh;
1341 my $sth = $dbh->prepare("SELECT flag, user_permissions.code as code
1342 FROM user_permissions
1343 JOIN permissions USING (module_bit, code)
1344 JOIN userflags ON (permissions.module_bit = userflags.bit)
1345 JOIN borrowers ON (user_permissions.borrowernumber=borrowers.borrowernumber)
1347 $sth->execute($userid);
1349 my $user_perms = {};
1350 while (my $perm = $sth->fetchrow_hashref) {
1351 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1356 =item get_all_subpermissions
1360 my $perm_hashref = get_all_subpermissions();
1364 Returns a hashref of hashrefs defining all specific
1365 permissions currently defined. The return value
1366 has the same structure as that of C<get_user_subpermissions>,
1367 except that the innermost hash value is the description
1368 of the subpermission.
1372 sub get_all_subpermissions {
1373 my $dbh = C4::Context->dbh;
1374 my $sth = $dbh->prepare("SELECT flag, code, description
1376 JOIN userflags ON (module_bit = bit)");
1380 while (my $perm = $sth->fetchrow_hashref) {
1381 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1388 $flags = ($userid, $flagsrequired);
1390 C<$userid> the userid of the member
1391 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1393 Returns member's flags or 0 if a permission is not met.
1398 my ($userid, $flagsrequired) = @_;
1399 my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1400 $sth->execute($userid);
1401 my $flags = getuserflags( $sth->fetchrow(), $userid );
1402 if ( $userid eq C4::Context->config('user') ) {
1403 # Super User Account from /etc/koha.conf
1404 $flags->{'superlibrarian'} = 1;
1406 elsif ( $userid eq 'demo' && C4::Context->config('demo') ) {
1407 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1408 $flags->{'superlibrarian'} = 1;
1410 return $flags if $flags->{superlibrarian};
1411 foreach my $module ( keys %$flagsrequired ) {
1412 if (C4::Context->preference('GranularPermissions')) {
1413 my $subperm = $flagsrequired->{$module};
1414 if ($subperm eq '*') {
1415 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1417 return 0 unless ( $flags->{$module} == 1 or
1418 ( ref($flags->{$module}) and
1419 exists $flags->{$module}->{$subperm} and
1420 $flags->{$module}->{$subperm} == 1
1425 return 0 unless ( $flags->{$module} );
1429 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1433 sub getborrowernumber {
1435 my $dbh = C4::Context->dbh;
1436 for my $field ( 'userid', 'cardnumber' ) {
1438 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1439 $sth->execute($userid);
1441 my ($bnumber) = $sth->fetchrow;
1448 END { } # module clean-up code here (global destructor)