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 OPACAmazonCoverImages => "" . C4::Context->preference("OPACAmazonCoverImages"),
314 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
315 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
316 LibraryName => "" . C4::Context->preference("LibraryName"),
317 LibraryNameTitle => "" . $LibraryNameTitle,
318 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
319 OPACAmazonSimilarItems => "" . C4::Context->preference("OPACAmazonSimilarItems"),
320 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
321 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
322 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
323 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
324 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
325 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
326 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
327 OPACBaseURL => ($in->{'query'}->https() ? "https://" : "http://") . $ENV{'SERVER_NAME'} .
328 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
329 opac_name => $opac_name,
330 opac_css_override => $ENV{'OPAC_CSS_OVERRIDE'},
331 opac_search_limit => $opac_search_limit,
332 opac_limit_override => $opac_limit_override,
333 OpacBrowser => C4::Context->preference("OpacBrowser"),
334 OpacCloud => C4::Context->preference("OpacCloud"),
335 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
336 OpacNav => "" . C4::Context->preference("OpacNav"),
337 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
338 OpacTopissue => C4::Context->preference("OpacTopissue"),
339 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
340 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
341 'Version' => C4::Context->preference('Version'),
342 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
343 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
344 hidelostitems => C4::Context->preference("hidelostitems"),
345 mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '',
346 opaclayoutstylesheet => "" . C4::Context->preference("opaclayoutstylesheet"),
347 opaccolorstylesheet => "" . C4::Context->preference("opaccolorstylesheet"),
348 opacstylesheet => "" . C4::Context->preference("opacstylesheet"),
349 opacbookbag => "" . C4::Context->preference("opacbookbag"),
350 opaccredits => "" . C4::Context->preference("opaccredits"),
351 opacheader => "" . C4::Context->preference("opacheader"),
352 opaclanguagesdisplay => "" . C4::Context->preference("opaclanguagesdisplay"),
353 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
354 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
355 opacuserjs => C4::Context->preference("opacuserjs"),
356 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
357 reviewson => C4::Context->preference("reviewson"),
358 suggestion => "" . C4::Context->preference("suggestion"),
359 virtualshelves => "" . C4::Context->preference("virtualshelves"),
362 return ( $template, $borrowernumber, $cookie, $flags);
367 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
369 Verifies that the user is authorized to run this script. If
370 the user is authorized, a (userid, cookie, session-id, flags)
371 quadruple is returned. If the user is not authorized due to
372 insufficent privileges (see $flagsrequired below), it
373 displays an error page and exits. Otherwise, it displays the
374 login page and exits.
376 Note that C<&checkauth> will return if and only if the user
377 is authorized, so it should be called early on, before any
378 unfinished operations (e.g., if you've opened a file, then
379 C<&checkauth> won't close it for you).
381 C<$query> is the CGI object for the script calling C<&checkauth>.
383 The C<$noauth> argument is optional. If it is set, then no
384 authorization is required for the script.
386 C<&checkauth> fetches user and session information from C<$query> and
387 ensures that the user is authorized to run scripts that require
390 The C<$flagsrequired> argument specifies the required privileges
391 the user must have if the username and password are correct.
392 It should be specified as a reference-to-hash; keys in the hash
393 should be the "flags" for the user, as specified in the Members
394 intranet module. Any key specified must correspond to a "flag"
395 in the userflags table. E.g., { circulate => 1 } would specify
396 that the user must have the "circulate" privilege in order to
397 proceed. To make sure that access control is correct, the
398 C<$flagsrequired> parameter must be specified correctly.
400 If the GranularPermissions system preference is ON, the
401 value of each key in the C<flagsrequired> hash takes on an additional
406 The user must have access to all subfunctions of the module
407 specified by the hash key.
411 The user must have access to at least one subfunction of the module
412 specified by the hash key.
414 =item specific permission, e.g., 'export_catalog'
416 The user must have access to the specific subfunction list, which
417 must correspond to a row in the permissions table.
419 The C<$type> argument specifies whether the template should be
420 retrieved from the opac or intranet directory tree. "opac" is
421 assumed if it is not specified; however, if C<$type> is specified,
422 "intranet" is assumed if it is not "opac".
424 If C<$query> does not have a valid session ID associated with it
425 (i.e., the user has not logged in) or if the session has expired,
426 C<&checkauth> presents the user with a login page (from the point of
427 view of the original script, C<&checkauth> does not return). Once the
428 user has authenticated, C<&checkauth> restarts the original script
429 (this time, C<&checkauth> returns).
431 The login page is provided using a HTML::Template, which is set in the
432 systempreferences table or at the top of this file. The variable C<$type>
433 selects which template to use, either the opac or the intranet
434 authentification template.
436 C<&checkauth> returns a user ID, a cookie, and a session ID. The
437 cookie should be sent back to the browser; it verifies that the user
442 sub _version_check ($$) {
446 # If Version syspref is unavailable, it means Koha is beeing installed,
447 # and so we must redirect to OPAC maintenance page or to the WebInstaller
448 # also, if OpacMaintenance is ON, OPAC should redirect to maintenance
449 if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') {
450 warn "OPAC Install required, redirecting to maintenance";
451 print $query->redirect("/cgi-bin/koha/maintenance.pl");
453 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
454 if ($type ne 'opac') {
455 warn "Install required, redirecting to Installer";
456 print $query->redirect("/cgi-bin/koha/installer/install.pl");
459 warn "OPAC Install required, redirecting to maintenance";
460 print $query->redirect("/cgi-bin/koha/maintenance.pl");
465 # check that database and koha version are the same
466 # there is no DB version, it's a fresh install,
467 # go to web installer
468 # there is a DB version, compare it to the code version
469 my $kohaversion=C4::Context::KOHAVERSION;
470 # remove the 3 last . to have a Perl number
471 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
472 $debug and print STDERR "kohaversion : $kohaversion\n";
473 if ($version < $kohaversion){
474 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
475 if ($type ne 'opac'){
476 warn sprintf($warning, 'Installer');
477 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
479 warn sprintf("OPAC: " . $warning, 'maintenance');
480 print $query->redirect("/cgi-bin/koha/maintenance.pl");
488 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
489 printf L join("\n",@_);
495 $debug and warn "Checking Auth";
496 # $authnotrequired will be set for scripts which will run without authentication
497 my $authnotrequired = shift;
498 my $flagsrequired = shift;
500 $type = 'opac' unless $type;
502 my $dbh = C4::Context->dbh;
503 my $timeout = C4::Context->preference('timeout');
505 if ($timeout =~ /(\d+)[dD]/) {
506 $timeout = $1 * 86400;
508 $timeout = 600 unless $timeout;
510 _version_check($type,$query);
514 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
515 my $logout = $query->param('logout.x');
517 if ( $userid = $ENV{'REMOTE_USER'} ) {
518 # Using Basic Authentication, no cookies required
519 $cookie = $query->cookie(
520 -name => 'CGISESSID',
526 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
527 my $session = get_session($sessionID);
528 C4::Context->_new_userenv($sessionID);
529 my ($ip, $lasttime, $sessiontype);
531 C4::Context::set_userenv(
532 $session->param('number'), $session->param('id'),
533 $session->param('cardnumber'), $session->param('firstname'),
534 $session->param('surname'), $session->param('branch'),
535 $session->param('branchname'), $session->param('flags'),
536 $session->param('emailaddress'), $session->param('branchprinter')
538 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
539 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
540 C4::Context::set_shelves_userenv('tot',$session->param('totshelves'));
541 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
542 $ip = $session->param('ip');
543 $lasttime = $session->param('lasttime');
544 $userid = $session->param('id');
545 $sessiontype = $session->param('sessiontype');
548 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
549 #if a user enters an id ne to the id in the current session, we need to log them in...
550 #first we need to clear the anonymous session...
551 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
554 C4::Context->_unset_userenv($sessionID);
559 # voluntary logout the user
562 C4::Context->_unset_userenv($sessionID);
563 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,localtime);
567 elsif ( $lasttime < time() - $timeout ) {
569 $info{'timed_out'} = 1;
571 C4::Context->_unset_userenv($sessionID);
572 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,localtime);
576 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
577 # Different ip than originally logged in from
578 $info{'oldip'} = $ip;
579 $info{'newip'} = $ENV{'REMOTE_ADDR'};
580 $info{'different_ip'} = 1;
582 C4::Context->_unset_userenv($sessionID);
583 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,localtime, $info{'newip'});
588 $cookie = $query->cookie( CGISESSID => $session->id );
589 $session->param('lasttime',time());
590 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...
591 $flags = haspermission($userid, $flagsrequired);
595 $info{'nopermission'} = 1;
600 unless ($userid || $sessionID) {
601 #we initiate a session prior to checking for a username to allow for anonymous sessions...
602 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
603 my $sessionID = $session->id;
604 C4::Context->_new_userenv($sessionID);
605 $cookie = $query->cookie(CGISESSID => $sessionID);
606 if ( $userid = $query->param('userid') ) {
607 my $password = $query->param('password');
608 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
610 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},localtime);
611 if ( $flags = haspermission($userid, $flagsrequired) ) {
615 $info{'nopermission'} = 1;
616 C4::Context->_unset_userenv($sessionID);
619 my ($borrowernumber, $firstname, $surname, $userflags,
620 $branchcode, $branchname, $branchprinter, $emailaddress);
622 if ( $return == 1 ) {
624 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
625 branches.branchname as branchname,
626 branches.branchprinter as branchprinter,
629 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
631 my $sth = $dbh->prepare("$select where userid=?");
632 $sth->execute($userid);
633 unless ($sth->rows) {
634 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
635 $sth = $dbh->prepare("$select where cardnumber=?");
636 $sth->execute($cardnumber);
637 unless ($sth->rows) {
638 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
639 $sth->execute($userid);
640 unless ($sth->rows) {
641 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
646 ($borrowernumber, $firstname, $surname, $userflags,
647 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
648 $debug and print STDERR "AUTH_3 results: " .
649 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
651 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
653 # launch a sequence to check if we have a ip for the branch, i
654 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
656 my $ip = $ENV{'REMOTE_ADDR'};
657 # if they specify at login, use that
658 if ($query->param('branch')) {
659 $branchcode = $query->param('branch');
660 $branchname = GetBranchName($branchcode);
662 my $branches = GetBranches();
663 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
664 # we have to check they are coming from the right ip range
665 my $domain = $branches->{$branchcode}->{'branchip'};
666 if ($ip !~ /^$domain/){
668 $info{'wrongip'} = 1;
673 foreach my $br ( keys %$branches ) {
674 # now we work with the treatment of ip
675 my $domain = $branches->{$br}->{'branchip'};
676 if ( $domain && $ip =~ /^$domain/ ) {
677 $branchcode = $branches->{$br}->{'branchcode'};
679 # new op dev : add the branchprinter and branchname in the cookie
680 $branchprinter = $branches->{$br}->{'branchprinter'};
681 $branchname = $branches->{$br}->{'branchname'};
684 $session->param('number',$borrowernumber);
685 $session->param('id',$userid);
686 $session->param('cardnumber',$cardnumber);
687 $session->param('firstname',$firstname);
688 $session->param('surname',$surname);
689 $session->param('branch',$branchcode);
690 $session->param('branchname',$branchname);
691 $session->param('flags',$userflags);
692 $session->param('emailaddress',$emailaddress);
693 $session->param('ip',$session->remote_addr());
694 $session->param('lasttime',time());
695 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
697 elsif ( $return == 2 ) {
698 #We suppose the user is the superlibrarian
700 $session->param('number',0);
701 $session->param('id',C4::Context->config('user'));
702 $session->param('cardnumber',C4::Context->config('user'));
703 $session->param('firstname',C4::Context->config('user'));
704 $session->param('surname',C4::Context->config('user'));
705 $session->param('branch','NO_LIBRARY_SET');
706 $session->param('branchname','NO_LIBRARY_SET');
707 $session->param('flags',1);
708 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
709 $session->param('ip',$session->remote_addr());
710 $session->param('lasttime',time());
712 C4::Context::set_userenv(
713 $session->param('number'), $session->param('id'),
714 $session->param('cardnumber'), $session->param('firstname'),
715 $session->param('surname'), $session->param('branch'),
716 $session->param('branchname'), $session->param('flags'),
717 $session->param('emailaddress'), $session->param('branchprinter')
720 # Grab borrower's shelves and public shelves and add them to the session
721 # $row_count determines how many records are returned from the db query
722 # and the number of lists to be displayed of each type in the 'Lists' button drop down
723 my $row_count = 10; # FIXME:This probably should be a syspref
724 my ($total, $totshelves, $barshelves, $pubshelves);
725 ($barshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(1, $row_count, $borrowernumber);
726 $total->{'bartotal'} = $totshelves;
727 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
728 $total->{'pubtotal'} = $totshelves;
729 $session->param('barshelves', $barshelves->[0]);
730 $session->param('pubshelves', $pubshelves->[0]);
731 $session->param('totshelves', $total);
733 C4::Context::set_shelves_userenv('bar',$barshelves->[0]);
734 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
735 C4::Context::set_shelves_userenv('tot',$total);
739 $info{'invalid_username_or_password'} = 1;
740 C4::Context->_unset_userenv($sessionID);
743 } # END if ( $userid = $query->param('userid') )
744 elsif ($type eq "opac") {
745 # if we are here this is an anonymous session; add public lists to it and a few other items...
746 # anonymous sessions are created only for the OPAC
747 $debug and warn "Initiating an anonymous session...";
749 # Grab the public shelves and add to the session...
750 my $row_count = 20; # FIXME:This probably should be a syspref
751 my ($total, $totshelves, $pubshelves);
752 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
753 $total->{'pubtotal'} = $totshelves;
754 $session->param('pubshelves', $pubshelves->[0]);
755 $session->param('totshelves', $total);
756 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
757 C4::Context::set_shelves_userenv('tot',$total);
759 # setting a couple of other session vars...
760 $session->param('ip',$session->remote_addr());
761 $session->param('lasttime',time());
762 $session->param('sessiontype','anon');
764 } # END unless ($userid)
765 my $insecure = C4::Context->boolean_preference('insecure');
767 # finished authentification, now respond
768 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
772 $cookie = $query->cookie( CGISESSID => '' );
774 return ( $userid, $cookie, $sessionID, $flags );
779 # AUTH rejected, show the login/password template, after checking the DB.
783 # get the inputs from the incoming query
785 foreach my $name ( param $query) {
786 (next) if ( $name eq 'userid' || $name eq 'password' );
787 my $value = $query->param($name);
788 push @inputs, { name => $name, value => $value };
790 # get the branchloop, which we need for authentication
791 my $branches = GetBranches();
793 for my $branch_hash (sort keys %$branches) {
794 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
797 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
798 my $template = gettemplate( $template_name, $type, $query );
799 $template->param(branchloop => \@branch_loop,);
803 suggestion => C4::Context->preference("suggestion"),
804 virtualshelves => C4::Context->preference("virtualshelves"),
805 LibraryName => C4::Context->preference("LibraryName"),
806 opacuserlogin => C4::Context->preference("opacuserlogin"),
807 OpacNav => C4::Context->preference("OpacNav"),
808 opaccredits => C4::Context->preference("opaccredits"),
809 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
810 opacsmallimage => C4::Context->preference("opacsmallimage"),
811 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
812 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
813 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
814 opacuserjs => C4::Context->preference("opacuserjs"),
815 opacbookbag => "" . C4::Context->preference("opacbookbag"),
816 OpacCloud => C4::Context->preference("OpacCloud"),
817 OpacTopissue => C4::Context->preference("OpacTopissue"),
818 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
819 OpacBrowser => C4::Context->preference("OpacBrowser"),
820 opacheader => C4::Context->preference("opacheader"),
821 TagsEnabled => C4::Context->preference("TagsEnabled"),
822 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
823 intranetcolorstylesheet =>
824 C4::Context->preference("intranetcolorstylesheet"),
825 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
826 IntranetNav => C4::Context->preference("IntranetNav"),
827 intranetuserjs => C4::Context->preference("intranetuserjs"),
828 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
829 IndependantBranches=> C4::Context->preference("IndependantBranches"),
830 AutoLocation => C4::Context->preference("AutoLocation"),
831 wrongip => $info{'wrongip'}
834 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
836 my $self_url = $query->url( -absolute => 1 );
839 LibraryName => C4::Context->preference("LibraryName"),
841 $template->param( \%info );
842 # $cookie = $query->cookie(CGISESSID => $session->id
844 print $query->header(
845 -type => 'text/html',
855 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
857 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
858 cookie, determine if the user has the privileges specified by C<$userflags>.
860 C<check_api_auth> is is meant for authenticating users of web services, and
861 consequently will always return and will not attempt to redirect the user
864 If a valid session cookie is already present, check_api_auth will return a status
865 of "ok", the cookie, and the Koha session ID.
867 If no session cookie is present, check_api_auth will check the 'userid' and 'password
868 parameters and create a session cookie and Koha session if the supplied credentials
871 Possible return values in C<$status> are:
875 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
877 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
879 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
881 =item "expired -- session cookie has expired; API user should resubmit userid and password
889 my $flagsrequired = shift;
891 my $dbh = C4::Context->dbh;
892 my $timeout = C4::Context->preference('timeout');
893 $timeout = 600 unless $timeout;
895 unless (C4::Context->preference('Version')) {
896 # database has not been installed yet
897 return ("maintenance", undef, undef);
899 my $kohaversion=C4::Context::KOHAVERSION;
900 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
901 if (C4::Context->preference('Version') < $kohaversion) {
902 # database in need of version update; assume that
903 # no API should be called while databsae is in
905 return ("maintenance", undef, undef);
908 # FIXME -- most of what follows is a copy-and-paste
909 # of code from checkauth. There is an obvious need
910 # for refactoring to separate the various parts of
911 # the authentication code, but as of 2007-11-19 this
912 # is deferred so as to not introduce bugs into the
913 # regular authentication code for Koha 3.0.
915 # see if we have a valid session cookie already
916 # however, if a userid parameter is present (i.e., from
917 # a form submission, assume that any current cookie
919 my $sessionID = undef;
920 unless ($query->param('userid')) {
921 $sessionID = $query->cookie("CGISESSID");
924 my $session = get_session($sessionID);
925 C4::Context->_new_userenv($sessionID);
927 C4::Context::set_userenv(
928 $session->param('number'), $session->param('id'),
929 $session->param('cardnumber'), $session->param('firstname'),
930 $session->param('surname'), $session->param('branch'),
931 $session->param('branchname'), $session->param('flags'),
932 $session->param('emailaddress'), $session->param('branchprinter')
935 my $ip = $session->param('ip');
936 my $lasttime = $session->param('lasttime');
937 my $userid = $session->param('id');
938 if ( $lasttime < time() - $timeout ) {
941 C4::Context->_unset_userenv($sessionID);
944 return ("expired", undef, undef);
945 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
948 C4::Context->_unset_userenv($sessionID);
951 return ("expired", undef, undef);
953 my $cookie = $query->cookie( CGISESSID => $session->id );
954 $session->param('lasttime',time());
955 my $flags = haspermission($userid, $flagsrequired);
957 return ("ok", $cookie, $sessionID);
960 C4::Context->_unset_userenv($sessionID);
963 return ("failed", undef, undef);
967 return ("expired", undef, undef);
971 my $userid = $query->param('userid');
972 my $password = $query->param('password');
973 unless ($userid and $password) {
974 # caller did something wrong, fail the authenticateion
975 return ("failed", undef, undef);
977 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
978 if ($return and haspermission($userid, $flagsrequired)) {
979 my $session = get_session("");
980 return ("failed", undef, undef) unless $session;
982 my $sessionID = $session->id;
983 C4::Context->_new_userenv($sessionID);
984 my $cookie = $query->cookie(CGISESSID => $sessionID);
985 if ( $return == 1 ) {
987 $borrowernumber, $firstname, $surname,
988 $userflags, $branchcode, $branchname,
989 $branchprinter, $emailaddress
993 "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=?"
995 $sth->execute($userid);
997 $borrowernumber, $firstname, $surname,
998 $userflags, $branchcode, $branchname,
999 $branchprinter, $emailaddress
1000 ) = $sth->fetchrow if ( $sth->rows );
1002 unless ($sth->rows ) {
1003 my $sth = $dbh->prepare(
1004 "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=?"
1006 $sth->execute($cardnumber);
1008 $borrowernumber, $firstname, $surname,
1009 $userflags, $branchcode, $branchname,
1010 $branchprinter, $emailaddress
1011 ) = $sth->fetchrow if ( $sth->rows );
1013 unless ( $sth->rows ) {
1014 $sth->execute($userid);
1016 $borrowernumber, $firstname, $surname, $userflags,
1017 $branchcode, $branchname, $branchprinter, $emailaddress
1018 ) = $sth->fetchrow if ( $sth->rows );
1022 my $ip = $ENV{'REMOTE_ADDR'};
1023 # if they specify at login, use that
1024 if ($query->param('branch')) {
1025 $branchcode = $query->param('branch');
1026 $branchname = GetBranchName($branchcode);
1028 my $branches = GetBranches();
1030 foreach my $br ( keys %$branches ) {
1031 # now we work with the treatment of ip
1032 my $domain = $branches->{$br}->{'branchip'};
1033 if ( $domain && $ip =~ /^$domain/ ) {
1034 $branchcode = $branches->{$br}->{'branchcode'};
1036 # new op dev : add the branchprinter and branchname in the cookie
1037 $branchprinter = $branches->{$br}->{'branchprinter'};
1038 $branchname = $branches->{$br}->{'branchname'};
1041 $session->param('number',$borrowernumber);
1042 $session->param('id',$userid);
1043 $session->param('cardnumber',$cardnumber);
1044 $session->param('firstname',$firstname);
1045 $session->param('surname',$surname);
1046 $session->param('branch',$branchcode);
1047 $session->param('branchname',$branchname);
1048 $session->param('flags',$userflags);
1049 $session->param('emailaddress',$emailaddress);
1050 $session->param('ip',$session->remote_addr());
1051 $session->param('lasttime',time());
1052 } elsif ( $return == 2 ) {
1053 #We suppose the user is the superlibrarian
1054 $session->param('number',0);
1055 $session->param('id',C4::Context->config('user'));
1056 $session->param('cardnumber',C4::Context->config('user'));
1057 $session->param('firstname',C4::Context->config('user'));
1058 $session->param('surname',C4::Context->config('user'));
1059 $session->param('branch','NO_LIBRARY_SET');
1060 $session->param('branchname','NO_LIBRARY_SET');
1061 $session->param('flags',1);
1062 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1063 $session->param('ip',$session->remote_addr());
1064 $session->param('lasttime',time());
1066 C4::Context::set_userenv(
1067 $session->param('number'), $session->param('id'),
1068 $session->param('cardnumber'), $session->param('firstname'),
1069 $session->param('surname'), $session->param('branch'),
1070 $session->param('branchname'), $session->param('flags'),
1071 $session->param('emailaddress'), $session->param('branchprinter')
1073 return ("ok", $cookie, $sessionID);
1075 return ("failed", undef, undef);
1080 =item check_cookie_auth
1082 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1084 Given a CGISESSID cookie set during a previous login to Koha, determine
1085 if the user has the privileges specified by C<$userflags>.
1087 C<check_cookie_auth> is meant for authenticating special services
1088 such as tools/upload-file.pl that are invoked by other pages that
1089 have been authenticated in the usual way.
1091 Possible return values in C<$status> are:
1095 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1097 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1099 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1101 =item "expired -- session cookie has expired; API user should resubmit userid and password
1107 sub check_cookie_auth {
1109 my $flagsrequired = shift;
1111 my $dbh = C4::Context->dbh;
1112 my $timeout = C4::Context->preference('timeout');
1113 $timeout = 600 unless $timeout;
1115 unless (C4::Context->preference('Version')) {
1116 # database has not been installed yet
1117 return ("maintenance", undef);
1119 my $kohaversion=C4::Context::KOHAVERSION;
1120 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1121 if (C4::Context->preference('Version') < $kohaversion) {
1122 # database in need of version update; assume that
1123 # no API should be called while databsae is in
1125 return ("maintenance", undef);
1128 # FIXME -- most of what follows is a copy-and-paste
1129 # of code from checkauth. There is an obvious need
1130 # for refactoring to separate the various parts of
1131 # the authentication code, but as of 2007-11-23 this
1132 # is deferred so as to not introduce bugs into the
1133 # regular authentication code for Koha 3.0.
1135 # see if we have a valid session cookie already
1136 # however, if a userid parameter is present (i.e., from
1137 # a form submission, assume that any current cookie
1139 unless (defined $cookie and $cookie) {
1140 return ("failed", undef);
1142 my $sessionID = $cookie;
1143 my $session = get_session($sessionID);
1144 C4::Context->_new_userenv($sessionID);
1146 C4::Context::set_userenv(
1147 $session->param('number'), $session->param('id'),
1148 $session->param('cardnumber'), $session->param('firstname'),
1149 $session->param('surname'), $session->param('branch'),
1150 $session->param('branchname'), $session->param('flags'),
1151 $session->param('emailaddress'), $session->param('branchprinter')
1154 my $ip = $session->param('ip');
1155 my $lasttime = $session->param('lasttime');
1156 my $userid = $session->param('id');
1157 if ( $lasttime < time() - $timeout ) {
1160 C4::Context->_unset_userenv($sessionID);
1163 return ("expired", undef);
1164 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1165 # IP address changed
1167 C4::Context->_unset_userenv($sessionID);
1170 return ("expired", undef);
1172 $session->param('lasttime',time());
1173 my $flags = haspermission($userid, $flagsrequired);
1175 return ("ok", $sessionID);
1178 C4::Context->_unset_userenv($sessionID);
1181 return ("failed", undef);
1185 return ("expired", undef);
1192 my $session = get_session($sessionID);
1194 Given a session ID, retrieve the CGI::Session object used to store
1195 the session's state. The session object can be used to store
1196 data that needs to be accessed by different scripts during a
1199 If the C<$sessionID> parameter is an empty string, a new session
1205 my $sessionID = shift;
1206 my $storage_method = C4::Context->preference('SessionStorage');
1207 my $dbh = C4::Context->dbh;
1209 if ($storage_method eq 'mysql'){
1210 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1212 elsif ($storage_method eq 'Pg') {
1213 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1216 # catch all defaults to tmp should work on all systems
1217 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1224 my ( $dbh, $userid, $password ) = @_;
1226 $debug and print STDERR "## checkpw - checking LDAP\n";
1227 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1228 ($retval) and return ($retval,$retcard);
1234 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1236 $sth->execute($userid);
1238 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1239 $surname, $branchcode, $flags )
1241 if ( ( md5_base64($password) eq $md5password ) and ($md5password ne "!") ) {
1243 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1244 $firstname, $surname, $branchcode, $flags );
1245 return 1, $cardnumber;
1250 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1252 $sth->execute($userid);
1254 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1255 $surname, $branchcode, $flags )
1257 if ( md5_base64($password) eq $md5password ) {
1259 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1260 $firstname, $surname, $branchcode, $flags );
1264 if ( $userid && $userid eq C4::Context->config('user')
1265 && "$password" eq C4::Context->config('pass') )
1268 # Koha superuser account
1269 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1272 if ( $userid && $userid eq 'demo'
1273 && "$password" eq 'demo'
1274 && C4::Context->config('demo') )
1277 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1278 # some features won't be effective : modify systempref, modify MARC structure,
1286 my $authflags = getuserflags($flags, $userid, [$dbh]);
1288 Translates integer flags into permissions strings hash.
1290 C<$flags> is the integer userflags value ( borrowers.userflags )
1291 C<$userid> is the members.userid, used for building subpermissions
1292 C<$authflags> is a hashref of permissions
1299 my $dbh = @_ ? shift : C4::Context->dbh;
1301 $flags = 0 unless $flags;
1302 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1305 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1306 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1307 $userflags->{$flag} = 1;
1310 $userflags->{$flag} = 0;
1314 # get subpermissions and merge with top-level permissions
1315 my $user_subperms = get_user_subpermissions($userid);
1316 foreach my $module (keys %$user_subperms) {
1317 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1318 $userflags->{$module} = $user_subperms->{$module};
1324 =item get_user_subpermissions
1328 my $user_perm_hashref = get_user_subpermissions($userid);
1332 Given the userid (note, not the borrowernumber) of a staff user,
1333 return a hashref of hashrefs of the specific subpermissions
1334 accorded to the user. An example return is
1338 export_catalog => 1,
1339 import_patrons => 1,
1343 The top-level hash-key is a module or function code from
1344 userflags.flag, while the second-level key is a code
1347 The results of this function do not give a complete picture
1348 of the functions that a staff user can access; it is also
1349 necessary to check borrowers.flags.
1353 sub get_user_subpermissions {
1356 my $dbh = C4::Context->dbh;
1357 my $sth = $dbh->prepare("SELECT flag, user_permissions.code as code
1358 FROM user_permissions
1359 JOIN permissions USING (module_bit, code)
1360 JOIN userflags ON (permissions.module_bit = userflags.bit)
1361 JOIN borrowers ON (user_permissions.borrowernumber=borrowers.borrowernumber)
1363 $sth->execute($userid);
1365 my $user_perms = {};
1366 while (my $perm = $sth->fetchrow_hashref) {
1367 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1372 =item get_all_subpermissions
1376 my $perm_hashref = get_all_subpermissions();
1380 Returns a hashref of hashrefs defining all specific
1381 permissions currently defined. The return value
1382 has the same structure as that of C<get_user_subpermissions>,
1383 except that the innermost hash value is the description
1384 of the subpermission.
1388 sub get_all_subpermissions {
1389 my $dbh = C4::Context->dbh;
1390 my $sth = $dbh->prepare("SELECT flag, code, description
1392 JOIN userflags ON (module_bit = bit)");
1396 while (my $perm = $sth->fetchrow_hashref) {
1397 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1404 $flags = ($userid, $flagsrequired);
1406 C<$userid> the userid of the member
1407 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1409 Returns member's flags or 0 if a permission is not met.
1414 my ($userid, $flagsrequired) = @_;
1415 my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1416 $sth->execute($userid);
1417 my $flags = getuserflags( $sth->fetchrow(), $userid );
1418 if ( $userid eq C4::Context->config('user') ) {
1419 # Super User Account from /etc/koha.conf
1420 $flags->{'superlibrarian'} = 1;
1422 elsif ( $userid eq 'demo' && C4::Context->config('demo') ) {
1423 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1424 $flags->{'superlibrarian'} = 1;
1426 return $flags if $flags->{superlibrarian};
1427 foreach my $module ( keys %$flagsrequired ) {
1428 if (C4::Context->preference('GranularPermissions')) {
1429 my $subperm = $flagsrequired->{$module};
1430 if ($subperm eq '*') {
1431 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1433 return 0 unless ( $flags->{$module} == 1 or
1434 ( ref($flags->{$module}) and
1435 exists $flags->{$module}->{$subperm} and
1436 $flags->{$module}->{$subperm} == 1
1441 return 0 unless ( $flags->{$module} );
1445 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1449 sub getborrowernumber {
1451 my $userenv = C4::Context->userenv;
1452 if ( defined( $userenv ) && ref( $userenv ) eq 'HASH' && $userenv->{number} ) {
1453 return $userenv->{number};
1455 my $dbh = C4::Context->dbh;
1456 for my $field ( 'userid', 'cardnumber' ) {
1458 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1459 $sth->execute($userid);
1461 my ($bnumber) = $sth->fetchrow;
1468 END { } # module clean-up code here (global destructor)