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);
22 use Storable qw(thaw freeze);
28 use C4::Output; # to get the template
31 use C4::Branch; # GetBranches
32 use C4::VirtualShelves;
33 use POSIX qw/strftime/;
36 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap);
39 $VERSION = 3.02; # set version for version checking
40 $debug = $ENV{DEBUG} || 0 ;
42 @EXPORT = qw(&checkauth &get_template_and_user &haspermission &get_user_subpermissions);
43 @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &get_all_subpermissions &get_user_subpermissions);
44 %EXPORT_TAGS = (EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)]);
45 $ldap = C4::Context->config('useldapserver') || 0;
47 require C4::Auth_with_ldap; # no import
48 import C4::Auth_with_ldap qw(checkpw_ldap);
54 C4::Auth - Authenticates Koha users
64 my ($template, $borrowernumber, $cookie)
65 = get_template_and_user(
67 template_name => "opac-main.tmpl",
71 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
75 output_html_with_http_headers $query, $cookie, $template->output;
79 The main function of this module is to provide
80 authentification. However the get_template_and_user function has
81 been provided so that a users login information is passed along
82 automatically. This gets loaded into the template.
88 =item get_template_and_user
90 my ($template, $borrowernumber, $cookie)
91 = get_template_and_user(
93 template_name => "opac-main.tmpl",
97 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
101 This call passes the C<query>, C<flagsrequired> and C<authnotrequired>
102 to C<&checkauth> (in this module) to perform authentification.
103 See C<&checkauth> for an explanation of these parameters.
105 The C<template_name> is then used to find the correct template for
106 the page. The authenticated users details are loaded onto the
107 template in the HTML::Template LOOP variable C<USER_INFO>. Also the
108 C<sessionID> is passed to the template. This can be used in templates
109 if cookies are disabled. It needs to be put as and input to every
112 More information on the C<gettemplate> sub can be found in the
117 sub get_template_and_user {
120 gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'} );
121 my ( $user, $cookie, $sessionID, $flags ) = checkauth(
123 $in->{'authnotrequired'},
124 $in->{'flagsrequired'},
126 ) unless ($in->{'template_name'}=~/maintenance/);
129 my $insecure = C4::Context->preference('insecure');
130 if ($user or $insecure) {
132 # load the template variables for stylesheets and JavaScript
133 $template->param( css_libs => $in->{'css_libs'} );
134 $template->param( css_module => $in->{'css_module'} );
135 $template->param( css_page => $in->{'css_page'} );
136 $template->param( css_widgets => $in->{'css_widgets'} );
138 $template->param( js_libs => $in->{'js_libs'} );
139 $template->param( js_module => $in->{'js_module'} );
140 $template->param( js_page => $in->{'js_page'} );
141 $template->param( js_widgets => $in->{'js_widgets'} );
144 $template->param( loggedinusername => $user );
145 $template->param( sessionID => $sessionID );
147 my ($total, $pubshelves, $barshelves) = C4::Context->get_shelves_userenv();
148 if (defined($pubshelves)) {
149 $template->param( pubshelves => scalar (@$pubshelves),
150 pubshelvesloop => $pubshelves,
152 $template->param( pubtotal => $total->{'pubtotal'}, ) if ($total->{'pubtotal'} > scalar (@$pubshelves));
154 if (defined($barshelves)) {
155 $template->param( barshelves => scalar (@$barshelves),
156 barshelvesloop => $barshelves,
158 $template->param( bartotal => $total->{'bartotal'}, ) if ($total->{'bartotal'} > scalar (@$barshelves));
161 $borrowernumber = getborrowernumber($user);
162 my ( $borr ) = GetMemberDetails( $borrowernumber );
165 $template->param( "USER_INFO" => \@bordat );
167 my $all_perms = get_all_subpermissions();
169 my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow
170 editcatalogue updatecharges management tools editauthorities serials reports acquisition);
171 # We are going to use the $flags returned by checkauth
172 # to create the template's parameters that will indicate
173 # which menus the user can access.
174 if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) {
175 $template->param( CAN_user_circulate => 1 );
176 $template->param( CAN_user_catalogue => 1 );
177 $template->param( CAN_user_parameters => 1 );
178 $template->param( CAN_user_borrowers => 1 );
179 $template->param( CAN_user_permissions => 1 );
180 $template->param( CAN_user_reserveforothers => 1 );
181 $template->param( CAN_user_borrow => 1 );
182 $template->param( CAN_user_editcatalogue => 1 );
183 $template->param( CAN_user_updatecharges => 1 );
184 $template->param( CAN_user_acquisition => 1 );
185 $template->param( CAN_user_management => 1 );
186 $template->param( CAN_user_tools => 1 );
187 $template->param( CAN_user_editauthorities => 1 );
188 $template->param( CAN_user_serials => 1 );
189 $template->param( CAN_user_reports => 1 );
190 $template->param( CAN_user_staffaccess => 1 );
191 foreach my $module (keys %$all_perms) {
192 foreach my $subperm (keys %{ $all_perms->{$module} }) {
193 $template->param( "CAN_user_${module}_${subperm}" => 1 );
198 if (C4::Context->preference('GranularPermissions')) {
200 foreach my $module (keys %$all_perms) {
201 if ( $flags->{$module} == 1) {
202 foreach my $subperm (keys %{ $all_perms->{$module} }) {
203 $template->param( "CAN_user_${module}_${subperm}" => 1 );
205 } elsif ( ref($flags->{$module}) ) {
206 foreach my $subperm (keys %{ $flags->{$module} } ) {
207 $template->param( "CAN_user_${module}_${subperm}" => 1 );
213 foreach my $module (keys %$all_perms) {
214 foreach my $subperm (keys %{ $all_perms->{$module} }) {
215 $template->param( "CAN_user_${module}_${subperm}" => 1 );
221 foreach my $module (keys %$flags) {
222 if ( $flags->{$module} == 1 or ref($flags->{$module}) ) {
223 $template->param( "CAN_user_$module" => 1 );
224 if ($module eq "parameters") {
225 $template->param( CAN_user_management => 1 );
230 # Logged-in opac search history
231 # If the requested template is an opac one and opac search history is enabled
232 if ($in->{'type'} == "opac" && C4::Context->preference('EnableOpacSearchHistory')) {
233 my $dbh = C4::Context->dbh;
234 my $query = "SELECT COUNT(*) FROM search_history WHERE userid=?";
235 my $sth = $dbh->prepare($query);
236 $sth->execute($borrowernumber);
238 # If at least one search has already been performed
239 if ($sth->fetchrow_array > 0) {
240 # We show the link in opac
241 $template->param(ShowOpacRecentSearchLink => 1);
244 # And if there's a cookie with searches performed when the user was not logged in,
245 # we add them to the logged-in search history
247 my $searchcookie = $in->{'query'}->cookie('KohaOpacRecentSearches');
249 $searchcookie = uri_unescape($searchcookie);
250 if (thaw($searchcookie)) {
251 @recentSearches = @{thaw($searchcookie)};
254 if (@recentSearches > 0) {
255 my $query = "INSERT INTO search_history(userid, sessionid, query_desc, query_cgi, total, time) VALUES";
257 foreach my $asearch (@recentSearches) {
259 $query .= $borrowernumber . ", ";
260 $query .= '"' . $in->{'query'}->cookie("CGISESSID") . "\", ";
261 $query .= '"' . $asearch->{'query_desc'} . "\", ";
262 $query .= '"' . $asearch->{'query_cgi'} . "\", ";
263 $query .= $asearch->{'total'} . ", ";
264 $query .= 'FROM_UNIXTIME(' . $asearch->{'time'} . "))";
265 if ($icount < @recentSearches) { $query .= ", ";}
269 my $sth = $dbh->prepare($query);
272 # And then, delete the cookie's content
273 my $newsearchcookie = $in->{'query'}->cookie(
274 -name => 'KohaOpacRecentSearches',
275 -value => freeze([]),
278 $cookie = [$cookie, $newsearchcookie];
283 else { # if this is an anonymous session, setup to display public lists...
285 # load the template variables for stylesheets and JavaScript
286 $template->param( css_libs => $in->{'css_libs'} );
287 $template->param( css_module => $in->{'css_module'} );
288 $template->param( css_page => $in->{'css_page'} );
289 $template->param( css_widgets => $in->{'css_widgets'} );
291 $template->param( js_libs => $in->{'js_libs'} );
292 $template->param( js_module => $in->{'js_module'} );
293 $template->param( js_page => $in->{'js_page'} );
294 $template->param( js_widgets => $in->{'js_widgets'} );
296 $template->param( sessionID => $sessionID );
298 my ($total, $pubshelves) = C4::Context->get_shelves_userenv(); # an anonymous user has no 'barshelves'...
299 if (defined(($pubshelves))) {
300 $template->param( pubshelves => scalar (@$pubshelves),
301 pubshelvesloop => $pubshelves,
303 $template->param( pubtotal => $total->{'pubtotal'}, ) if ($total->{'pubtotal'} > scalar (@$pubshelves));
307 # Anonymous opac search history
308 # If opac search history is enabled and at least one search has already been performed
309 if (C4::Context->preference('EnableOpacSearchHistory') && $in->{'query'}->cookie('KohaOpacRecentSearches')) {
310 # We show the link in opac
311 if (thaw(uri_unescape($in->{'query'}->cookie('KohaOpacRecentSearches')))) {
312 my @recentSearches = @{thaw(uri_unescape($in->{'query'}->cookie('KohaOpacRecentSearches')))};
313 if (@recentSearches > 0) {
314 $template->param(ShowOpacRecentSearchLink => 1);
319 # these template parameters are set the same regardless of $in->{'type'}
321 "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
322 EnhancedMessagingPreferences => C4::Context->preference('EnhancedMessagingPreferences'),
323 GoogleJackets => C4::Context->preference("GoogleJackets"),
324 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
325 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
326 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
327 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
328 TagsEnabled => C4::Context->preference("TagsEnabled"),
329 hide_marc => C4::Context->preference("hide_marc"),
330 'item-level_itypes' => C4::Context->preference('item-level_itypes'),
331 patronimages => C4::Context->preference("patronimages"),
332 singleBranchMode => C4::Context->preference("singleBranchMode"),
335 if ( $in->{'type'} eq "intranet" ) {
337 AmazonContent => C4::Context->preference("AmazonContent"),
338 AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
339 AutoLocation => C4::Context->preference("AutoLocation"),
340 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
341 CircAutocompl => C4::Context->preference("CircAutocompl"),
342 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
343 IndependantBranches => C4::Context->preference("IndependantBranches"),
344 IntranetNav => C4::Context->preference("IntranetNav"),
345 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
346 LibraryName => C4::Context->preference("LibraryName"),
347 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
348 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
349 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
350 canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
351 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
352 intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
353 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
354 intranetuserjs => C4::Context->preference("intranetuserjs"),
355 intranetbookbag => C4::Context->preference("intranetbookbag"),
356 noItemTypeImages => C4::Context->preference("noItemTypeImages"),
357 suggestion => C4::Context->preference("suggestion"),
358 virtualshelves => C4::Context->preference("virtualshelves"),
359 StaffSerialIssueDisplayCount => C4::Context->preference("StaffSerialIssueDisplayCount"),
360 NoZebra => C4::Context->preference('NoZebra'),
364 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
365 #TODO : replace LibraryName syspref with 'system name', and remove this html processing
366 my $LibraryNameTitle = C4::Context->preference("LibraryName");
367 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
368 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
369 # variables passed from CGI: opac_css_override and opac_search_limits.
370 my $opac_search_limit = $ENV{'OPAC_SEARCH_LIMIT'};
371 my $opac_limit_override = $ENV{'OPAC_LIMIT_OVERRIDE'};
372 my $mylibraryfirst = C4::Context->preference("SearchMyLibraryFirst");
374 if($opac_limit_override && ($opac_search_limit =~ /branch:(\w+)/) ){
375 $opac_name = C4::Branch::GetBranchName($1) # opac_search_limit is a branch, so we use it.
376 } elsif($mylibraryfirst){
377 $opac_name = C4::Branch::GetBranchName($mylibraryfirst);
380 AmazonContent => "" . C4::Context->preference("AmazonContent"),
381 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
382 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
383 LibraryName => "" . C4::Context->preference("LibraryName"),
384 LibraryNameTitle => "" . $LibraryNameTitle,
385 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
386 OPACAmazonSimilarItems => "" . C4::Context->preference("OPACAmazonSimilarItems"),
387 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
388 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
389 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
390 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
391 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
392 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
393 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
394 OPACBaseURL => ($in->{'query'}->https() ? "https://" : "http://") . $ENV{'SERVER_NAME'} .
395 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
396 opac_name => $opac_name,
397 opac_css_override => $ENV{'OPAC_CSS_OVERRIDE'},
398 opac_search_limit => $opac_search_limit,
399 opac_limit_override => $opac_limit_override,
400 OpacBrowser => C4::Context->preference("OpacBrowser"),
401 OpacCloud => C4::Context->preference("OpacCloud"),
402 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
403 OpacNav => "" . C4::Context->preference("OpacNav"),
404 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
405 OPACPatronDetails => C4::Context->preference("OPACPatronDetails"),
406 OPACFinesTab => C4::Context->preference("OPACFinesTab"),
407 OpacTopissue => C4::Context->preference("OpacTopissue"),
408 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
409 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
410 'Version' => C4::Context->preference('Version'),
411 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
412 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
413 hidelostitems => C4::Context->preference("hidelostitems"),
414 mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '',
415 opaclayoutstylesheet => "" . C4::Context->preference("opaclayoutstylesheet"),
416 opaccolorstylesheet => "" . C4::Context->preference("opaccolorstylesheet"),
417 opacstylesheet => "" . C4::Context->preference("opacstylesheet"),
418 opacbookbag => "" . C4::Context->preference("opacbookbag"),
419 opaccredits => "" . C4::Context->preference("opaccredits"),
420 opacheader => "" . C4::Context->preference("opacheader"),
421 opaclanguagesdisplay => "" . C4::Context->preference("opaclanguagesdisplay"),
422 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
423 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
424 opacuserjs => C4::Context->preference("opacuserjs"),
425 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
426 reviewson => C4::Context->preference("reviewson"),
427 suggestion => "" . C4::Context->preference("suggestion"),
428 virtualshelves => "" . C4::Context->preference("virtualshelves"),
429 OPACSerialIssueDisplayCount => C4::Context->preference("OPACSerialIssueDisplayCount"),
432 $template->param(listloop=>[{shelfname=>"Freelist", shelfnumber=>110}]);
433 return ( $template, $borrowernumber, $cookie, $flags);
438 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
440 Verifies that the user is authorized to run this script. If
441 the user is authorized, a (userid, cookie, session-id, flags)
442 quadruple is returned. If the user is not authorized but does
443 not have the required privilege (see $flagsrequired below), it
444 displays an error page and exits. Otherwise, it displays the
445 login page and exits.
447 Note that C<&checkauth> will return if and only if the user
448 is authorized, so it should be called early on, before any
449 unfinished operations (e.g., if you've opened a file, then
450 C<&checkauth> won't close it for you).
452 C<$query> is the CGI object for the script calling C<&checkauth>.
454 The C<$noauth> argument is optional. If it is set, then no
455 authorization is required for the script.
457 C<&checkauth> fetches user and session information from C<$query> and
458 ensures that the user is authorized to run scripts that require
461 The C<$flagsrequired> argument specifies the required privileges
462 the user must have if the username and password are correct.
463 It should be specified as a reference-to-hash; keys in the hash
464 should be the "flags" for the user, as specified in the Members
465 intranet module. Any key specified must correspond to a "flag"
466 in the userflags table. E.g., { circulate => 1 } would specify
467 that the user must have the "circulate" privilege in order to
468 proceed. To make sure that access control is correct, the
469 C<$flagsrequired> parameter must be specified correctly.
471 If the GranularPermissions system preference is ON, the
472 value of each key in the C<flagsrequired> hash takes on an additional
477 The user must have access to all subfunctions of the module
478 specified by the hash key.
482 The user must have access to at least one subfunction of the module
483 specified by the hash key.
485 =item specific permission, e.g., 'export_catalog'
487 The user must have access to the specific subfunction list, which
488 must correspond to a row in the permissions table.
490 The C<$type> argument specifies whether the template should be
491 retrieved from the opac or intranet directory tree. "opac" is
492 assumed if it is not specified; however, if C<$type> is specified,
493 "intranet" is assumed if it is not "opac".
495 If C<$query> does not have a valid session ID associated with it
496 (i.e., the user has not logged in) or if the session has expired,
497 C<&checkauth> presents the user with a login page (from the point of
498 view of the original script, C<&checkauth> does not return). Once the
499 user has authenticated, C<&checkauth> restarts the original script
500 (this time, C<&checkauth> returns).
502 The login page is provided using a HTML::Template, which is set in the
503 systempreferences table or at the top of this file. The variable C<$type>
504 selects which template to use, either the opac or the intranet
505 authentification template.
507 C<&checkauth> returns a user ID, a cookie, and a session ID. The
508 cookie should be sent back to the browser; it verifies that the user
513 sub _version_check ($$) {
517 # If Version syspref is unavailable, it means Koha is beeing installed,
518 # and so we must redirect to OPAC maintenance page or to the WebInstaller
519 # also, if OpacMaintenance is ON, OPAC should redirect to maintenance
520 if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') {
521 warn "OPAC Install required, redirecting to maintenance";
522 print $query->redirect("/cgi-bin/koha/maintenance.pl");
524 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
525 if ($type ne 'opac') {
526 warn "Install required, redirecting to Installer";
527 print $query->redirect("/cgi-bin/koha/installer/install.pl");
530 warn "OPAC Install required, redirecting to maintenance";
531 print $query->redirect("/cgi-bin/koha/maintenance.pl");
536 # check that database and koha version are the same
537 # there is no DB version, it's a fresh install,
538 # go to web installer
539 # there is a DB version, compare it to the code version
540 my $kohaversion=C4::Context::KOHAVERSION;
541 # remove the 3 last . to have a Perl number
542 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
543 $debug and print STDERR "kohaversion : $kohaversion\n";
544 if ($version < $kohaversion){
545 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
546 if ($type ne 'opac'){
547 warn sprintf($warning, 'Installer');
548 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
550 warn sprintf("OPAC: " . $warning, 'maintenance');
551 print $query->redirect("/cgi-bin/koha/maintenance.pl");
559 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
560 printf L join("\n",@_);
566 $debug and warn "Checking Auth";
567 # $authnotrequired will be set for scripts which will run without authentication
568 my $authnotrequired = shift;
569 my $flagsrequired = shift;
571 $type = 'opac' unless $type;
573 my $dbh = C4::Context->dbh;
574 my $timeout = C4::Context->preference('timeout');
576 if ($timeout =~ /(\d+)[dD]/) {
577 $timeout = $1 * 86400;
579 $timeout = 600 unless $timeout;
581 _version_check($type,$query);
585 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
586 my $logout = $query->param('logout.x');
588 if ( $userid = $ENV{'REMOTE_USER'} ) {
589 # Using Basic Authentication, no cookies required
590 $cookie = $query->cookie(
591 -name => 'CGISESSID',
597 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
598 my $session = get_session($sessionID);
599 C4::Context->_new_userenv($sessionID);
600 my ($ip, $lasttime, $sessiontype);
602 C4::Context::set_userenv(
603 $session->param('number'), $session->param('id'),
604 $session->param('cardnumber'), $session->param('firstname'),
605 $session->param('surname'), $session->param('branch'),
606 $session->param('branchname'), $session->param('flags'),
607 $session->param('emailaddress'), $session->param('branchprinter')
609 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
610 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
611 C4::Context::set_shelves_userenv('tot',$session->param('totshelves'));
612 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
613 $ip = $session->param('ip');
614 $lasttime = $session->param('lasttime');
615 $userid = $session->param('id');
616 $sessiontype = $session->param('sessiontype');
619 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
620 #if a user enters an id ne to the id in the current session, we need to log them in...
621 #first we need to clear the anonymous session...
622 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
625 C4::Context->_unset_userenv($sessionID);
630 # voluntary logout the user
633 C4::Context->_unset_userenv($sessionID);
634 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,(strftime "%c",localtime));
638 elsif ( $lasttime < time() - $timeout ) {
640 $info{'timed_out'} = 1;
642 C4::Context->_unset_userenv($sessionID);
643 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,(strftime "%c",localtime));
647 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
648 # Different ip than originally logged in from
649 $info{'oldip'} = $ip;
650 $info{'newip'} = $ENV{'REMOTE_ADDR'};
651 $info{'different_ip'} = 1;
653 C4::Context->_unset_userenv($sessionID);
654 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,(strftime "%c",localtime), $info{'newip'});
659 $cookie = $query->cookie( CGISESSID => $session->id );
660 $session->param('lasttime',time());
661 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...
662 $flags = haspermission($userid, $flagsrequired);
666 $info{'nopermission'} = 1;
671 unless ($userid || $sessionID) {
672 #we initiate a session prior to checking for a username to allow for anonymous sessions...
673 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
674 my $sessionID = $session->id;
675 C4::Context->_new_userenv($sessionID);
676 $cookie = $query->cookie(CGISESSID => $sessionID);
677 if ( $userid = $query->param('userid') ) {
678 my $password = $query->param('password');
679 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
681 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},(strftime "%c",localtime));
682 if ( $flags = haspermission($userid, $flagsrequired) ) {
686 $info{'nopermission'} = 1;
687 C4::Context->_unset_userenv($sessionID);
690 my ($borrowernumber, $firstname, $surname, $userflags,
691 $branchcode, $branchname, $branchprinter, $emailaddress);
693 if ( $return == 1 ) {
695 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
696 branches.branchname as branchname,
697 branches.branchprinter as branchprinter,
700 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
702 my $sth = $dbh->prepare("$select where userid=?");
703 $sth->execute($userid);
704 unless ($sth->rows) {
705 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
706 $sth = $dbh->prepare("$select where cardnumber=?");
707 $sth->execute($cardnumber);
708 unless ($sth->rows) {
709 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
710 $sth->execute($userid);
711 unless ($sth->rows) {
712 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
717 ($borrowernumber, $firstname, $surname, $userflags,
718 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
719 $debug and print STDERR "AUTH_3 results: " .
720 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
722 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
725 # launch a sequence to check if we have a ip for the branch, i
726 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
728 my $ip = $ENV{'REMOTE_ADDR'};
729 # if they specify at login, use that
730 if ($query->param('branch')) {
731 $branchcode = $query->param('branch');
732 $branchname = GetBranchName($branchcode);
734 my $branches = GetBranches();
735 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
736 # we have to check they are coming from the right ip range
737 my $domain = $branches->{$branchcode}->{'branchip'};
738 if ($ip !~ /^$domain/){
740 $info{'wrongip'} = 1;
745 foreach my $br ( keys %$branches ) {
746 # now we work with the treatment of ip
747 my $domain = $branches->{$br}->{'branchip'};
748 if ( $domain && $ip =~ /^$domain/ ) {
749 $branchcode = $branches->{$br}->{'branchcode'};
751 # new op dev : add the branchprinter and branchname in the cookie
752 $branchprinter = $branches->{$br}->{'branchprinter'};
753 $branchname = $branches->{$br}->{'branchname'};
756 $session->param('number',$borrowernumber);
757 $session->param('id',$userid);
758 $session->param('cardnumber',$cardnumber);
759 $session->param('firstname',$firstname);
760 $session->param('surname',$surname);
761 $session->param('branch',$branchcode);
762 $session->param('branchname',$branchname);
763 $session->param('flags',$userflags);
764 $session->param('emailaddress',$emailaddress);
765 $session->param('ip',$session->remote_addr());
766 $session->param('lasttime',time());
767 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
769 elsif ( $return == 2 ) {
770 #We suppose the user is the superlibrarian
772 $session->param('number',0);
773 $session->param('id',C4::Context->config('user'));
774 $session->param('cardnumber',C4::Context->config('user'));
775 $session->param('firstname',C4::Context->config('user'));
776 $session->param('surname',C4::Context->config('user'));
777 $session->param('branch','NO_LIBRARY_SET');
778 $session->param('branchname','NO_LIBRARY_SET');
779 $session->param('flags',1);
780 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
781 $session->param('ip',$session->remote_addr());
782 $session->param('lasttime',time());
784 C4::Context::set_userenv(
785 $session->param('number'), $session->param('id'),
786 $session->param('cardnumber'), $session->param('firstname'),
787 $session->param('surname'), $session->param('branch'),
788 $session->param('branchname'), $session->param('flags'),
789 $session->param('emailaddress'), $session->param('branchprinter')
792 # Grab borrower's shelves and public shelves and add them to the session
793 # $row_count determines how many records are returned from the db query
794 # and the number of lists to be displayed of each type in the 'Lists' button drop down
795 my $row_count = 10; # FIXME:This probably should be a syspref
796 my ($total, $totshelves, $barshelves, $pubshelves);
797 ($barshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(1, $row_count, $borrowernumber);
798 $total->{'bartotal'} = $totshelves;
799 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
800 $total->{'pubtotal'} = $totshelves;
801 $session->param('barshelves', $barshelves->[0]);
802 $session->param('pubshelves', $pubshelves->[0]);
803 $session->param('totshelves', $total);
805 C4::Context::set_shelves_userenv('bar',$barshelves->[0]);
806 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
807 C4::Context::set_shelves_userenv('tot',$total);
811 $info{'invalid_username_or_password'} = 1;
812 C4::Context->_unset_userenv($sessionID);
815 } # END if ( $userid = $query->param('userid') )
816 elsif ($type eq "opac") {
817 # if we are here this is an anonymous session; add public lists to it and a few other items...
818 # anonymous sessions are created only for the OPAC
819 $debug and warn "Initiating an anonymous session...";
821 # Grab the public shelves and add to the session...
822 my $row_count = 20; # FIXME:This probably should be a syspref
823 my ($total, $totshelves, $pubshelves);
824 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
825 $total->{'pubtotal'} = $totshelves;
826 $session->param('pubshelves', $pubshelves->[0]);
827 $session->param('totshelves', $total);
828 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
829 C4::Context::set_shelves_userenv('tot',$total);
831 # setting a couple of other session vars...
832 $session->param('ip',$session->remote_addr());
833 $session->param('lasttime',time());
834 $session->param('sessiontype','anon');
836 } # END unless ($userid)
837 my $insecure = C4::Context->boolean_preference('insecure');
839 # finished authentification, now respond
840 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
844 $cookie = $query->cookie( CGISESSID => '' );
846 return ( $userid, $cookie, $sessionID, $flags );
851 # AUTH rejected, show the login/password template, after checking the DB.
855 # get the inputs from the incoming query
857 foreach my $name ( param $query) {
858 (next) if ( $name eq 'userid' || $name eq 'password' );
859 my $value = $query->param($name);
860 push @inputs, { name => $name, value => $value };
862 # get the branchloop, which we need for authentication
863 my $branches = GetBranches();
865 for my $branch_hash (sort keys %$branches) {
866 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
869 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
870 my $template = gettemplate( $template_name, $type, $query );
871 $template->param(branchloop => \@branch_loop,);
875 suggestion => C4::Context->preference("suggestion"),
876 virtualshelves => C4::Context->preference("virtualshelves"),
877 LibraryName => C4::Context->preference("LibraryName"),
878 opacuserlogin => C4::Context->preference("opacuserlogin"),
879 OpacNav => C4::Context->preference("OpacNav"),
880 opaccredits => C4::Context->preference("opaccredits"),
881 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
882 opacsmallimage => C4::Context->preference("opacsmallimage"),
883 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
884 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
885 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
886 opacuserjs => C4::Context->preference("opacuserjs"),
887 opacbookbag => "" . C4::Context->preference("opacbookbag"),
888 OpacCloud => C4::Context->preference("OpacCloud"),
889 OpacTopissue => C4::Context->preference("OpacTopissue"),
890 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
891 OpacBrowser => C4::Context->preference("OpacBrowser"),
892 opacheader => C4::Context->preference("opacheader"),
893 TagsEnabled => C4::Context->preference("TagsEnabled"),
894 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
895 intranetcolorstylesheet =>
896 C4::Context->preference("intranetcolorstylesheet"),
897 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
898 intranetbookbag => C4::Context->preference("intranetbookbag"),
899 IntranetNav => C4::Context->preference("IntranetNav"),
900 intranetuserjs => C4::Context->preference("intranetuserjs"),
901 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
902 IndependantBranches=> C4::Context->preference("IndependantBranches"),
903 AutoLocation => C4::Context->preference("AutoLocation"),
904 wrongip => $info{'wrongip'}
906 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
908 my $self_url = $query->url( -absolute => 1 );
911 LibraryName => C4::Context->preference("LibraryName"),
913 $template->param( \%info );
914 # $cookie = $query->cookie(CGISESSID => $session->id
916 print $query->header(
917 -type => 'text/html',
927 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
929 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
930 cookie, determine if the user has the privileges specified by C<$userflags>.
932 C<check_api_auth> is is meant for authenticating users of web services, and
933 consequently will always return and will not attempt to redirect the user
936 If a valid session cookie is already present, check_api_auth will return a status
937 of "ok", the cookie, and the Koha session ID.
939 If no session cookie is present, check_api_auth will check the 'userid' and 'password
940 parameters and create a session cookie and Koha session if the supplied credentials
943 Possible return values in C<$status> are:
947 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
949 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
951 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
953 =item "expired -- session cookie has expired; API user should resubmit userid and password
961 my $flagsrequired = shift;
963 my $dbh = C4::Context->dbh;
964 my $timeout = C4::Context->preference('timeout');
965 $timeout = 600 unless $timeout;
967 unless (C4::Context->preference('Version')) {
968 # database has not been installed yet
969 return ("maintenance", undef, undef);
971 my $kohaversion=C4::Context::KOHAVERSION;
972 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
973 if (C4::Context->preference('Version') < $kohaversion) {
974 # database in need of version update; assume that
975 # no API should be called while databsae is in
977 return ("maintenance", undef, undef);
980 # FIXME -- most of what follows is a copy-and-paste
981 # of code from checkauth. There is an obvious need
982 # for refactoring to separate the various parts of
983 # the authentication code, but as of 2007-11-19 this
984 # is deferred so as to not introduce bugs into the
985 # regular authentication code for Koha 3.0.
987 # see if we have a valid session cookie already
988 # however, if a userid parameter is present (i.e., from
989 # a form submission, assume that any current cookie
991 my $sessionID = undef;
992 unless ($query->param('userid')) {
993 $sessionID = $query->cookie("CGISESSID");
996 my $session = get_session($sessionID);
997 C4::Context->_new_userenv($sessionID);
999 C4::Context::set_userenv(
1000 $session->param('number'), $session->param('id'),
1001 $session->param('cardnumber'), $session->param('firstname'),
1002 $session->param('surname'), $session->param('branch'),
1003 $session->param('branchname'), $session->param('flags'),
1004 $session->param('emailaddress'), $session->param('branchprinter')
1007 my $ip = $session->param('ip');
1008 my $lasttime = $session->param('lasttime');
1009 my $userid = $session->param('id');
1010 if ( $lasttime < time() - $timeout ) {
1013 C4::Context->_unset_userenv($sessionID);
1016 return ("expired", undef, undef);
1017 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1018 # IP address changed
1020 C4::Context->_unset_userenv($sessionID);
1023 return ("expired", undef, undef);
1025 my $cookie = $query->cookie( CGISESSID => $session->id );
1026 $session->param('lasttime',time());
1027 my $flags = haspermission($userid, $flagsrequired);
1029 return ("ok", $cookie, $sessionID);
1032 C4::Context->_unset_userenv($sessionID);
1035 return ("failed", undef, undef);
1039 return ("expired", undef, undef);
1043 my $userid = $query->param('userid');
1044 my $password = $query->param('password');
1045 unless ($userid and $password) {
1046 # caller did something wrong, fail the authenticateion
1047 return ("failed", undef, undef);
1049 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
1050 if ($return and haspermission($userid, $flagsrequired)) {
1051 my $session = get_session("");
1052 return ("failed", undef, undef) unless $session;
1054 my $sessionID = $session->id;
1055 C4::Context->_new_userenv($sessionID);
1056 my $cookie = $query->cookie(CGISESSID => $sessionID);
1057 if ( $return == 1 ) {
1059 $borrowernumber, $firstname, $surname,
1060 $userflags, $branchcode, $branchname,
1061 $branchprinter, $emailaddress
1065 "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=?"
1067 $sth->execute($userid);
1069 $borrowernumber, $firstname, $surname,
1070 $userflags, $branchcode, $branchname,
1071 $branchprinter, $emailaddress
1072 ) = $sth->fetchrow if ( $sth->rows );
1074 unless ($sth->rows ) {
1075 my $sth = $dbh->prepare(
1076 "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=?"
1078 $sth->execute($cardnumber);
1080 $borrowernumber, $firstname, $surname,
1081 $userflags, $branchcode, $branchname,
1082 $branchprinter, $emailaddress
1083 ) = $sth->fetchrow if ( $sth->rows );
1085 unless ( $sth->rows ) {
1086 $sth->execute($userid);
1088 $borrowernumber, $firstname, $surname, $userflags,
1089 $branchcode, $branchname, $branchprinter, $emailaddress
1090 ) = $sth->fetchrow if ( $sth->rows );
1094 my $ip = $ENV{'REMOTE_ADDR'};
1095 # if they specify at login, use that
1096 if ($query->param('branch')) {
1097 $branchcode = $query->param('branch');
1098 $branchname = GetBranchName($branchcode);
1100 my $branches = GetBranches();
1102 foreach my $br ( keys %$branches ) {
1103 # now we work with the treatment of ip
1104 my $domain = $branches->{$br}->{'branchip'};
1105 if ( $domain && $ip =~ /^$domain/ ) {
1106 $branchcode = $branches->{$br}->{'branchcode'};
1108 # new op dev : add the branchprinter and branchname in the cookie
1109 $branchprinter = $branches->{$br}->{'branchprinter'};
1110 $branchname = $branches->{$br}->{'branchname'};
1113 $session->param('number',$borrowernumber);
1114 $session->param('id',$userid);
1115 $session->param('cardnumber',$cardnumber);
1116 $session->param('firstname',$firstname);
1117 $session->param('surname',$surname);
1118 $session->param('branch',$branchcode);
1119 $session->param('branchname',$branchname);
1120 $session->param('flags',$userflags);
1121 $session->param('emailaddress',$emailaddress);
1122 $session->param('ip',$session->remote_addr());
1123 $session->param('lasttime',time());
1124 } elsif ( $return == 2 ) {
1125 #We suppose the user is the superlibrarian
1126 $session->param('number',0);
1127 $session->param('id',C4::Context->config('user'));
1128 $session->param('cardnumber',C4::Context->config('user'));
1129 $session->param('firstname',C4::Context->config('user'));
1130 $session->param('surname',C4::Context->config('user'));
1131 $session->param('branch','NO_LIBRARY_SET');
1132 $session->param('branchname','NO_LIBRARY_SET');
1133 $session->param('flags',1);
1134 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1135 $session->param('ip',$session->remote_addr());
1136 $session->param('lasttime',time());
1138 C4::Context::set_userenv(
1139 $session->param('number'), $session->param('id'),
1140 $session->param('cardnumber'), $session->param('firstname'),
1141 $session->param('surname'), $session->param('branch'),
1142 $session->param('branchname'), $session->param('flags'),
1143 $session->param('emailaddress'), $session->param('branchprinter')
1145 return ("ok", $cookie, $sessionID);
1147 return ("failed", undef, undef);
1152 =item check_cookie_auth
1154 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1156 Given a CGISESSID cookie set during a previous login to Koha, determine
1157 if the user has the privileges specified by C<$userflags>.
1159 C<check_cookie_auth> is meant for authenticating special services
1160 such as tools/upload-file.pl that are invoked by other pages that
1161 have been authenticated in the usual way.
1163 Possible return values in C<$status> are:
1167 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1169 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1171 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1173 =item "expired -- session cookie has expired; API user should resubmit userid and password
1179 sub check_cookie_auth {
1181 my $flagsrequired = shift;
1183 my $dbh = C4::Context->dbh;
1184 my $timeout = C4::Context->preference('timeout');
1185 $timeout = 600 unless $timeout;
1187 unless (C4::Context->preference('Version')) {
1188 # database has not been installed yet
1189 return ("maintenance", undef);
1191 my $kohaversion=C4::Context::KOHAVERSION;
1192 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1193 if (C4::Context->preference('Version') < $kohaversion) {
1194 # database in need of version update; assume that
1195 # no API should be called while databsae is in
1197 return ("maintenance", undef);
1200 # FIXME -- most of what follows is a copy-and-paste
1201 # of code from checkauth. There is an obvious need
1202 # for refactoring to separate the various parts of
1203 # the authentication code, but as of 2007-11-23 this
1204 # is deferred so as to not introduce bugs into the
1205 # regular authentication code for Koha 3.0.
1207 # see if we have a valid session cookie already
1208 # however, if a userid parameter is present (i.e., from
1209 # a form submission, assume that any current cookie
1211 unless (defined $cookie and $cookie) {
1212 return ("failed", undef);
1214 my $sessionID = $cookie;
1215 my $session = get_session($sessionID);
1216 C4::Context->_new_userenv($sessionID);
1218 C4::Context::set_userenv(
1219 $session->param('number'), $session->param('id'),
1220 $session->param('cardnumber'), $session->param('firstname'),
1221 $session->param('surname'), $session->param('branch'),
1222 $session->param('branchname'), $session->param('flags'),
1223 $session->param('emailaddress'), $session->param('branchprinter')
1226 my $ip = $session->param('ip');
1227 my $lasttime = $session->param('lasttime');
1228 my $userid = $session->param('id');
1229 if ( $lasttime < time() - $timeout ) {
1232 C4::Context->_unset_userenv($sessionID);
1235 return ("expired", undef);
1236 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1237 # IP address changed
1239 C4::Context->_unset_userenv($sessionID);
1242 return ("expired", undef);
1244 $session->param('lasttime',time());
1245 my $flags = haspermission($userid, $flagsrequired);
1247 return ("ok", $sessionID);
1250 C4::Context->_unset_userenv($sessionID);
1253 return ("failed", undef);
1257 return ("expired", undef);
1264 my $session = get_session($sessionID);
1266 Given a session ID, retrieve the CGI::Session object used to store
1267 the session's state. The session object can be used to store
1268 data that needs to be accessed by different scripts during a
1271 If the C<$sessionID> parameter is an empty string, a new session
1277 my $sessionID = shift;
1278 my $storage_method = C4::Context->preference('SessionStorage');
1279 my $dbh = C4::Context->dbh;
1281 if ($storage_method eq 'mysql'){
1282 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1284 elsif ($storage_method eq 'Pg') {
1285 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1288 # catch all defaults to tmp should work on all systems
1289 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1296 my ( $dbh, $userid, $password ) = @_;
1298 $debug and print "## checkpw - checking LDAP\n";
1299 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1300 ($retval) and return ($retval,$retcard);
1306 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1308 $sth->execute($userid);
1310 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1311 $surname, $branchcode, $flags )
1313 if ( md5_base64($password) eq $md5password ) {
1315 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1316 $firstname, $surname, $branchcode, $flags );
1317 return 1, $cardnumber;
1322 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1324 $sth->execute($userid);
1326 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1327 $surname, $branchcode, $flags )
1329 if ( md5_base64($password) eq $md5password ) {
1331 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1332 $firstname, $surname, $branchcode, $flags );
1336 if ( $userid && $userid eq C4::Context->config('user')
1337 && "$password" eq C4::Context->config('pass') )
1340 # Koha superuser account
1341 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1344 if ( $userid && $userid eq 'demo'
1345 && "$password" eq 'demo'
1346 && C4::Context->config('demo') )
1349 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1350 # some features won't be effective : modify systempref, modify MARC structure,
1358 my $authflags = getuserflags($flags, $userid, [$dbh]);
1360 Translates integer flags into permissions strings hash.
1362 C<$flags> is the integer userflags value ( borrowers.userflags )
1363 C<$userid> is the members.userid, used for building subpermissions
1364 C<$authflags> is a hashref of permissions
1371 my $dbh = @_ ? shift : C4::Context->dbh;
1373 $flags = 0 unless $flags;
1374 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1377 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1378 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1379 $userflags->{$flag} = 1;
1382 $userflags->{$flag} = 0;
1386 # get subpermissions and merge with top-level permissions
1387 my $user_subperms = get_user_subpermissions($userid);
1388 foreach my $module (keys %$user_subperms) {
1389 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1390 $userflags->{$module} = $user_subperms->{$module};
1396 =item get_user_subpermissions
1400 my $user_perm_hashref = get_user_subpermissions($userid);
1404 Given the userid (note, not the borrowernumber) of a staff user,
1405 return a hashref of hashrefs of the specific subpermissions
1406 accorded to the user. An example return is
1410 export_catalog => 1,
1411 import_patrons => 1,
1415 The top-level hash-key is a module or function code from
1416 userflags.flag, while the second-level key is a code
1419 The results of this function do not give a complete picture
1420 of the functions that a staff user can access; it is also
1421 necessary to check borrowers.flags.
1425 sub get_user_subpermissions {
1428 my $dbh = C4::Context->dbh;
1429 my $sth = $dbh->prepare("SELECT flag, user_permissions.code
1430 FROM user_permissions
1431 JOIN permissions USING (module_bit, code)
1432 JOIN userflags ON (module_bit = bit)
1433 JOIN borrowers USING (borrowernumber)
1435 $sth->execute($userid);
1437 my $user_perms = {};
1438 while (my $perm = $sth->fetchrow_hashref) {
1439 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1444 =item get_all_subpermissions
1448 my $perm_hashref = get_all_subpermissions();
1452 Returns a hashref of hashrefs defining all specific
1453 permissions currently defined. The return value
1454 has the same structure as that of C<get_user_subpermissions>,
1455 except that the innermost hash value is the description
1456 of the subpermission.
1460 sub get_all_subpermissions {
1461 my $dbh = C4::Context->dbh;
1462 my $sth = $dbh->prepare("SELECT flag, code, description
1464 JOIN userflags ON (module_bit = bit)");
1468 while (my $perm = $sth->fetchrow_hashref) {
1469 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1476 $flags = ($userid, $flagsrequired);
1478 C<$userid> the userid of the member
1479 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1481 Returns member's flags or 0 if a permission is not met.
1486 my ($userid, $flagsrequired) = @_;
1487 my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1488 $sth->execute($userid);
1489 my $flags = getuserflags( $sth->fetchrow(), $userid );
1490 if ( $userid eq C4::Context->config('user') ) {
1491 # Super User Account from /etc/koha.conf
1492 $flags->{'superlibrarian'} = 1;
1494 elsif ( $userid eq 'demo' && C4::Context->config('demo') ) {
1495 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1496 $flags->{'superlibrarian'} = 1;
1498 return $flags if $flags->{superlibrarian};
1499 foreach my $module ( keys %$flagsrequired ) {
1500 if (C4::Context->preference('GranularPermissions')) {
1501 my $subperm = $flagsrequired->{$module};
1502 if ($subperm eq '*') {
1503 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1505 return 0 unless ( $flags->{$module} == 1 or
1506 ( ref($flags->{$module}) and
1507 exists $flags->{$module}->{$subperm} and
1508 $flags->{$module}->{$subperm} == 1
1513 return 0 unless ( $flags->{$module} );
1517 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1521 sub getborrowernumber {
1523 my $userenv = C4::Context->userenv;
1524 if ( defined( $userenv ) && ref( $userenv ) eq 'HASH' && $userenv->{number} ) {
1525 return $userenv->{number};
1527 my $dbh = C4::Context->dbh;
1528 for my $field ( 'userid', 'cardnumber' ) {
1530 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1531 $sth->execute($userid);
1533 my ($bnumber) = $sth->fetchrow;
1540 END { } # module clean-up code here (global destructor)