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 OpacRecentAcquisitions => C4::Context->preference("OpacRecentAcquisitions"),
340 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
341 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
342 'Version' => C4::Context->preference('Version'),
343 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
344 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
345 hidelostitems => C4::Context->preference("hidelostitems"),
346 mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '',
347 opaclayoutstylesheet => "" . C4::Context->preference("opaclayoutstylesheet"),
348 opaccolorstylesheet => "" . C4::Context->preference("opaccolorstylesheet"),
349 opacstylesheet => "" . C4::Context->preference("opacstylesheet"),
350 opacbookbag => "" . C4::Context->preference("opacbookbag"),
351 opaccredits => "" . C4::Context->preference("opaccredits"),
352 opacheader => "" . C4::Context->preference("opacheader"),
353 opaclanguagesdisplay => "" . C4::Context->preference("opaclanguagesdisplay"),
354 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
355 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
356 opacuserjs => C4::Context->preference("opacuserjs"),
357 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
358 reviewson => C4::Context->preference("reviewson"),
359 suggestion => "" . C4::Context->preference("suggestion"),
360 virtualshelves => "" . C4::Context->preference("virtualshelves"),
363 return ( $template, $borrowernumber, $cookie, $flags);
368 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
370 Verifies that the user is authorized to run this script. If
371 the user is authorized, a (userid, cookie, session-id, flags)
372 quadruple is returned. If the user is not authorized due to
373 insufficent privileges (see $flagsrequired below), it
374 displays an error page and exits. Otherwise, it displays the
375 login page and exits.
377 Note that C<&checkauth> will return if and only if the user
378 is authorized, so it should be called early on, before any
379 unfinished operations (e.g., if you've opened a file, then
380 C<&checkauth> won't close it for you).
382 C<$query> is the CGI object for the script calling C<&checkauth>.
384 The C<$noauth> argument is optional. If it is set, then no
385 authorization is required for the script.
387 C<&checkauth> fetches user and session information from C<$query> and
388 ensures that the user is authorized to run scripts that require
391 The C<$flagsrequired> argument specifies the required privileges
392 the user must have if the username and password are correct.
393 It should be specified as a reference-to-hash; keys in the hash
394 should be the "flags" for the user, as specified in the Members
395 intranet module. Any key specified must correspond to a "flag"
396 in the userflags table. E.g., { circulate => 1 } would specify
397 that the user must have the "circulate" privilege in order to
398 proceed. To make sure that access control is correct, the
399 C<$flagsrequired> parameter must be specified correctly.
401 If the GranularPermissions system preference is ON, the
402 value of each key in the C<flagsrequired> hash takes on an additional
407 The user must have access to all subfunctions of the module
408 specified by the hash key.
412 The user must have access to at least one subfunction of the module
413 specified by the hash key.
415 =item specific permission, e.g., 'export_catalog'
417 The user must have access to the specific subfunction list, which
418 must correspond to a row in the permissions table.
420 The C<$type> argument specifies whether the template should be
421 retrieved from the opac or intranet directory tree. "opac" is
422 assumed if it is not specified; however, if C<$type> is specified,
423 "intranet" is assumed if it is not "opac".
425 If C<$query> does not have a valid session ID associated with it
426 (i.e., the user has not logged in) or if the session has expired,
427 C<&checkauth> presents the user with a login page (from the point of
428 view of the original script, C<&checkauth> does not return). Once the
429 user has authenticated, C<&checkauth> restarts the original script
430 (this time, C<&checkauth> returns).
432 The login page is provided using a HTML::Template, which is set in the
433 systempreferences table or at the top of this file. The variable C<$type>
434 selects which template to use, either the opac or the intranet
435 authentification template.
437 C<&checkauth> returns a user ID, a cookie, and a session ID. The
438 cookie should be sent back to the browser; it verifies that the user
443 sub _version_check ($$) {
447 # If Version syspref is unavailable, it means Koha is beeing installed,
448 # and so we must redirect to OPAC maintenance page or to the WebInstaller
449 # also, if OpacMaintenance is ON, OPAC should redirect to maintenance
450 if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') {
451 warn "OPAC Install required, redirecting to maintenance";
452 print $query->redirect("/cgi-bin/koha/maintenance.pl");
454 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
455 if ($type ne 'opac') {
456 warn "Install required, redirecting to Installer";
457 print $query->redirect("/cgi-bin/koha/installer/install.pl");
460 warn "OPAC Install required, redirecting to maintenance";
461 print $query->redirect("/cgi-bin/koha/maintenance.pl");
466 # check that database and koha version are the same
467 # there is no DB version, it's a fresh install,
468 # go to web installer
469 # there is a DB version, compare it to the code version
470 my $kohaversion=C4::Context::KOHAVERSION;
471 # remove the 3 last . to have a Perl number
472 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
473 $debug and print STDERR "kohaversion : $kohaversion\n";
474 if ($version < $kohaversion){
475 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
476 if ($type ne 'opac'){
477 warn sprintf($warning, 'Installer');
478 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
480 warn sprintf("OPAC: " . $warning, 'maintenance');
481 print $query->redirect("/cgi-bin/koha/maintenance.pl");
489 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
490 printf L join("\n",@_);
496 $debug and warn "Checking Auth";
497 # $authnotrequired will be set for scripts which will run without authentication
498 my $authnotrequired = shift;
499 my $flagsrequired = shift;
501 $type = 'opac' unless $type;
503 my $dbh = C4::Context->dbh;
504 my $timeout = C4::Context->preference('timeout');
506 if ($timeout =~ /(\d+)[dD]/) {
507 $timeout = $1 * 86400;
509 $timeout = 600 unless $timeout;
511 _version_check($type,$query);
515 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
516 my $logout = $query->param('logout.x');
518 if ( $userid = $ENV{'REMOTE_USER'} ) {
519 # Using Basic Authentication, no cookies required
520 $cookie = $query->cookie(
521 -name => 'CGISESSID',
527 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
528 my $session = get_session($sessionID);
529 C4::Context->_new_userenv($sessionID);
530 my ($ip, $lasttime, $sessiontype);
532 C4::Context::set_userenv(
533 $session->param('number'), $session->param('id'),
534 $session->param('cardnumber'), $session->param('firstname'),
535 $session->param('surname'), $session->param('branch'),
536 $session->param('branchname'), $session->param('flags'),
537 $session->param('emailaddress'), $session->param('branchprinter')
539 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
540 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
541 C4::Context::set_shelves_userenv('tot',$session->param('totshelves'));
542 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
543 $ip = $session->param('ip');
544 $lasttime = $session->param('lasttime');
545 $userid = $session->param('id');
546 $sessiontype = $session->param('sessiontype');
549 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
550 #if a user enters an id ne to the id in the current session, we need to log them in...
551 #first we need to clear the anonymous session...
552 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
555 C4::Context->_unset_userenv($sessionID);
560 # voluntary logout the user
563 C4::Context->_unset_userenv($sessionID);
564 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,localtime);
568 elsif ( $lasttime < time() - $timeout ) {
570 $info{'timed_out'} = 1;
572 C4::Context->_unset_userenv($sessionID);
573 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,localtime);
577 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
578 # Different ip than originally logged in from
579 $info{'oldip'} = $ip;
580 $info{'newip'} = $ENV{'REMOTE_ADDR'};
581 $info{'different_ip'} = 1;
583 C4::Context->_unset_userenv($sessionID);
584 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,localtime, $info{'newip'});
589 $cookie = $query->cookie( CGISESSID => $session->id );
590 $session->param('lasttime',time());
591 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...
592 $flags = haspermission($userid, $flagsrequired);
596 $info{'nopermission'} = 1;
601 unless ($userid || $sessionID) {
602 #we initiate a session prior to checking for a username to allow for anonymous sessions...
603 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
604 my $sessionID = $session->id;
605 C4::Context->_new_userenv($sessionID);
606 $cookie = $query->cookie(CGISESSID => $sessionID);
607 if ( $userid = $query->param('userid') ) {
608 my $password = $query->param('password');
609 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
611 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},localtime);
612 if ( $flags = haspermission($userid, $flagsrequired) ) {
616 $info{'nopermission'} = 1;
617 C4::Context->_unset_userenv($sessionID);
620 my ($borrowernumber, $firstname, $surname, $userflags,
621 $branchcode, $branchname, $branchprinter, $emailaddress);
623 if ( $return == 1 ) {
625 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
626 branches.branchname as branchname,
627 branches.branchprinter as branchprinter,
630 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
632 my $sth = $dbh->prepare("$select where userid=?");
633 $sth->execute($userid);
634 unless ($sth->rows) {
635 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
636 $sth = $dbh->prepare("$select where cardnumber=?");
637 $sth->execute($cardnumber);
638 unless ($sth->rows) {
639 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
640 $sth->execute($userid);
641 unless ($sth->rows) {
642 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
647 ($borrowernumber, $firstname, $surname, $userflags,
648 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
649 $debug and print STDERR "AUTH_3 results: " .
650 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
652 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
654 # launch a sequence to check if we have a ip for the branch, i
655 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
657 my $ip = $ENV{'REMOTE_ADDR'};
658 # if they specify at login, use that
659 if ($query->param('branch')) {
660 $branchcode = $query->param('branch');
661 $branchname = GetBranchName($branchcode);
663 my $branches = GetBranches();
664 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
665 # we have to check they are coming from the right ip range
666 my $domain = $branches->{$branchcode}->{'branchip'};
667 if ($ip !~ /^$domain/){
669 $info{'wrongip'} = 1;
674 foreach my $br ( keys %$branches ) {
675 # now we work with the treatment of ip
676 my $domain = $branches->{$br}->{'branchip'};
677 if ( $domain && $ip =~ /^$domain/ ) {
678 $branchcode = $branches->{$br}->{'branchcode'};
680 # new op dev : add the branchprinter and branchname in the cookie
681 $branchprinter = $branches->{$br}->{'branchprinter'};
682 $branchname = $branches->{$br}->{'branchname'};
685 $session->param('number',$borrowernumber);
686 $session->param('id',$userid);
687 $session->param('cardnumber',$cardnumber);
688 $session->param('firstname',$firstname);
689 $session->param('surname',$surname);
690 $session->param('branch',$branchcode);
691 $session->param('branchname',$branchname);
692 $session->param('flags',$userflags);
693 $session->param('emailaddress',$emailaddress);
694 $session->param('ip',$session->remote_addr());
695 $session->param('lasttime',time());
696 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
698 elsif ( $return == 2 ) {
699 #We suppose the user is the superlibrarian
701 $session->param('number',0);
702 $session->param('id',C4::Context->config('user'));
703 $session->param('cardnumber',C4::Context->config('user'));
704 $session->param('firstname',C4::Context->config('user'));
705 $session->param('surname',C4::Context->config('user'));
706 $session->param('branch','NO_LIBRARY_SET');
707 $session->param('branchname','NO_LIBRARY_SET');
708 $session->param('flags',1);
709 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
710 $session->param('ip',$session->remote_addr());
711 $session->param('lasttime',time());
713 C4::Context::set_userenv(
714 $session->param('number'), $session->param('id'),
715 $session->param('cardnumber'), $session->param('firstname'),
716 $session->param('surname'), $session->param('branch'),
717 $session->param('branchname'), $session->param('flags'),
718 $session->param('emailaddress'), $session->param('branchprinter')
721 # Grab borrower's shelves and public shelves and add them to the session
722 # $row_count determines how many records are returned from the db query
723 # and the number of lists to be displayed of each type in the 'Lists' button drop down
724 my $row_count = 10; # FIXME:This probably should be a syspref
725 my ($total, $totshelves, $barshelves, $pubshelves);
726 ($barshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(1, $row_count, $borrowernumber);
727 $total->{'bartotal'} = $totshelves;
728 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
729 $total->{'pubtotal'} = $totshelves;
730 $session->param('barshelves', $barshelves->[0]);
731 $session->param('pubshelves', $pubshelves->[0]);
732 $session->param('totshelves', $total);
734 C4::Context::set_shelves_userenv('bar',$barshelves->[0]);
735 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
736 C4::Context::set_shelves_userenv('tot',$total);
740 $info{'invalid_username_or_password'} = 1;
741 C4::Context->_unset_userenv($sessionID);
744 } # END if ( $userid = $query->param('userid') )
745 elsif ($type eq "opac") {
746 # if we are here this is an anonymous session; add public lists to it and a few other items...
747 # anonymous sessions are created only for the OPAC
748 $debug and warn "Initiating an anonymous session...";
750 # Grab the public shelves and add to the session...
751 my $row_count = 20; # FIXME:This probably should be a syspref
752 my ($total, $totshelves, $pubshelves);
753 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
754 $total->{'pubtotal'} = $totshelves;
755 $session->param('pubshelves', $pubshelves->[0]);
756 $session->param('totshelves', $total);
757 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
758 C4::Context::set_shelves_userenv('tot',$total);
760 # setting a couple of other session vars...
761 $session->param('ip',$session->remote_addr());
762 $session->param('lasttime',time());
763 $session->param('sessiontype','anon');
765 } # END unless ($userid)
766 my $insecure = C4::Context->boolean_preference('insecure');
768 # finished authentification, now respond
769 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
773 $cookie = $query->cookie( CGISESSID => '' );
775 return ( $userid, $cookie, $sessionID, $flags );
780 # AUTH rejected, show the login/password template, after checking the DB.
784 # get the inputs from the incoming query
786 foreach my $name ( param $query) {
787 (next) if ( $name eq 'userid' || $name eq 'password' );
788 my $value = $query->param($name);
789 push @inputs, { name => $name, value => $value };
791 # get the branchloop, which we need for authentication
792 my $branches = GetBranches();
794 for my $branch_hash (sort keys %$branches) {
795 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
798 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
799 my $template = gettemplate( $template_name, $type, $query );
800 $template->param(branchloop => \@branch_loop,);
804 suggestion => C4::Context->preference("suggestion"),
805 virtualshelves => C4::Context->preference("virtualshelves"),
806 LibraryName => C4::Context->preference("LibraryName"),
807 opacuserlogin => C4::Context->preference("opacuserlogin"),
808 OpacNav => C4::Context->preference("OpacNav"),
809 opaccredits => C4::Context->preference("opaccredits"),
810 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
811 opacsmallimage => C4::Context->preference("opacsmallimage"),
812 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
813 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
814 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
815 opacuserjs => C4::Context->preference("opacuserjs"),
816 opacbookbag => "" . C4::Context->preference("opacbookbag"),
817 OpacCloud => C4::Context->preference("OpacCloud"),
818 OpacTopissue => C4::Context->preference("OpacTopissue"),
819 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
820 OpacBrowser => C4::Context->preference("OpacBrowser"),
821 opacheader => C4::Context->preference("opacheader"),
822 TagsEnabled => C4::Context->preference("TagsEnabled"),
823 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
824 intranetcolorstylesheet =>
825 C4::Context->preference("intranetcolorstylesheet"),
826 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
827 IntranetNav => C4::Context->preference("IntranetNav"),
828 intranetuserjs => C4::Context->preference("intranetuserjs"),
829 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
830 IndependantBranches=> C4::Context->preference("IndependantBranches"),
831 AutoLocation => C4::Context->preference("AutoLocation"),
832 wrongip => $info{'wrongip'}
835 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
837 my $self_url = $query->url( -absolute => 1 );
840 LibraryName => C4::Context->preference("LibraryName"),
842 $template->param( \%info );
843 # $cookie = $query->cookie(CGISESSID => $session->id
845 print $query->header(
846 -type => 'text/html',
856 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
858 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
859 cookie, determine if the user has the privileges specified by C<$userflags>.
861 C<check_api_auth> is is meant for authenticating users of web services, and
862 consequently will always return and will not attempt to redirect the user
865 If a valid session cookie is already present, check_api_auth will return a status
866 of "ok", the cookie, and the Koha session ID.
868 If no session cookie is present, check_api_auth will check the 'userid' and 'password
869 parameters and create a session cookie and Koha session if the supplied credentials
872 Possible return values in C<$status> are:
876 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
878 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
880 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
882 =item "expired -- session cookie has expired; API user should resubmit userid and password
890 my $flagsrequired = shift;
892 my $dbh = C4::Context->dbh;
893 my $timeout = C4::Context->preference('timeout');
894 $timeout = 600 unless $timeout;
896 unless (C4::Context->preference('Version')) {
897 # database has not been installed yet
898 return ("maintenance", undef, undef);
900 my $kohaversion=C4::Context::KOHAVERSION;
901 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
902 if (C4::Context->preference('Version') < $kohaversion) {
903 # database in need of version update; assume that
904 # no API should be called while databsae is in
906 return ("maintenance", undef, undef);
909 # FIXME -- most of what follows is a copy-and-paste
910 # of code from checkauth. There is an obvious need
911 # for refactoring to separate the various parts of
912 # the authentication code, but as of 2007-11-19 this
913 # is deferred so as to not introduce bugs into the
914 # regular authentication code for Koha 3.0.
916 # see if we have a valid session cookie already
917 # however, if a userid parameter is present (i.e., from
918 # a form submission, assume that any current cookie
920 my $sessionID = undef;
921 unless ($query->param('userid')) {
922 $sessionID = $query->cookie("CGISESSID");
925 my $session = get_session($sessionID);
926 C4::Context->_new_userenv($sessionID);
928 C4::Context::set_userenv(
929 $session->param('number'), $session->param('id'),
930 $session->param('cardnumber'), $session->param('firstname'),
931 $session->param('surname'), $session->param('branch'),
932 $session->param('branchname'), $session->param('flags'),
933 $session->param('emailaddress'), $session->param('branchprinter')
936 my $ip = $session->param('ip');
937 my $lasttime = $session->param('lasttime');
938 my $userid = $session->param('id');
939 if ( $lasttime < time() - $timeout ) {
942 C4::Context->_unset_userenv($sessionID);
945 return ("expired", undef, undef);
946 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
949 C4::Context->_unset_userenv($sessionID);
952 return ("expired", undef, undef);
954 my $cookie = $query->cookie( CGISESSID => $session->id );
955 $session->param('lasttime',time());
956 my $flags = haspermission($userid, $flagsrequired);
958 return ("ok", $cookie, $sessionID);
961 C4::Context->_unset_userenv($sessionID);
964 return ("failed", undef, undef);
968 return ("expired", undef, undef);
972 my $userid = $query->param('userid');
973 my $password = $query->param('password');
974 unless ($userid and $password) {
975 # caller did something wrong, fail the authenticateion
976 return ("failed", undef, undef);
978 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
979 if ($return and haspermission($userid, $flagsrequired)) {
980 my $session = get_session("");
981 return ("failed", undef, undef) unless $session;
983 my $sessionID = $session->id;
984 C4::Context->_new_userenv($sessionID);
985 my $cookie = $query->cookie(CGISESSID => $sessionID);
986 if ( $return == 1 ) {
988 $borrowernumber, $firstname, $surname,
989 $userflags, $branchcode, $branchname,
990 $branchprinter, $emailaddress
994 "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=?"
996 $sth->execute($userid);
998 $borrowernumber, $firstname, $surname,
999 $userflags, $branchcode, $branchname,
1000 $branchprinter, $emailaddress
1001 ) = $sth->fetchrow if ( $sth->rows );
1003 unless ($sth->rows ) {
1004 my $sth = $dbh->prepare(
1005 "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=?"
1007 $sth->execute($cardnumber);
1009 $borrowernumber, $firstname, $surname,
1010 $userflags, $branchcode, $branchname,
1011 $branchprinter, $emailaddress
1012 ) = $sth->fetchrow if ( $sth->rows );
1014 unless ( $sth->rows ) {
1015 $sth->execute($userid);
1017 $borrowernumber, $firstname, $surname, $userflags,
1018 $branchcode, $branchname, $branchprinter, $emailaddress
1019 ) = $sth->fetchrow if ( $sth->rows );
1023 my $ip = $ENV{'REMOTE_ADDR'};
1024 # if they specify at login, use that
1025 if ($query->param('branch')) {
1026 $branchcode = $query->param('branch');
1027 $branchname = GetBranchName($branchcode);
1029 my $branches = GetBranches();
1031 foreach my $br ( keys %$branches ) {
1032 # now we work with the treatment of ip
1033 my $domain = $branches->{$br}->{'branchip'};
1034 if ( $domain && $ip =~ /^$domain/ ) {
1035 $branchcode = $branches->{$br}->{'branchcode'};
1037 # new op dev : add the branchprinter and branchname in the cookie
1038 $branchprinter = $branches->{$br}->{'branchprinter'};
1039 $branchname = $branches->{$br}->{'branchname'};
1042 $session->param('number',$borrowernumber);
1043 $session->param('id',$userid);
1044 $session->param('cardnumber',$cardnumber);
1045 $session->param('firstname',$firstname);
1046 $session->param('surname',$surname);
1047 $session->param('branch',$branchcode);
1048 $session->param('branchname',$branchname);
1049 $session->param('flags',$userflags);
1050 $session->param('emailaddress',$emailaddress);
1051 $session->param('ip',$session->remote_addr());
1052 $session->param('lasttime',time());
1053 } elsif ( $return == 2 ) {
1054 #We suppose the user is the superlibrarian
1055 $session->param('number',0);
1056 $session->param('id',C4::Context->config('user'));
1057 $session->param('cardnumber',C4::Context->config('user'));
1058 $session->param('firstname',C4::Context->config('user'));
1059 $session->param('surname',C4::Context->config('user'));
1060 $session->param('branch','NO_LIBRARY_SET');
1061 $session->param('branchname','NO_LIBRARY_SET');
1062 $session->param('flags',1);
1063 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1064 $session->param('ip',$session->remote_addr());
1065 $session->param('lasttime',time());
1067 C4::Context::set_userenv(
1068 $session->param('number'), $session->param('id'),
1069 $session->param('cardnumber'), $session->param('firstname'),
1070 $session->param('surname'), $session->param('branch'),
1071 $session->param('branchname'), $session->param('flags'),
1072 $session->param('emailaddress'), $session->param('branchprinter')
1074 return ("ok", $cookie, $sessionID);
1076 return ("failed", undef, undef);
1081 =item check_cookie_auth
1083 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1085 Given a CGISESSID cookie set during a previous login to Koha, determine
1086 if the user has the privileges specified by C<$userflags>.
1088 C<check_cookie_auth> is meant for authenticating special services
1089 such as tools/upload-file.pl that are invoked by other pages that
1090 have been authenticated in the usual way.
1092 Possible return values in C<$status> are:
1096 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1098 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1100 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1102 =item "expired -- session cookie has expired; API user should resubmit userid and password
1108 sub check_cookie_auth {
1110 my $flagsrequired = shift;
1112 my $dbh = C4::Context->dbh;
1113 my $timeout = C4::Context->preference('timeout');
1114 $timeout = 600 unless $timeout;
1116 unless (C4::Context->preference('Version')) {
1117 # database has not been installed yet
1118 return ("maintenance", undef);
1120 my $kohaversion=C4::Context::KOHAVERSION;
1121 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1122 if (C4::Context->preference('Version') < $kohaversion) {
1123 # database in need of version update; assume that
1124 # no API should be called while databsae is in
1126 return ("maintenance", undef);
1129 # FIXME -- most of what follows is a copy-and-paste
1130 # of code from checkauth. There is an obvious need
1131 # for refactoring to separate the various parts of
1132 # the authentication code, but as of 2007-11-23 this
1133 # is deferred so as to not introduce bugs into the
1134 # regular authentication code for Koha 3.0.
1136 # see if we have a valid session cookie already
1137 # however, if a userid parameter is present (i.e., from
1138 # a form submission, assume that any current cookie
1140 unless (defined $cookie and $cookie) {
1141 return ("failed", undef);
1143 my $sessionID = $cookie;
1144 my $session = get_session($sessionID);
1145 C4::Context->_new_userenv($sessionID);
1147 C4::Context::set_userenv(
1148 $session->param('number'), $session->param('id'),
1149 $session->param('cardnumber'), $session->param('firstname'),
1150 $session->param('surname'), $session->param('branch'),
1151 $session->param('branchname'), $session->param('flags'),
1152 $session->param('emailaddress'), $session->param('branchprinter')
1155 my $ip = $session->param('ip');
1156 my $lasttime = $session->param('lasttime');
1157 my $userid = $session->param('id');
1158 if ( $lasttime < time() - $timeout ) {
1161 C4::Context->_unset_userenv($sessionID);
1164 return ("expired", undef);
1165 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1166 # IP address changed
1168 C4::Context->_unset_userenv($sessionID);
1171 return ("expired", undef);
1173 $session->param('lasttime',time());
1174 my $flags = haspermission($userid, $flagsrequired);
1176 return ("ok", $sessionID);
1179 C4::Context->_unset_userenv($sessionID);
1182 return ("failed", undef);
1186 return ("expired", undef);
1193 my $session = get_session($sessionID);
1195 Given a session ID, retrieve the CGI::Session object used to store
1196 the session's state. The session object can be used to store
1197 data that needs to be accessed by different scripts during a
1200 If the C<$sessionID> parameter is an empty string, a new session
1206 my $sessionID = shift;
1207 my $storage_method = C4::Context->preference('SessionStorage');
1208 my $dbh = C4::Context->dbh;
1210 if ($storage_method eq 'mysql'){
1211 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1213 elsif ($storage_method eq 'Pg') {
1214 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1217 # catch all defaults to tmp should work on all systems
1218 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1225 my ( $dbh, $userid, $password ) = @_;
1227 $debug and print STDERR "## checkpw - checking LDAP\n";
1228 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1229 ($retval) and return ($retval,$retcard);
1235 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1237 $sth->execute($userid);
1239 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1240 $surname, $branchcode, $flags )
1242 if ( ( md5_base64($password) eq $md5password ) and ($md5password ne "!") ) {
1244 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1245 $firstname, $surname, $branchcode, $flags );
1246 return 1, $cardnumber;
1251 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1253 $sth->execute($userid);
1255 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1256 $surname, $branchcode, $flags )
1258 if ( md5_base64($password) eq $md5password ) {
1260 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1261 $firstname, $surname, $branchcode, $flags );
1265 if ( $userid && $userid eq C4::Context->config('user')
1266 && "$password" eq C4::Context->config('pass') )
1269 # Koha superuser account
1270 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1273 if ( $userid && $userid eq 'demo'
1274 && "$password" eq 'demo'
1275 && C4::Context->config('demo') )
1278 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1279 # some features won't be effective : modify systempref, modify MARC structure,
1287 my $authflags = getuserflags($flags, $userid, [$dbh]);
1289 Translates integer flags into permissions strings hash.
1291 C<$flags> is the integer userflags value ( borrowers.userflags )
1292 C<$userid> is the members.userid, used for building subpermissions
1293 C<$authflags> is a hashref of permissions
1300 my $dbh = @_ ? shift : C4::Context->dbh;
1302 $flags = 0 unless $flags;
1303 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1306 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1307 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1308 $userflags->{$flag} = 1;
1311 $userflags->{$flag} = 0;
1315 # get subpermissions and merge with top-level permissions
1316 my $user_subperms = get_user_subpermissions($userid);
1317 foreach my $module (keys %$user_subperms) {
1318 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1319 $userflags->{$module} = $user_subperms->{$module};
1325 =item get_user_subpermissions
1329 my $user_perm_hashref = get_user_subpermissions($userid);
1333 Given the userid (note, not the borrowernumber) of a staff user,
1334 return a hashref of hashrefs of the specific subpermissions
1335 accorded to the user. An example return is
1339 export_catalog => 1,
1340 import_patrons => 1,
1344 The top-level hash-key is a module or function code from
1345 userflags.flag, while the second-level key is a code
1348 The results of this function do not give a complete picture
1349 of the functions that a staff user can access; it is also
1350 necessary to check borrowers.flags.
1354 sub get_user_subpermissions {
1357 my $dbh = C4::Context->dbh;
1358 my $sth = $dbh->prepare("SELECT flag, user_permissions.code as code
1359 FROM user_permissions
1360 JOIN permissions USING (module_bit, code)
1361 JOIN userflags ON (permissions.module_bit = userflags.bit)
1362 JOIN borrowers ON (user_permissions.borrowernumber=borrowers.borrowernumber)
1364 $sth->execute($userid);
1366 my $user_perms = {};
1367 while (my $perm = $sth->fetchrow_hashref) {
1368 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1373 =item get_all_subpermissions
1377 my $perm_hashref = get_all_subpermissions();
1381 Returns a hashref of hashrefs defining all specific
1382 permissions currently defined. The return value
1383 has the same structure as that of C<get_user_subpermissions>,
1384 except that the innermost hash value is the description
1385 of the subpermission.
1389 sub get_all_subpermissions {
1390 my $dbh = C4::Context->dbh;
1391 my $sth = $dbh->prepare("SELECT flag, code, description
1393 JOIN userflags ON (module_bit = bit)");
1397 while (my $perm = $sth->fetchrow_hashref) {
1398 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1405 $flags = ($userid, $flagsrequired);
1407 C<$userid> the userid of the member
1408 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1410 Returns member's flags or 0 if a permission is not met.
1415 my ($userid, $flagsrequired) = @_;
1416 my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1417 $sth->execute($userid);
1418 my $flags = getuserflags( $sth->fetchrow(), $userid );
1419 if ( $userid eq C4::Context->config('user') ) {
1420 # Super User Account from /etc/koha.conf
1421 $flags->{'superlibrarian'} = 1;
1423 elsif ( $userid eq 'demo' && C4::Context->config('demo') ) {
1424 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1425 $flags->{'superlibrarian'} = 1;
1427 return $flags if $flags->{superlibrarian};
1428 foreach my $module ( keys %$flagsrequired ) {
1429 if (C4::Context->preference('GranularPermissions')) {
1430 my $subperm = $flagsrequired->{$module};
1431 if ($subperm eq '*') {
1432 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1434 return 0 unless ( $flags->{$module} == 1 or
1435 ( ref($flags->{$module}) and
1436 exists $flags->{$module}->{$subperm} and
1437 $flags->{$module}->{$subperm} == 1
1442 return 0 unless ( $flags->{$module} );
1446 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1450 sub getborrowernumber {
1452 my $userenv = C4::Context->userenv;
1453 if ( defined( $userenv ) && ref( $userenv ) eq 'HASH' && $userenv->{number} ) {
1454 return $userenv->{number};
1456 my $dbh = C4::Context->dbh;
1457 for my $field ( 'userid', 'cardnumber' ) {
1459 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1460 $sth->execute($userid);
1462 my ($bnumber) = $sth->fetchrow;
1469 END { } # module clean-up code here (global destructor)