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 #TODO : replace LibraryName syspref with 'system name', and remove this html processing
298 my $LibraryNameTitle = C4::Context->preference("LibraryName");
299 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
300 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
301 # variables passed from CGI: opac_css_override and opac_search_limits.
302 my $opac_search_limit = $ENV{'OPAC_SEARCH_LIMIT'};
303 my $opac_limit_override = $ENV{'OPAC_LIMIT_OVERRIDE'};
304 my $mylibraryfirst = C4::Context->preference("SearchMyLibraryFirst");
306 if($opac_limit_override && ($opac_search_limit =~ /branch:(\w+)/) ){
307 $opac_name = C4::Branch::GetBranchName($1) # opac_search_limit is a branch, so we use it.
308 } elsif($mylibraryfirst){
309 $opac_name = C4::Branch::GetBranchName($mylibraryfirst);
312 OPACAmazonContent => "" . C4::Context->preference("OPACAmazonContent"),
313 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
314 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
315 LibraryName => "" . C4::Context->preference("LibraryName"),
316 LibraryNameTitle => "" . $LibraryNameTitle,
317 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
318 OPACAmazonSimilarItems => "" . C4::Context->preference("OPACAmazonSimilarItems"),
319 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
320 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
321 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
322 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
323 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
324 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
325 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
326 OPACBaseURL => ($in->{'query'}->https() ? "https://" : "http://") . $ENV{'SERVER_NAME'} .
327 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
328 opac_name => $opac_name,
329 opac_css_override => $ENV{'OPAC_CSS_OVERRIDE'},
330 opac_search_limit => $opac_search_limit,
331 opac_limit_override => $opac_limit_override,
332 OpacBrowser => C4::Context->preference("OpacBrowser"),
333 OpacCloud => C4::Context->preference("OpacCloud"),
334 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
335 OpacNav => "" . C4::Context->preference("OpacNav"),
336 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
337 OpacTopissue => C4::Context->preference("OpacTopissue"),
338 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
339 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
340 'Version' => C4::Context->preference('Version'),
341 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
342 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
343 hidelostitems => C4::Context->preference("hidelostitems"),
344 mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '',
345 opaclayoutstylesheet => "" . C4::Context->preference("opaclayoutstylesheet"),
346 opaccolorstylesheet => "" . C4::Context->preference("opaccolorstylesheet"),
347 opacstylesheet => "" . C4::Context->preference("opacstylesheet"),
348 opacbookbag => "" . C4::Context->preference("opacbookbag"),
349 opaccredits => "" . C4::Context->preference("opaccredits"),
350 opacheader => "" . C4::Context->preference("opacheader"),
351 opaclanguagesdisplay => "" . C4::Context->preference("opaclanguagesdisplay"),
352 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
353 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
354 opacuserjs => C4::Context->preference("opacuserjs"),
355 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
356 reviewson => C4::Context->preference("reviewson"),
357 suggestion => "" . C4::Context->preference("suggestion"),
358 virtualshelves => "" . C4::Context->preference("virtualshelves"),
361 return ( $template, $borrowernumber, $cookie, $flags);
366 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
368 Verifies that the user is authorized to run this script. If
369 the user is authorized, a (userid, cookie, session-id, flags)
370 quadruple is returned. If the user is not authorized due to
371 insufficent privileges (see $flagsrequired below), it
372 displays an error page and exits. Otherwise, it displays the
373 login page and exits.
375 Note that C<&checkauth> will return if and only if the user
376 is authorized, so it should be called early on, before any
377 unfinished operations (e.g., if you've opened a file, then
378 C<&checkauth> won't close it for you).
380 C<$query> is the CGI object for the script calling C<&checkauth>.
382 The C<$noauth> argument is optional. If it is set, then no
383 authorization is required for the script.
385 C<&checkauth> fetches user and session information from C<$query> and
386 ensures that the user is authorized to run scripts that require
389 The C<$flagsrequired> argument specifies the required privileges
390 the user must have if the username and password are correct.
391 It should be specified as a reference-to-hash; keys in the hash
392 should be the "flags" for the user, as specified in the Members
393 intranet module. Any key specified must correspond to a "flag"
394 in the userflags table. E.g., { circulate => 1 } would specify
395 that the user must have the "circulate" privilege in order to
396 proceed. To make sure that access control is correct, the
397 C<$flagsrequired> parameter must be specified correctly.
399 If the GranularPermissions system preference is ON, the
400 value of each key in the C<flagsrequired> hash takes on an additional
405 The user must have access to all subfunctions of the module
406 specified by the hash key.
410 The user must have access to at least one subfunction of the module
411 specified by the hash key.
413 =item specific permission, e.g., 'export_catalog'
415 The user must have access to the specific subfunction list, which
416 must correspond to a row in the permissions table.
418 The C<$type> argument specifies whether the template should be
419 retrieved from the opac or intranet directory tree. "opac" is
420 assumed if it is not specified; however, if C<$type> is specified,
421 "intranet" is assumed if it is not "opac".
423 If C<$query> does not have a valid session ID associated with it
424 (i.e., the user has not logged in) or if the session has expired,
425 C<&checkauth> presents the user with a login page (from the point of
426 view of the original script, C<&checkauth> does not return). Once the
427 user has authenticated, C<&checkauth> restarts the original script
428 (this time, C<&checkauth> returns).
430 The login page is provided using a HTML::Template, which is set in the
431 systempreferences table or at the top of this file. The variable C<$type>
432 selects which template to use, either the opac or the intranet
433 authentification template.
435 C<&checkauth> returns a user ID, a cookie, and a session ID. The
436 cookie should be sent back to the browser; it verifies that the user
441 sub _version_check ($$) {
445 # If Version syspref is unavailable, it means Koha is beeing installed,
446 # and so we must redirect to OPAC maintenance page or to the WebInstaller
447 # also, if OpacMaintenance is ON, OPAC should redirect to maintenance
448 if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') {
449 warn "OPAC Install required, redirecting to maintenance";
450 print $query->redirect("/cgi-bin/koha/maintenance.pl");
452 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
453 if ($type ne 'opac') {
454 warn "Install required, redirecting to Installer";
455 print $query->redirect("/cgi-bin/koha/installer/install.pl");
458 warn "OPAC Install required, redirecting to maintenance";
459 print $query->redirect("/cgi-bin/koha/maintenance.pl");
464 # check that database and koha version are the same
465 # there is no DB version, it's a fresh install,
466 # go to web installer
467 # there is a DB version, compare it to the code version
468 my $kohaversion=C4::Context::KOHAVERSION;
469 # remove the 3 last . to have a Perl number
470 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
471 $debug and print STDERR "kohaversion : $kohaversion\n";
472 if ($version < $kohaversion){
473 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
474 if ($type ne 'opac'){
475 warn sprintf($warning, 'Installer');
476 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
478 warn sprintf("OPAC: " . $warning, 'maintenance');
479 print $query->redirect("/cgi-bin/koha/maintenance.pl");
487 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
488 printf L join("\n",@_);
494 $debug and warn "Checking Auth";
495 # $authnotrequired will be set for scripts which will run without authentication
496 my $authnotrequired = shift;
497 my $flagsrequired = shift;
499 $type = 'opac' unless $type;
501 my $dbh = C4::Context->dbh;
502 my $timeout = C4::Context->preference('timeout');
504 if ($timeout =~ /(\d+)[dD]/) {
505 $timeout = $1 * 86400;
507 $timeout = 600 unless $timeout;
509 _version_check($type,$query);
513 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
514 my $logout = $query->param('logout.x');
516 if ( $userid = $ENV{'REMOTE_USER'} ) {
517 # Using Basic Authentication, no cookies required
518 $cookie = $query->cookie(
519 -name => 'CGISESSID',
525 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
526 my $session = get_session($sessionID);
527 C4::Context->_new_userenv($sessionID);
528 my ($ip, $lasttime, $sessiontype);
530 C4::Context::set_userenv(
531 $session->param('number'), $session->param('id'),
532 $session->param('cardnumber'), $session->param('firstname'),
533 $session->param('surname'), $session->param('branch'),
534 $session->param('branchname'), $session->param('flags'),
535 $session->param('emailaddress'), $session->param('branchprinter')
537 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
538 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
539 C4::Context::set_shelves_userenv('tot',$session->param('totshelves'));
540 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
541 $ip = $session->param('ip');
542 $lasttime = $session->param('lasttime');
543 $userid = $session->param('id');
544 $sessiontype = $session->param('sessiontype');
547 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
548 #if a user enters an id ne to the id in the current session, we need to log them in...
549 #first we need to clear the anonymous session...
550 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
553 C4::Context->_unset_userenv($sessionID);
558 # voluntary logout the user
561 C4::Context->_unset_userenv($sessionID);
562 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,localtime);
566 elsif ( $lasttime < time() - $timeout ) {
568 $info{'timed_out'} = 1;
570 C4::Context->_unset_userenv($sessionID);
571 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,localtime);
575 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
576 # Different ip than originally logged in from
577 $info{'oldip'} = $ip;
578 $info{'newip'} = $ENV{'REMOTE_ADDR'};
579 $info{'different_ip'} = 1;
581 C4::Context->_unset_userenv($sessionID);
582 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,localtime, $info{'newip'});
587 $cookie = $query->cookie( CGISESSID => $session->id );
588 $session->param('lasttime',time());
589 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...
590 $flags = haspermission($userid, $flagsrequired);
594 $info{'nopermission'} = 1;
599 unless ($userid || $sessionID) {
600 #we initiate a session prior to checking for a username to allow for anonymous sessions...
601 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
602 my $sessionID = $session->id;
603 C4::Context->_new_userenv($sessionID);
604 $cookie = $query->cookie(CGISESSID => $sessionID);
605 if ( $userid = $query->param('userid') ) {
606 my $password = $query->param('password');
607 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
609 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},localtime);
610 if ( $flags = haspermission($userid, $flagsrequired) ) {
614 $info{'nopermission'} = 1;
615 C4::Context->_unset_userenv($sessionID);
618 my ($borrowernumber, $firstname, $surname, $userflags,
619 $branchcode, $branchname, $branchprinter, $emailaddress);
621 if ( $return == 1 ) {
623 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
624 branches.branchname as branchname,
625 branches.branchprinter as branchprinter,
628 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
630 my $sth = $dbh->prepare("$select where userid=?");
631 $sth->execute($userid);
632 unless ($sth->rows) {
633 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
634 $sth = $dbh->prepare("$select where cardnumber=?");
635 $sth->execute($cardnumber);
636 unless ($sth->rows) {
637 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
638 $sth->execute($userid);
639 unless ($sth->rows) {
640 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
645 ($borrowernumber, $firstname, $surname, $userflags,
646 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
647 $debug and print STDERR "AUTH_3 results: " .
648 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
650 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
652 # launch a sequence to check if we have a ip for the branch, i
653 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
655 my $ip = $ENV{'REMOTE_ADDR'};
656 # if they specify at login, use that
657 if ($query->param('branch')) {
658 $branchcode = $query->param('branch');
659 $branchname = GetBranchName($branchcode);
661 my $branches = GetBranches();
662 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
663 # we have to check they are coming from the right ip range
664 my $domain = $branches->{$branchcode}->{'branchip'};
665 if ($ip !~ /^$domain/){
667 $info{'wrongip'} = 1;
672 foreach my $br ( keys %$branches ) {
673 # now we work with the treatment of ip
674 my $domain = $branches->{$br}->{'branchip'};
675 if ( $domain && $ip =~ /^$domain/ ) {
676 $branchcode = $branches->{$br}->{'branchcode'};
678 # new op dev : add the branchprinter and branchname in the cookie
679 $branchprinter = $branches->{$br}->{'branchprinter'};
680 $branchname = $branches->{$br}->{'branchname'};
683 $session->param('number',$borrowernumber);
684 $session->param('id',$userid);
685 $session->param('cardnumber',$cardnumber);
686 $session->param('firstname',$firstname);
687 $session->param('surname',$surname);
688 $session->param('branch',$branchcode);
689 $session->param('branchname',$branchname);
690 $session->param('flags',$userflags);
691 $session->param('emailaddress',$emailaddress);
692 $session->param('ip',$session->remote_addr());
693 $session->param('lasttime',time());
694 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
696 elsif ( $return == 2 ) {
697 #We suppose the user is the superlibrarian
699 $session->param('number',0);
700 $session->param('id',C4::Context->config('user'));
701 $session->param('cardnumber',C4::Context->config('user'));
702 $session->param('firstname',C4::Context->config('user'));
703 $session->param('surname',C4::Context->config('user'));
704 $session->param('branch','NO_LIBRARY_SET');
705 $session->param('branchname','NO_LIBRARY_SET');
706 $session->param('flags',1);
707 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
708 $session->param('ip',$session->remote_addr());
709 $session->param('lasttime',time());
711 C4::Context::set_userenv(
712 $session->param('number'), $session->param('id'),
713 $session->param('cardnumber'), $session->param('firstname'),
714 $session->param('surname'), $session->param('branch'),
715 $session->param('branchname'), $session->param('flags'),
716 $session->param('emailaddress'), $session->param('branchprinter')
719 # Grab borrower's shelves and public shelves and add them to the session
720 # $row_count determines how many records are returned from the db query
721 # and the number of lists to be displayed of each type in the 'Lists' button drop down
722 my $row_count = 10; # FIXME:This probably should be a syspref
723 my ($total, $totshelves, $barshelves, $pubshelves);
724 ($barshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(1, $row_count, $borrowernumber);
725 $total->{'bartotal'} = $totshelves;
726 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
727 $total->{'pubtotal'} = $totshelves;
728 $session->param('barshelves', $barshelves->[0]);
729 $session->param('pubshelves', $pubshelves->[0]);
730 $session->param('totshelves', $total);
732 C4::Context::set_shelves_userenv('bar',$barshelves->[0]);
733 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
734 C4::Context::set_shelves_userenv('tot',$total);
738 $info{'invalid_username_or_password'} = 1;
739 C4::Context->_unset_userenv($sessionID);
742 } # END if ( $userid = $query->param('userid') )
743 elsif ($type eq "opac") {
744 # if we are here this is an anonymous session; add public lists to it and a few other items...
745 # anonymous sessions are created only for the OPAC
746 $debug and warn "Initiating an anonymous session...";
748 # Grab the public shelves and add to the session...
749 my $row_count = 20; # FIXME:This probably should be a syspref
750 my ($total, $totshelves, $pubshelves);
751 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
752 $total->{'pubtotal'} = $totshelves;
753 $session->param('pubshelves', $pubshelves->[0]);
754 $session->param('totshelves', $total);
755 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
756 C4::Context::set_shelves_userenv('tot',$total);
758 # setting a couple of other session vars...
759 $session->param('ip',$session->remote_addr());
760 $session->param('lasttime',time());
761 $session->param('sessiontype','anon');
763 } # END unless ($userid)
764 my $insecure = C4::Context->boolean_preference('insecure');
766 # finished authentification, now respond
767 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
771 $cookie = $query->cookie( CGISESSID => '' );
773 return ( $userid, $cookie, $sessionID, $flags );
778 # AUTH rejected, show the login/password template, after checking the DB.
782 # get the inputs from the incoming query
784 foreach my $name ( param $query) {
785 (next) if ( $name eq 'userid' || $name eq 'password' );
786 my $value = $query->param($name);
787 push @inputs, { name => $name, value => $value };
789 # get the branchloop, which we need for authentication
790 my $branches = GetBranches();
792 for my $branch_hash (sort keys %$branches) {
793 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
796 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
797 my $template = gettemplate( $template_name, $type, $query );
798 $template->param(branchloop => \@branch_loop,);
802 suggestion => C4::Context->preference("suggestion"),
803 virtualshelves => C4::Context->preference("virtualshelves"),
804 LibraryName => C4::Context->preference("LibraryName"),
805 opacuserlogin => C4::Context->preference("opacuserlogin"),
806 OpacNav => C4::Context->preference("OpacNav"),
807 opaccredits => C4::Context->preference("opaccredits"),
808 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
809 opacsmallimage => C4::Context->preference("opacsmallimage"),
810 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
811 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
812 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
813 opacuserjs => C4::Context->preference("opacuserjs"),
814 opacbookbag => "" . C4::Context->preference("opacbookbag"),
815 OpacCloud => C4::Context->preference("OpacCloud"),
816 OpacTopissue => C4::Context->preference("OpacTopissue"),
817 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
818 OpacBrowser => C4::Context->preference("OpacBrowser"),
819 opacheader => C4::Context->preference("opacheader"),
820 TagsEnabled => C4::Context->preference("TagsEnabled"),
821 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
822 intranetcolorstylesheet =>
823 C4::Context->preference("intranetcolorstylesheet"),
824 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
825 IntranetNav => C4::Context->preference("IntranetNav"),
826 intranetuserjs => C4::Context->preference("intranetuserjs"),
827 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
828 IndependantBranches=> C4::Context->preference("IndependantBranches"),
829 AutoLocation => C4::Context->preference("AutoLocation"),
830 wrongip => $info{'wrongip'}
833 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
835 my $self_url = $query->url( -absolute => 1 );
838 LibraryName => C4::Context->preference("LibraryName"),
840 $template->param( \%info );
841 # $cookie = $query->cookie(CGISESSID => $session->id
843 print $query->header(
844 -type => 'text/html',
854 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
856 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
857 cookie, determine if the user has the privileges specified by C<$userflags>.
859 C<check_api_auth> is is meant for authenticating users of web services, and
860 consequently will always return and will not attempt to redirect the user
863 If a valid session cookie is already present, check_api_auth will return a status
864 of "ok", the cookie, and the Koha session ID.
866 If no session cookie is present, check_api_auth will check the 'userid' and 'password
867 parameters and create a session cookie and Koha session if the supplied credentials
870 Possible return values in C<$status> are:
874 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
876 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
878 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
880 =item "expired -- session cookie has expired; API user should resubmit userid and password
888 my $flagsrequired = shift;
890 my $dbh = C4::Context->dbh;
891 my $timeout = C4::Context->preference('timeout');
892 $timeout = 600 unless $timeout;
894 unless (C4::Context->preference('Version')) {
895 # database has not been installed yet
896 return ("maintenance", undef, undef);
898 my $kohaversion=C4::Context::KOHAVERSION;
899 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
900 if (C4::Context->preference('Version') < $kohaversion) {
901 # database in need of version update; assume that
902 # no API should be called while databsae is in
904 return ("maintenance", undef, undef);
907 # FIXME -- most of what follows is a copy-and-paste
908 # of code from checkauth. There is an obvious need
909 # for refactoring to separate the various parts of
910 # the authentication code, but as of 2007-11-19 this
911 # is deferred so as to not introduce bugs into the
912 # regular authentication code for Koha 3.0.
914 # see if we have a valid session cookie already
915 # however, if a userid parameter is present (i.e., from
916 # a form submission, assume that any current cookie
918 my $sessionID = undef;
919 unless ($query->param('userid')) {
920 $sessionID = $query->cookie("CGISESSID");
923 my $session = get_session($sessionID);
924 C4::Context->_new_userenv($sessionID);
926 C4::Context::set_userenv(
927 $session->param('number'), $session->param('id'),
928 $session->param('cardnumber'), $session->param('firstname'),
929 $session->param('surname'), $session->param('branch'),
930 $session->param('branchname'), $session->param('flags'),
931 $session->param('emailaddress'), $session->param('branchprinter')
934 my $ip = $session->param('ip');
935 my $lasttime = $session->param('lasttime');
936 my $userid = $session->param('id');
937 if ( $lasttime < time() - $timeout ) {
940 C4::Context->_unset_userenv($sessionID);
943 return ("expired", undef, undef);
944 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
947 C4::Context->_unset_userenv($sessionID);
950 return ("expired", undef, undef);
952 my $cookie = $query->cookie( CGISESSID => $session->id );
953 $session->param('lasttime',time());
954 my $flags = haspermission($userid, $flagsrequired);
956 return ("ok", $cookie, $sessionID);
959 C4::Context->_unset_userenv($sessionID);
962 return ("failed", undef, undef);
966 return ("expired", undef, undef);
970 my $userid = $query->param('userid');
971 my $password = $query->param('password');
972 unless ($userid and $password) {
973 # caller did something wrong, fail the authenticateion
974 return ("failed", undef, undef);
976 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
977 if ($return and haspermission($userid, $flagsrequired)) {
978 my $session = get_session("");
979 return ("failed", undef, undef) unless $session;
981 my $sessionID = $session->id;
982 C4::Context->_new_userenv($sessionID);
983 my $cookie = $query->cookie(CGISESSID => $sessionID);
984 if ( $return == 1 ) {
986 $borrowernumber, $firstname, $surname,
987 $userflags, $branchcode, $branchname,
988 $branchprinter, $emailaddress
992 "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=?"
994 $sth->execute($userid);
996 $borrowernumber, $firstname, $surname,
997 $userflags, $branchcode, $branchname,
998 $branchprinter, $emailaddress
999 ) = $sth->fetchrow if ( $sth->rows );
1001 unless ($sth->rows ) {
1002 my $sth = $dbh->prepare(
1003 "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=?"
1005 $sth->execute($cardnumber);
1007 $borrowernumber, $firstname, $surname,
1008 $userflags, $branchcode, $branchname,
1009 $branchprinter, $emailaddress
1010 ) = $sth->fetchrow if ( $sth->rows );
1012 unless ( $sth->rows ) {
1013 $sth->execute($userid);
1015 $borrowernumber, $firstname, $surname, $userflags,
1016 $branchcode, $branchname, $branchprinter, $emailaddress
1017 ) = $sth->fetchrow if ( $sth->rows );
1021 my $ip = $ENV{'REMOTE_ADDR'};
1022 # if they specify at login, use that
1023 if ($query->param('branch')) {
1024 $branchcode = $query->param('branch');
1025 $branchname = GetBranchName($branchcode);
1027 my $branches = GetBranches();
1029 foreach my $br ( keys %$branches ) {
1030 # now we work with the treatment of ip
1031 my $domain = $branches->{$br}->{'branchip'};
1032 if ( $domain && $ip =~ /^$domain/ ) {
1033 $branchcode = $branches->{$br}->{'branchcode'};
1035 # new op dev : add the branchprinter and branchname in the cookie
1036 $branchprinter = $branches->{$br}->{'branchprinter'};
1037 $branchname = $branches->{$br}->{'branchname'};
1040 $session->param('number',$borrowernumber);
1041 $session->param('id',$userid);
1042 $session->param('cardnumber',$cardnumber);
1043 $session->param('firstname',$firstname);
1044 $session->param('surname',$surname);
1045 $session->param('branch',$branchcode);
1046 $session->param('branchname',$branchname);
1047 $session->param('flags',$userflags);
1048 $session->param('emailaddress',$emailaddress);
1049 $session->param('ip',$session->remote_addr());
1050 $session->param('lasttime',time());
1051 } elsif ( $return == 2 ) {
1052 #We suppose the user is the superlibrarian
1053 $session->param('number',0);
1054 $session->param('id',C4::Context->config('user'));
1055 $session->param('cardnumber',C4::Context->config('user'));
1056 $session->param('firstname',C4::Context->config('user'));
1057 $session->param('surname',C4::Context->config('user'));
1058 $session->param('branch','NO_LIBRARY_SET');
1059 $session->param('branchname','NO_LIBRARY_SET');
1060 $session->param('flags',1);
1061 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1062 $session->param('ip',$session->remote_addr());
1063 $session->param('lasttime',time());
1065 C4::Context::set_userenv(
1066 $session->param('number'), $session->param('id'),
1067 $session->param('cardnumber'), $session->param('firstname'),
1068 $session->param('surname'), $session->param('branch'),
1069 $session->param('branchname'), $session->param('flags'),
1070 $session->param('emailaddress'), $session->param('branchprinter')
1072 return ("ok", $cookie, $sessionID);
1074 return ("failed", undef, undef);
1079 =item check_cookie_auth
1081 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1083 Given a CGISESSID cookie set during a previous login to Koha, determine
1084 if the user has the privileges specified by C<$userflags>.
1086 C<check_cookie_auth> is meant for authenticating special services
1087 such as tools/upload-file.pl that are invoked by other pages that
1088 have been authenticated in the usual way.
1090 Possible return values in C<$status> are:
1094 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1096 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1098 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1100 =item "expired -- session cookie has expired; API user should resubmit userid and password
1106 sub check_cookie_auth {
1108 my $flagsrequired = shift;
1110 my $dbh = C4::Context->dbh;
1111 my $timeout = C4::Context->preference('timeout');
1112 $timeout = 600 unless $timeout;
1114 unless (C4::Context->preference('Version')) {
1115 # database has not been installed yet
1116 return ("maintenance", undef);
1118 my $kohaversion=C4::Context::KOHAVERSION;
1119 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1120 if (C4::Context->preference('Version') < $kohaversion) {
1121 # database in need of version update; assume that
1122 # no API should be called while databsae is in
1124 return ("maintenance", undef);
1127 # FIXME -- most of what follows is a copy-and-paste
1128 # of code from checkauth. There is an obvious need
1129 # for refactoring to separate the various parts of
1130 # the authentication code, but as of 2007-11-23 this
1131 # is deferred so as to not introduce bugs into the
1132 # regular authentication code for Koha 3.0.
1134 # see if we have a valid session cookie already
1135 # however, if a userid parameter is present (i.e., from
1136 # a form submission, assume that any current cookie
1138 unless (defined $cookie and $cookie) {
1139 return ("failed", undef);
1141 my $sessionID = $cookie;
1142 my $session = get_session($sessionID);
1143 C4::Context->_new_userenv($sessionID);
1145 C4::Context::set_userenv(
1146 $session->param('number'), $session->param('id'),
1147 $session->param('cardnumber'), $session->param('firstname'),
1148 $session->param('surname'), $session->param('branch'),
1149 $session->param('branchname'), $session->param('flags'),
1150 $session->param('emailaddress'), $session->param('branchprinter')
1153 my $ip = $session->param('ip');
1154 my $lasttime = $session->param('lasttime');
1155 my $userid = $session->param('id');
1156 if ( $lasttime < time() - $timeout ) {
1159 C4::Context->_unset_userenv($sessionID);
1162 return ("expired", undef);
1163 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1164 # IP address changed
1166 C4::Context->_unset_userenv($sessionID);
1169 return ("expired", undef);
1171 $session->param('lasttime',time());
1172 my $flags = haspermission($userid, $flagsrequired);
1174 return ("ok", $sessionID);
1177 C4::Context->_unset_userenv($sessionID);
1180 return ("failed", undef);
1184 return ("expired", undef);
1191 my $session = get_session($sessionID);
1193 Given a session ID, retrieve the CGI::Session object used to store
1194 the session's state. The session object can be used to store
1195 data that needs to be accessed by different scripts during a
1198 If the C<$sessionID> parameter is an empty string, a new session
1204 my $sessionID = shift;
1205 my $storage_method = C4::Context->preference('SessionStorage');
1206 my $dbh = C4::Context->dbh;
1208 if ($storage_method eq 'mysql'){
1209 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1211 elsif ($storage_method eq 'Pg') {
1212 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1215 # catch all defaults to tmp should work on all systems
1216 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1223 my ( $dbh, $userid, $password ) = @_;
1225 $debug and print STDERR "## checkpw - checking LDAP\n";
1226 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1227 ($retval) and return ($retval,$retcard);
1233 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1235 $sth->execute($userid);
1237 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1238 $surname, $branchcode, $flags )
1240 if ( md5_base64($password) eq $md5password ) {
1242 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1243 $firstname, $surname, $branchcode, $flags );
1244 return 1, $cardnumber;
1249 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1251 $sth->execute($userid);
1253 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1254 $surname, $branchcode, $flags )
1256 if ( md5_base64($password) eq $md5password ) {
1258 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1259 $firstname, $surname, $branchcode, $flags );
1263 if ( $userid && $userid eq C4::Context->config('user')
1264 && "$password" eq C4::Context->config('pass') )
1267 # Koha superuser account
1268 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1271 if ( $userid && $userid eq 'demo'
1272 && "$password" eq 'demo'
1273 && C4::Context->config('demo') )
1276 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1277 # some features won't be effective : modify systempref, modify MARC structure,
1285 my $authflags = getuserflags($flags, $userid, [$dbh]);
1287 Translates integer flags into permissions strings hash.
1289 C<$flags> is the integer userflags value ( borrowers.userflags )
1290 C<$userid> is the members.userid, used for building subpermissions
1291 C<$authflags> is a hashref of permissions
1298 my $dbh = @_ ? shift : C4::Context->dbh;
1300 $flags = 0 unless $flags;
1301 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1304 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1305 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1306 $userflags->{$flag} = 1;
1309 $userflags->{$flag} = 0;
1313 # get subpermissions and merge with top-level permissions
1314 my $user_subperms = get_user_subpermissions($userid);
1315 foreach my $module (keys %$user_subperms) {
1316 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1317 $userflags->{$module} = $user_subperms->{$module};
1323 =item get_user_subpermissions
1327 my $user_perm_hashref = get_user_subpermissions($userid);
1331 Given the userid (note, not the borrowernumber) of a staff user,
1332 return a hashref of hashrefs of the specific subpermissions
1333 accorded to the user. An example return is
1337 export_catalog => 1,
1338 import_patrons => 1,
1342 The top-level hash-key is a module or function code from
1343 userflags.flag, while the second-level key is a code
1346 The results of this function do not give a complete picture
1347 of the functions that a staff user can access; it is also
1348 necessary to check borrowers.flags.
1352 sub get_user_subpermissions {
1355 my $dbh = C4::Context->dbh;
1356 my $sth = $dbh->prepare("SELECT flag, user_permissions.code as code
1357 FROM user_permissions
1358 JOIN permissions USING (module_bit, code)
1359 JOIN userflags ON (permissions.module_bit = userflags.bit)
1360 JOIN borrowers ON (user_permissions.borrowernumber=borrowers.borrowernumber)
1362 $sth->execute($userid);
1364 my $user_perms = {};
1365 while (my $perm = $sth->fetchrow_hashref) {
1366 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1371 =item get_all_subpermissions
1375 my $perm_hashref = get_all_subpermissions();
1379 Returns a hashref of hashrefs defining all specific
1380 permissions currently defined. The return value
1381 has the same structure as that of C<get_user_subpermissions>,
1382 except that the innermost hash value is the description
1383 of the subpermission.
1387 sub get_all_subpermissions {
1388 my $dbh = C4::Context->dbh;
1389 my $sth = $dbh->prepare("SELECT flag, code, description
1391 JOIN userflags ON (module_bit = bit)");
1395 while (my $perm = $sth->fetchrow_hashref) {
1396 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1403 $flags = ($userid, $flagsrequired);
1405 C<$userid> the userid of the member
1406 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1408 Returns member's flags or 0 if a permission is not met.
1413 my ($userid, $flagsrequired) = @_;
1414 my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1415 $sth->execute($userid);
1416 my $flags = getuserflags( $sth->fetchrow(), $userid );
1417 if ( $userid eq C4::Context->config('user') ) {
1418 # Super User Account from /etc/koha.conf
1419 $flags->{'superlibrarian'} = 1;
1421 elsif ( $userid eq 'demo' && C4::Context->config('demo') ) {
1422 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1423 $flags->{'superlibrarian'} = 1;
1425 return $flags if $flags->{superlibrarian};
1426 foreach my $module ( keys %$flagsrequired ) {
1427 if (C4::Context->preference('GranularPermissions')) {
1428 my $subperm = $flagsrequired->{$module};
1429 if ($subperm eq '*') {
1430 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1432 return 0 unless ( $flags->{$module} == 1 or
1433 ( ref($flags->{$module}) and
1434 exists $flags->{$module}->{$subperm} and
1435 $flags->{$module}->{$subperm} == 1
1440 return 0 unless ( $flags->{$module} );
1444 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1448 sub getborrowernumber {
1450 my $dbh = C4::Context->dbh;
1451 for my $field ( 'userid', 'cardnumber' ) {
1453 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1454 $sth->execute($userid);
1456 my ($bnumber) = $sth->fetchrow;
1463 END { } # module clean-up code here (global destructor)