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->{'branch'} : '',
345 opacbookbag => "" . C4::Context->preference("opacbookbag"),
346 opaccolorstylesheet => "". C4::Context->preference("opaccolorstylesheet"),
347 opaccredits => "" . C4::Context->preference("opaccredits"),
348 opacheader => "" . C4::Context->preference("opacheader"),
349 opaclanguagesdisplay => "". C4::Context->preference("opaclanguagesdisplay"),
350 opaclayoutstylesheet => "". C4::Context->preference("opaclayoutstylesheet"),
351 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
352 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
353 opacuserjs => C4::Context->preference("opacuserjs"),
354 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
355 reviewson => C4::Context->preference("reviewson"),
356 suggestion => "" . C4::Context->preference("suggestion"),
357 virtualshelves => "" . C4::Context->preference("virtualshelves"),
360 return ( $template, $borrowernumber, $cookie, $flags);
365 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
367 Verifies that the user is authorized to run this script. If
368 the user is authorized, a (userid, cookie, session-id, flags)
369 quadruple is returned. If the user is not authorized due to
370 insufficent privileges (see $flagsrequired below), it
371 displays an error page and exits. Otherwise, it displays the
372 login page and exits.
374 Note that C<&checkauth> will return if and only if the user
375 is authorized, so it should be called early on, before any
376 unfinished operations (e.g., if you've opened a file, then
377 C<&checkauth> won't close it for you).
379 C<$query> is the CGI object for the script calling C<&checkauth>.
381 The C<$noauth> argument is optional. If it is set, then no
382 authorization is required for the script.
384 C<&checkauth> fetches user and session information from C<$query> and
385 ensures that the user is authorized to run scripts that require
388 The C<$flagsrequired> argument specifies the required privileges
389 the user must have if the username and password are correct.
390 It should be specified as a reference-to-hash; keys in the hash
391 should be the "flags" for the user, as specified in the Members
392 intranet module. Any key specified must correspond to a "flag"
393 in the userflags table. E.g., { circulate => 1 } would specify
394 that the user must have the "circulate" privilege in order to
395 proceed. To make sure that access control is correct, the
396 C<$flagsrequired> parameter must be specified correctly.
398 If the GranularPermissions system preference is ON, the
399 value of each key in the C<flagsrequired> hash takes on an additional
404 The user must have access to all subfunctions of the module
405 specified by the hash key.
409 The user must have access to at least one subfunction of the module
410 specified by the hash key.
412 =item specific permission, e.g., 'export_catalog'
414 The user must have access to the specific subfunction list, which
415 must correspond to a row in the permissions table.
417 The C<$type> argument specifies whether the template should be
418 retrieved from the opac or intranet directory tree. "opac" is
419 assumed if it is not specified; however, if C<$type> is specified,
420 "intranet" is assumed if it is not "opac".
422 If C<$query> does not have a valid session ID associated with it
423 (i.e., the user has not logged in) or if the session has expired,
424 C<&checkauth> presents the user with a login page (from the point of
425 view of the original script, C<&checkauth> does not return). Once the
426 user has authenticated, C<&checkauth> restarts the original script
427 (this time, C<&checkauth> returns).
429 The login page is provided using a HTML::Template, which is set in the
430 systempreferences table or at the top of this file. The variable C<$type>
431 selects which template to use, either the opac or the intranet
432 authentification template.
434 C<&checkauth> returns a user ID, a cookie, and a session ID. The
435 cookie should be sent back to the browser; it verifies that the user
440 sub _version_check ($$) {
444 # If Version syspref is unavailable, it means Koha is beeing installed,
445 # and so we must redirect to OPAC maintenance page or to the WebInstaller
446 # also, if OpacMaintenance is ON, OPAC should redirect to maintenance
447 if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') {
448 warn "OPAC Install required, redirecting to maintenance";
449 print $query->redirect("/cgi-bin/koha/maintenance.pl");
451 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
452 if ($type ne 'opac') {
453 warn "Install required, redirecting to Installer";
454 print $query->redirect("/cgi-bin/koha/installer/install.pl");
457 warn "OPAC Install required, redirecting to maintenance";
458 print $query->redirect("/cgi-bin/koha/maintenance.pl");
463 # check that database and koha version are the same
464 # there is no DB version, it's a fresh install,
465 # go to web installer
466 # there is a DB version, compare it to the code version
467 my $kohaversion=C4::Context::KOHAVERSION;
468 # remove the 3 last . to have a Perl number
469 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
470 $debug and print STDERR "kohaversion : $kohaversion\n";
471 if ($version < $kohaversion){
472 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
473 if ($type ne 'opac'){
474 warn sprintf($warning, 'Installer');
475 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
477 warn sprintf("OPAC: " . $warning, 'maintenance');
478 print $query->redirect("/cgi-bin/koha/maintenance.pl");
486 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
487 printf L join("\n",@_);
493 $debug and warn "Checking Auth";
494 # $authnotrequired will be set for scripts which will run without authentication
495 my $authnotrequired = shift;
496 my $flagsrequired = shift;
498 $type = 'opac' unless $type;
500 my $dbh = C4::Context->dbh;
501 my $timeout = C4::Context->preference('timeout');
503 if ($timeout =~ /(\d+)[dD]/) {
504 $timeout = $1 * 86400;
506 $timeout = 600 unless $timeout;
508 _version_check($type,$query);
512 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
513 my $logout = $query->param('logout.x');
515 if ( $userid = $ENV{'REMOTE_USER'} ) {
516 # Using Basic Authentication, no cookies required
517 $cookie = $query->cookie(
518 -name => 'CGISESSID',
524 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
525 my $session = get_session($sessionID);
526 C4::Context->_new_userenv($sessionID);
527 my ($ip, $lasttime, $sessiontype);
529 C4::Context::set_userenv(
530 $session->param('number'), $session->param('id'),
531 $session->param('cardnumber'), $session->param('firstname'),
532 $session->param('surname'), $session->param('branch'),
533 $session->param('branchname'), $session->param('flags'),
534 $session->param('emailaddress'), $session->param('branchprinter')
536 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
537 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
538 C4::Context::set_shelves_userenv('tot',$session->param('totshelves'));
539 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
540 $ip = $session->param('ip');
541 $lasttime = $session->param('lasttime');
542 $userid = $session->param('id');
543 $sessiontype = $session->param('sessiontype');
546 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
547 #if a user enters an id ne to the id in the current session, we need to log them in...
548 #first we need to clear the anonymous session...
549 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
552 C4::Context->_unset_userenv($sessionID);
557 # voluntary logout the user
560 C4::Context->_unset_userenv($sessionID);
561 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,localtime);
565 elsif ( $lasttime < time() - $timeout ) {
567 $info{'timed_out'} = 1;
569 C4::Context->_unset_userenv($sessionID);
570 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,localtime);
574 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
575 # Different ip than originally logged in from
576 $info{'oldip'} = $ip;
577 $info{'newip'} = $ENV{'REMOTE_ADDR'};
578 $info{'different_ip'} = 1;
580 C4::Context->_unset_userenv($sessionID);
581 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,localtime, $info{'newip'});
586 $cookie = $query->cookie( CGISESSID => $session->id );
587 $session->param('lasttime',time());
588 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...
589 $flags = haspermission($userid, $flagsrequired);
593 $info{'nopermission'} = 1;
598 unless ($userid || $sessionID) {
599 #we initiate a session prior to checking for a username to allow for anonymous sessions...
600 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
601 my $sessionID = $session->id;
602 C4::Context->_new_userenv($sessionID);
603 $cookie = $query->cookie(CGISESSID => $sessionID);
604 if ( $userid = $query->param('userid') ) {
605 my $password = $query->param('password');
606 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
608 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},localtime);
609 if ( $flags = haspermission($userid, $flagsrequired) ) {
613 $info{'nopermission'} = 1;
614 C4::Context->_unset_userenv($sessionID);
617 my ($borrowernumber, $firstname, $surname, $userflags,
618 $branchcode, $branchname, $branchprinter, $emailaddress);
620 if ( $return == 1 ) {
622 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
623 branches.branchname as branchname,
624 branches.branchprinter as branchprinter,
627 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
629 my $sth = $dbh->prepare("$select where userid=?");
630 $sth->execute($userid);
631 unless ($sth->rows) {
632 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
633 $sth = $dbh->prepare("$select where cardnumber=?");
634 $sth->execute($cardnumber);
635 unless ($sth->rows) {
636 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
637 $sth->execute($userid);
638 unless ($sth->rows) {
639 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
644 ($borrowernumber, $firstname, $surname, $userflags,
645 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
646 $debug and print STDERR "AUTH_3 results: " .
647 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
649 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
651 # launch a sequence to check if we have a ip for the branch, i
652 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
654 my $ip = $ENV{'REMOTE_ADDR'};
655 # if they specify at login, use that
656 if ($query->param('branch')) {
657 $branchcode = $query->param('branch');
658 $branchname = GetBranchName($branchcode);
660 my $branches = GetBranches();
661 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
662 # we have to check they are coming from the right ip range
663 my $domain = $branches->{$branchcode}->{'branchip'};
664 if ($ip !~ /^$domain/){
666 $info{'wrongip'} = 1;
671 foreach my $br ( keys %$branches ) {
672 # now we work with the treatment of ip
673 my $domain = $branches->{$br}->{'branchip'};
674 if ( $domain && $ip =~ /^$domain/ ) {
675 $branchcode = $branches->{$br}->{'branchcode'};
677 # new op dev : add the branchprinter and branchname in the cookie
678 $branchprinter = $branches->{$br}->{'branchprinter'};
679 $branchname = $branches->{$br}->{'branchname'};
682 $session->param('number',$borrowernumber);
683 $session->param('id',$userid);
684 $session->param('cardnumber',$cardnumber);
685 $session->param('firstname',$firstname);
686 $session->param('surname',$surname);
687 $session->param('branch',$branchcode);
688 $session->param('branchname',$branchname);
689 $session->param('flags',$userflags);
690 $session->param('emailaddress',$emailaddress);
691 $session->param('ip',$session->remote_addr());
692 $session->param('lasttime',time());
693 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
695 elsif ( $return == 2 ) {
696 #We suppose the user is the superlibrarian
698 $session->param('number',0);
699 $session->param('id',C4::Context->config('user'));
700 $session->param('cardnumber',C4::Context->config('user'));
701 $session->param('firstname',C4::Context->config('user'));
702 $session->param('surname',C4::Context->config('user'));
703 $session->param('branch','NO_LIBRARY_SET');
704 $session->param('branchname','NO_LIBRARY_SET');
705 $session->param('flags',1);
706 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
707 $session->param('ip',$session->remote_addr());
708 $session->param('lasttime',time());
710 C4::Context::set_userenv(
711 $session->param('number'), $session->param('id'),
712 $session->param('cardnumber'), $session->param('firstname'),
713 $session->param('surname'), $session->param('branch'),
714 $session->param('branchname'), $session->param('flags'),
715 $session->param('emailaddress'), $session->param('branchprinter')
718 # Grab borrower's shelves and public shelves and add them to the session
719 # $row_count determines how many records are returned from the db query
720 # and the number of lists to be displayed of each type in the 'Lists' button drop down
721 my $row_count = 10; # FIXME:This probably should be a syspref
722 my ($total, $totshelves, $barshelves, $pubshelves);
723 ($barshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(1, $row_count, $borrowernumber);
724 $total->{'bartotal'} = $totshelves;
725 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
726 $total->{'pubtotal'} = $totshelves;
727 $session->param('barshelves', $barshelves->[0]);
728 $session->param('pubshelves', $pubshelves->[0]);
729 $session->param('totshelves', $total);
731 C4::Context::set_shelves_userenv('bar',$barshelves->[0]);
732 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
733 C4::Context::set_shelves_userenv('tot',$total);
737 $info{'invalid_username_or_password'} = 1;
738 C4::Context->_unset_userenv($sessionID);
741 } # END if ( $userid = $query->param('userid') )
742 elsif ($type eq "opac") {
743 # if we are here this is an anonymous session; add public lists to it and a few other items...
744 # anonymous sessions are created only for the OPAC
745 $debug and warn "Initiating an anonymous session...";
747 # Grab the public shelves and add to the session...
748 my $row_count = 20; # FIXME:This probably should be a syspref
749 my ($total, $totshelves, $pubshelves);
750 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
751 $total->{'pubtotal'} = $totshelves;
752 $session->param('pubshelves', $pubshelves->[0]);
753 $session->param('totshelves', $total);
754 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
755 C4::Context::set_shelves_userenv('tot',$total);
757 # setting a couple of other session vars...
758 $session->param('ip',$session->remote_addr());
759 $session->param('lasttime',time());
760 $session->param('sessiontype','anon');
762 } # END unless ($userid)
763 my $insecure = C4::Context->boolean_preference('insecure');
765 # finished authentification, now respond
766 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
770 $cookie = $query->cookie( CGISESSID => '' );
772 return ( $userid, $cookie, $sessionID, $flags );
777 # AUTH rejected, show the login/password template, after checking the DB.
781 # get the inputs from the incoming query
783 foreach my $name ( param $query) {
784 (next) if ( $name eq 'userid' || $name eq 'password' );
785 my $value = $query->param($name);
786 push @inputs, { name => $name, value => $value };
788 # get the branchloop, which we need for authentication
789 my $branches = GetBranches();
791 for my $branch_hash (sort keys %$branches) {
792 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
795 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
796 my $template = gettemplate( $template_name, $type, $query );
797 $template->param(branchloop => \@branch_loop,);
801 suggestion => C4::Context->preference("suggestion"),
802 virtualshelves => C4::Context->preference("virtualshelves"),
803 LibraryName => C4::Context->preference("LibraryName"),
804 opacuserlogin => C4::Context->preference("opacuserlogin"),
805 OpacNav => C4::Context->preference("OpacNav"),
806 opaccredits => C4::Context->preference("opaccredits"),
807 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
808 opacsmallimage => C4::Context->preference("opacsmallimage"),
809 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
810 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
811 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
812 opacuserjs => C4::Context->preference("opacuserjs"),
813 opacbookbag => "" . C4::Context->preference("opacbookbag"),
814 OpacCloud => C4::Context->preference("OpacCloud"),
815 OpacTopissue => C4::Context->preference("OpacTopissue"),
816 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
817 OpacBrowser => C4::Context->preference("OpacBrowser"),
818 opacheader => C4::Context->preference("opacheader"),
819 TagsEnabled => C4::Context->preference("TagsEnabled"),
820 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
821 intranetcolorstylesheet =>
822 C4::Context->preference("intranetcolorstylesheet"),
823 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
824 IntranetNav => C4::Context->preference("IntranetNav"),
825 intranetuserjs => C4::Context->preference("intranetuserjs"),
826 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
827 IndependantBranches=> C4::Context->preference("IndependantBranches"),
828 AutoLocation => C4::Context->preference("AutoLocation"),
829 wrongip => $info{'wrongip'}
832 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
834 my $self_url = $query->url( -absolute => 1 );
837 LibraryName => C4::Context->preference("LibraryName"),
839 $template->param( \%info );
840 # $cookie = $query->cookie(CGISESSID => $session->id
842 print $query->header(
843 -type => 'text/html',
853 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
855 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
856 cookie, determine if the user has the privileges specified by C<$userflags>.
858 C<check_api_auth> is is meant for authenticating users of web services, and
859 consequently will always return and will not attempt to redirect the user
862 If a valid session cookie is already present, check_api_auth will return a status
863 of "ok", the cookie, and the Koha session ID.
865 If no session cookie is present, check_api_auth will check the 'userid' and 'password
866 parameters and create a session cookie and Koha session if the supplied credentials
869 Possible return values in C<$status> are:
873 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
875 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
877 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
879 =item "expired -- session cookie has expired; API user should resubmit userid and password
887 my $flagsrequired = shift;
889 my $dbh = C4::Context->dbh;
890 my $timeout = C4::Context->preference('timeout');
891 $timeout = 600 unless $timeout;
893 unless (C4::Context->preference('Version')) {
894 # database has not been installed yet
895 return ("maintenance", undef, undef);
897 my $kohaversion=C4::Context::KOHAVERSION;
898 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
899 if (C4::Context->preference('Version') < $kohaversion) {
900 # database in need of version update; assume that
901 # no API should be called while databsae is in
903 return ("maintenance", undef, undef);
906 # FIXME -- most of what follows is a copy-and-paste
907 # of code from checkauth. There is an obvious need
908 # for refactoring to separate the various parts of
909 # the authentication code, but as of 2007-11-19 this
910 # is deferred so as to not introduce bugs into the
911 # regular authentication code for Koha 3.0.
913 # see if we have a valid session cookie already
914 # however, if a userid parameter is present (i.e., from
915 # a form submission, assume that any current cookie
917 my $sessionID = undef;
918 unless ($query->param('userid')) {
919 $sessionID = $query->cookie("CGISESSID");
922 my $session = get_session($sessionID);
923 C4::Context->_new_userenv($sessionID);
925 C4::Context::set_userenv(
926 $session->param('number'), $session->param('id'),
927 $session->param('cardnumber'), $session->param('firstname'),
928 $session->param('surname'), $session->param('branch'),
929 $session->param('branchname'), $session->param('flags'),
930 $session->param('emailaddress'), $session->param('branchprinter')
933 my $ip = $session->param('ip');
934 my $lasttime = $session->param('lasttime');
935 my $userid = $session->param('id');
936 if ( $lasttime < time() - $timeout ) {
939 C4::Context->_unset_userenv($sessionID);
942 return ("expired", undef, undef);
943 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
946 C4::Context->_unset_userenv($sessionID);
949 return ("expired", undef, undef);
951 my $cookie = $query->cookie( CGISESSID => $session->id );
952 $session->param('lasttime',time());
953 my $flags = haspermission($userid, $flagsrequired);
955 return ("ok", $cookie, $sessionID);
958 C4::Context->_unset_userenv($sessionID);
961 return ("failed", undef, undef);
965 return ("expired", undef, undef);
969 my $userid = $query->param('userid');
970 my $password = $query->param('password');
971 unless ($userid and $password) {
972 # caller did something wrong, fail the authenticateion
973 return ("failed", undef, undef);
975 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
976 if ($return and haspermission($userid, $flagsrequired)) {
977 my $session = get_session("");
978 return ("failed", undef, undef) unless $session;
980 my $sessionID = $session->id;
981 C4::Context->_new_userenv($sessionID);
982 my $cookie = $query->cookie(CGISESSID => $sessionID);
983 if ( $return == 1 ) {
985 $borrowernumber, $firstname, $surname,
986 $userflags, $branchcode, $branchname,
987 $branchprinter, $emailaddress
991 "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=?"
993 $sth->execute($userid);
995 $borrowernumber, $firstname, $surname,
996 $userflags, $branchcode, $branchname,
997 $branchprinter, $emailaddress
998 ) = $sth->fetchrow if ( $sth->rows );
1000 unless ($sth->rows ) {
1001 my $sth = $dbh->prepare(
1002 "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=?"
1004 $sth->execute($cardnumber);
1006 $borrowernumber, $firstname, $surname,
1007 $userflags, $branchcode, $branchname,
1008 $branchprinter, $emailaddress
1009 ) = $sth->fetchrow if ( $sth->rows );
1011 unless ( $sth->rows ) {
1012 $sth->execute($userid);
1014 $borrowernumber, $firstname, $surname, $userflags,
1015 $branchcode, $branchname, $branchprinter, $emailaddress
1016 ) = $sth->fetchrow if ( $sth->rows );
1020 my $ip = $ENV{'REMOTE_ADDR'};
1021 # if they specify at login, use that
1022 if ($query->param('branch')) {
1023 $branchcode = $query->param('branch');
1024 $branchname = GetBranchName($branchcode);
1026 my $branches = GetBranches();
1028 foreach my $br ( keys %$branches ) {
1029 # now we work with the treatment of ip
1030 my $domain = $branches->{$br}->{'branchip'};
1031 if ( $domain && $ip =~ /^$domain/ ) {
1032 $branchcode = $branches->{$br}->{'branchcode'};
1034 # new op dev : add the branchprinter and branchname in the cookie
1035 $branchprinter = $branches->{$br}->{'branchprinter'};
1036 $branchname = $branches->{$br}->{'branchname'};
1039 $session->param('number',$borrowernumber);
1040 $session->param('id',$userid);
1041 $session->param('cardnumber',$cardnumber);
1042 $session->param('firstname',$firstname);
1043 $session->param('surname',$surname);
1044 $session->param('branch',$branchcode);
1045 $session->param('branchname',$branchname);
1046 $session->param('flags',$userflags);
1047 $session->param('emailaddress',$emailaddress);
1048 $session->param('ip',$session->remote_addr());
1049 $session->param('lasttime',time());
1050 } elsif ( $return == 2 ) {
1051 #We suppose the user is the superlibrarian
1052 $session->param('number',0);
1053 $session->param('id',C4::Context->config('user'));
1054 $session->param('cardnumber',C4::Context->config('user'));
1055 $session->param('firstname',C4::Context->config('user'));
1056 $session->param('surname',C4::Context->config('user'));
1057 $session->param('branch','NO_LIBRARY_SET');
1058 $session->param('branchname','NO_LIBRARY_SET');
1059 $session->param('flags',1);
1060 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1061 $session->param('ip',$session->remote_addr());
1062 $session->param('lasttime',time());
1064 C4::Context::set_userenv(
1065 $session->param('number'), $session->param('id'),
1066 $session->param('cardnumber'), $session->param('firstname'),
1067 $session->param('surname'), $session->param('branch'),
1068 $session->param('branchname'), $session->param('flags'),
1069 $session->param('emailaddress'), $session->param('branchprinter')
1071 return ("ok", $cookie, $sessionID);
1073 return ("failed", undef, undef);
1078 =item check_cookie_auth
1080 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1082 Given a CGISESSID cookie set during a previous login to Koha, determine
1083 if the user has the privileges specified by C<$userflags>.
1085 C<check_cookie_auth> is meant for authenticating special services
1086 such as tools/upload-file.pl that are invoked by other pages that
1087 have been authenticated in the usual way.
1089 Possible return values in C<$status> are:
1093 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1095 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1097 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1099 =item "expired -- session cookie has expired; API user should resubmit userid and password
1105 sub check_cookie_auth {
1107 my $flagsrequired = shift;
1109 my $dbh = C4::Context->dbh;
1110 my $timeout = C4::Context->preference('timeout');
1111 $timeout = 600 unless $timeout;
1113 unless (C4::Context->preference('Version')) {
1114 # database has not been installed yet
1115 return ("maintenance", undef);
1117 my $kohaversion=C4::Context::KOHAVERSION;
1118 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1119 if (C4::Context->preference('Version') < $kohaversion) {
1120 # database in need of version update; assume that
1121 # no API should be called while databsae is in
1123 return ("maintenance", undef);
1126 # FIXME -- most of what follows is a copy-and-paste
1127 # of code from checkauth. There is an obvious need
1128 # for refactoring to separate the various parts of
1129 # the authentication code, but as of 2007-11-23 this
1130 # is deferred so as to not introduce bugs into the
1131 # regular authentication code for Koha 3.0.
1133 # see if we have a valid session cookie already
1134 # however, if a userid parameter is present (i.e., from
1135 # a form submission, assume that any current cookie
1137 unless (defined $cookie and $cookie) {
1138 return ("failed", undef);
1140 my $sessionID = $cookie;
1141 my $session = get_session($sessionID);
1142 C4::Context->_new_userenv($sessionID);
1144 C4::Context::set_userenv(
1145 $session->param('number'), $session->param('id'),
1146 $session->param('cardnumber'), $session->param('firstname'),
1147 $session->param('surname'), $session->param('branch'),
1148 $session->param('branchname'), $session->param('flags'),
1149 $session->param('emailaddress'), $session->param('branchprinter')
1152 my $ip = $session->param('ip');
1153 my $lasttime = $session->param('lasttime');
1154 my $userid = $session->param('id');
1155 if ( $lasttime < time() - $timeout ) {
1158 C4::Context->_unset_userenv($sessionID);
1161 return ("expired", undef);
1162 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1163 # IP address changed
1165 C4::Context->_unset_userenv($sessionID);
1168 return ("expired", undef);
1170 $session->param('lasttime',time());
1171 my $flags = haspermission($userid, $flagsrequired);
1173 return ("ok", $sessionID);
1176 C4::Context->_unset_userenv($sessionID);
1179 return ("failed", undef);
1183 return ("expired", undef);
1190 my $session = get_session($sessionID);
1192 Given a session ID, retrieve the CGI::Session object used to store
1193 the session's state. The session object can be used to store
1194 data that needs to be accessed by different scripts during a
1197 If the C<$sessionID> parameter is an empty string, a new session
1203 my $sessionID = shift;
1204 my $storage_method = C4::Context->preference('SessionStorage');
1205 my $dbh = C4::Context->dbh;
1207 if ($storage_method eq 'mysql'){
1208 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1210 elsif ($storage_method eq 'Pg') {
1211 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1214 # catch all defaults to tmp should work on all systems
1215 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1222 my ( $dbh, $userid, $password ) = @_;
1224 $debug and print STDERR "## checkpw - checking LDAP\n";
1225 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1226 ($retval) and return ($retval,$retcard);
1232 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1234 $sth->execute($userid);
1236 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1237 $surname, $branchcode, $flags )
1239 if ( md5_base64($password) eq $md5password ) {
1241 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1242 $firstname, $surname, $branchcode, $flags );
1243 return 1, $cardnumber;
1248 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1250 $sth->execute($userid);
1252 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1253 $surname, $branchcode, $flags )
1255 if ( md5_base64($password) eq $md5password ) {
1257 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1258 $firstname, $surname, $branchcode, $flags );
1262 if ( $userid && $userid eq C4::Context->config('user')
1263 && "$password" eq C4::Context->config('pass') )
1266 # Koha superuser account
1267 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1270 if ( $userid && $userid eq 'demo'
1271 && "$password" eq 'demo'
1272 && C4::Context->config('demo') )
1275 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1276 # some features won't be effective : modify systempref, modify MARC structure,
1284 my $authflags = getuserflags($flags, $userid, [$dbh]);
1286 Translates integer flags into permissions strings hash.
1288 C<$flags> is the integer userflags value ( borrowers.userflags )
1289 C<$userid> is the members.userid, used for building subpermissions
1290 C<$authflags> is a hashref of permissions
1297 my $dbh = @_ ? shift : C4::Context->dbh;
1299 $flags = 0 unless $flags;
1300 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1303 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1304 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1305 $userflags->{$flag} = 1;
1308 $userflags->{$flag} = 0;
1312 # get subpermissions and merge with top-level permissions
1313 my $user_subperms = get_user_subpermissions($userid);
1314 foreach my $module (keys %$user_subperms) {
1315 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1316 $userflags->{$module} = $user_subperms->{$module};
1322 =item get_user_subpermissions
1326 my $user_perm_hashref = get_user_subpermissions($userid);
1330 Given the userid (note, not the borrowernumber) of a staff user,
1331 return a hashref of hashrefs of the specific subpermissions
1332 accorded to the user. An example return is
1336 export_catalog => 1,
1337 import_patrons => 1,
1341 The top-level hash-key is a module or function code from
1342 userflags.flag, while the second-level key is a code
1345 The results of this function do not give a complete picture
1346 of the functions that a staff user can access; it is also
1347 necessary to check borrowers.flags.
1351 sub get_user_subpermissions {
1354 my $dbh = C4::Context->dbh;
1355 my $sth = $dbh->prepare("SELECT flag, user_permissions.code as code
1356 FROM user_permissions
1357 JOIN permissions USING (module_bit, code)
1358 JOIN userflags ON (permissions.module_bit = userflags.bit)
1359 JOIN borrowers ON (user_permissions.borrowernumber=borrowers.borrowernumber)
1361 $sth->execute($userid);
1363 my $user_perms = {};
1364 while (my $perm = $sth->fetchrow_hashref) {
1365 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1370 =item get_all_subpermissions
1374 my $perm_hashref = get_all_subpermissions();
1378 Returns a hashref of hashrefs defining all specific
1379 permissions currently defined. The return value
1380 has the same structure as that of C<get_user_subpermissions>,
1381 except that the innermost hash value is the description
1382 of the subpermission.
1386 sub get_all_subpermissions {
1387 my $dbh = C4::Context->dbh;
1388 my $sth = $dbh->prepare("SELECT flag, code, description
1390 JOIN userflags ON (module_bit = bit)");
1394 while (my $perm = $sth->fetchrow_hashref) {
1395 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1402 $flags = ($userid, $flagsrequired);
1404 C<$userid> the userid of the member
1405 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1407 Returns member's flags or 0 if a permission is not met.
1412 my ($userid, $flagsrequired) = @_;
1413 my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1414 $sth->execute($userid);
1415 my $flags = getuserflags( $sth->fetchrow(), $userid );
1416 if ( $userid eq C4::Context->config('user') ) {
1417 # Super User Account from /etc/koha.conf
1418 $flags->{'superlibrarian'} = 1;
1420 elsif ( $userid eq 'demo' && C4::Context->config('demo') ) {
1421 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1422 $flags->{'superlibrarian'} = 1;
1424 return $flags if $flags->{superlibrarian};
1425 foreach my $module ( keys %$flagsrequired ) {
1426 if (C4::Context->preference('GranularPermissions')) {
1427 my $subperm = $flagsrequired->{$module};
1428 if ($subperm eq '*') {
1429 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1431 return 0 unless ( $flags->{$module} == 1 or
1432 ( ref($flags->{$module}) and
1433 exists $flags->{$module}->{$subperm} and
1434 $flags->{$module}->{$subperm} == 1
1439 return 0 unless ( $flags->{$module} );
1443 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1447 sub getborrowernumber {
1449 my $dbh = C4::Context->dbh;
1450 for my $field ( 'userid', 'cardnumber' ) {
1452 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1453 $sth->execute($userid);
1455 my ($bnumber) = $sth->fetchrow;
1462 END { } # module clean-up code here (global destructor)