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);
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);
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 dateformat => C4::Context->preference("dateformat"),
331 'item-level_itypes' => C4::Context->preference('item-level_itypes'),
332 patronimages => C4::Context->preference("patronimages"),
333 singleBranchMode => C4::Context->preference("singleBranchMode"),
334 SyndeticsClientCode => C4::Context->preference("SyndeticsClientCode"),
335 SyndeticsEnabled => C4::Context->preference("SyndeticsEnabled"),
336 SyndeticsCoverImages => C4::Context->preference("SyndeticsCoverImages"),
337 SyndeticsTOC => C4::Context->preference("SyndeticsTOC"),
338 SyndeticsSummary => C4::Context->preference("SyndeticsSummary"),
339 SyndeticsEditions => C4::Context->preference("SyndeticsEditions"),
340 SyndeticsExcerpt => C4::Context->preference("SyndeticsExcerpt"),
341 SyndeticsReviews => C4::Context->preference("SyndeticsReviews"),
342 SyndeticsAuthorNotes => C4::Context->preference("SyndeticsAuthorNotes"),
343 SyndeticsAwards => C4::Context->preference("SyndeticsAwards"),
344 SyndeticsSeries => C4::Context->preference("SyndeticsSeries"),
345 SyndeticsCoverImageSize => C4::Context->preference("SyndeticsCoverImageSize"),
348 if ( $in->{'type'} eq "intranet" ) {
350 AmazonEnabled => C4::Context->preference("AmazonEnabled"),
351 AmazonCoverImages => C4::Context->preference("AmazonCoverImages"),
352 AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
353 AutoLocation => C4::Context->preference("AutoLocation"),
354 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
355 CircAutocompl => C4::Context->preference("CircAutocompl"),
356 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
357 IndependantBranches => C4::Context->preference("IndependantBranches"),
358 IntranetNav => C4::Context->preference("IntranetNav"),
359 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
360 LibraryName => C4::Context->preference("LibraryName"),
361 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
362 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
363 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
364 canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
365 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
366 intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
367 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
368 intranetuserjs => C4::Context->preference("intranetuserjs"),
369 noItemTypeImages => C4::Context->preference("noItemTypeImages"),
370 suggestion => C4::Context->preference("suggestion"),
371 virtualshelves => C4::Context->preference("virtualshelves"),
372 StaffSerialIssueDisplayCount => C4::Context->preference("StaffSerialIssueDisplayCount"),
373 NoZebra => C4::Context->preference('NoZebra'),
377 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
378 #TODO : replace LibraryName syspref with 'system name', and remove this html processing
379 my $LibraryNameTitle = C4::Context->preference("LibraryName");
380 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
381 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
382 # variables passed from CGI: opac_css_override and opac_search_limits.
383 my $opac_search_limit = $ENV{'OPAC_SEARCH_LIMIT'};
384 my $opac_limit_override = $ENV{'OPAC_LIMIT_OVERRIDE'};
385 my $mylibraryfirst = C4::Context->preference("SearchMyLibraryFirst");
387 if($opac_limit_override && ($opac_search_limit =~ /branch:(\w+)/) ){
388 $opac_name = C4::Branch::GetBranchName($1) # opac_search_limit is a branch, so we use it.
389 } elsif($mylibraryfirst){
390 $opac_name = C4::Branch::GetBranchName($mylibraryfirst);
393 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
394 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
395 LibraryName => "" . C4::Context->preference("LibraryName"),
396 LibraryNameTitle => "" . $LibraryNameTitle,
397 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
398 OPACAmazonEnabled => C4::Context->preference("OPACAmazonEnabled"),
399 OPACAmazonCoverImages => C4::Context->preference("OPACAmazonCoverImages"),
400 OPACAmazonSimilarItems => "" . C4::Context->preference("OPACAmazonSimilarItems"),
401 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
402 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
403 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
404 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
405 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
406 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
407 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
408 OPACBaseURL => ($in->{'query'}->https() ? "https://" : "http://") . $ENV{'SERVER_NAME'} .
409 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
410 opac_name => $opac_name,
411 opac_css_override => $ENV{'OPAC_CSS_OVERRIDE'},
412 opac_search_limit => $opac_search_limit,
413 opac_limit_override => $opac_limit_override,
414 OpacBrowser => C4::Context->preference("OpacBrowser"),
415 OpacCloud => C4::Context->preference("OpacCloud"),
416 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
417 OpacNav => "" . C4::Context->preference("OpacNav"),
418 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
419 OPACPatronDetails => C4::Context->preference("OPACPatronDetails"),
420 OPACFinesTab => C4::Context->preference("OPACFinesTab"),
421 OpacTopissue => C4::Context->preference("OpacTopissue"),
422 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
423 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
424 'Version' => C4::Context->preference('Version'),
425 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
426 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
427 hidelostitems => C4::Context->preference("hidelostitems"),
428 mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '',
429 opaclayoutstylesheet => "" . C4::Context->preference("opaclayoutstylesheet"),
430 opaccolorstylesheet => "" . C4::Context->preference("opaccolorstylesheet"),
431 opacstylesheet => "" . C4::Context->preference("opacstylesheet"),
432 opacbookbag => "" . C4::Context->preference("opacbookbag"),
433 opaccredits => "" . C4::Context->preference("opaccredits"),
434 opacheader => "" . C4::Context->preference("opacheader"),
435 opaclanguagesdisplay => "" . C4::Context->preference("opaclanguagesdisplay"),
436 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
437 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
438 opacuserjs => C4::Context->preference("opacuserjs"),
439 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
440 reviewson => C4::Context->preference("reviewson"),
441 suggestion => "" . C4::Context->preference("suggestion"),
442 virtualshelves => "" . C4::Context->preference("virtualshelves"),
443 OPACSerialIssueDisplayCount => C4::Context->preference("OPACSerialIssueDisplayCount"),
446 return ( $template, $borrowernumber, $cookie, $flags);
451 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
453 Verifies that the user is authorized to run this script. If
454 the user is authorized, a (userid, cookie, session-id, flags)
455 quadruple is returned. If the user is not authorized due to
456 insufficent privileges (see $flagsrequired below), it
457 displays an error page and exits. Otherwise, it displays the
458 login page and exits.
460 Note that C<&checkauth> will return if and only if the user
461 is authorized, so it should be called early on, before any
462 unfinished operations (e.g., if you've opened a file, then
463 C<&checkauth> won't close it for you).
465 C<$query> is the CGI object for the script calling C<&checkauth>.
467 The C<$noauth> argument is optional. If it is set, then no
468 authorization is required for the script.
470 C<&checkauth> fetches user and session information from C<$query> and
471 ensures that the user is authorized to run scripts that require
474 The C<$flagsrequired> argument specifies the required privileges
475 the user must have if the username and password are correct.
476 It should be specified as a reference-to-hash; keys in the hash
477 should be the "flags" for the user, as specified in the Members
478 intranet module. Any key specified must correspond to a "flag"
479 in the userflags table. E.g., { circulate => 1 } would specify
480 that the user must have the "circulate" privilege in order to
481 proceed. To make sure that access control is correct, the
482 C<$flagsrequired> parameter must be specified correctly.
484 If the GranularPermissions system preference is ON, the
485 value of each key in the C<flagsrequired> hash takes on an additional
490 The user must have access to all subfunctions of the module
491 specified by the hash key.
495 The user must have access to at least one subfunction of the module
496 specified by the hash key.
498 =item specific permission, e.g., 'export_catalog'
500 The user must have access to the specific subfunction list, which
501 must correspond to a row in the permissions table.
503 The C<$type> argument specifies whether the template should be
504 retrieved from the opac or intranet directory tree. "opac" is
505 assumed if it is not specified; however, if C<$type> is specified,
506 "intranet" is assumed if it is not "opac".
508 If C<$query> does not have a valid session ID associated with it
509 (i.e., the user has not logged in) or if the session has expired,
510 C<&checkauth> presents the user with a login page (from the point of
511 view of the original script, C<&checkauth> does not return). Once the
512 user has authenticated, C<&checkauth> restarts the original script
513 (this time, C<&checkauth> returns).
515 The login page is provided using a HTML::Template, which is set in the
516 systempreferences table or at the top of this file. The variable C<$type>
517 selects which template to use, either the opac or the intranet
518 authentification template.
520 C<&checkauth> returns a user ID, a cookie, and a session ID. The
521 cookie should be sent back to the browser; it verifies that the user
526 sub _version_check ($$) {
530 # If Version syspref is unavailable, it means Koha is beeing installed,
531 # and so we must redirect to OPAC maintenance page or to the WebInstaller
532 # also, if OpacMaintenance is ON, OPAC should redirect to maintenance
533 if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') {
534 warn "OPAC Install required, redirecting to maintenance";
535 print $query->redirect("/cgi-bin/koha/maintenance.pl");
537 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
538 if ($type ne 'opac') {
539 warn "Install required, redirecting to Installer";
540 print $query->redirect("/cgi-bin/koha/installer/install.pl");
543 warn "OPAC Install required, redirecting to maintenance";
544 print $query->redirect("/cgi-bin/koha/maintenance.pl");
549 # check that database and koha version are the same
550 # there is no DB version, it's a fresh install,
551 # go to web installer
552 # there is a DB version, compare it to the code version
553 my $kohaversion=C4::Context::KOHAVERSION;
554 # remove the 3 last . to have a Perl number
555 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
556 $debug and print STDERR "kohaversion : $kohaversion\n";
557 if ($version < $kohaversion){
558 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
559 if ($type ne 'opac'){
560 warn sprintf($warning, 'Installer');
561 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
563 warn sprintf("OPAC: " . $warning, 'maintenance');
564 print $query->redirect("/cgi-bin/koha/maintenance.pl");
572 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
573 printf L join("\n",@_);
579 $debug and warn "Checking Auth";
580 # $authnotrequired will be set for scripts which will run without authentication
581 my $authnotrequired = shift;
582 my $flagsrequired = shift;
584 $type = 'opac' unless $type;
586 my $dbh = C4::Context->dbh;
587 my $timeout = C4::Context->preference('timeout');
589 if ($timeout =~ /(\d+)[dD]/) {
590 $timeout = $1 * 86400;
592 $timeout = 600 unless $timeout;
594 _version_check($type,$query);
598 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
599 my $logout = $query->param('logout.x');
601 if ( $userid = $ENV{'REMOTE_USER'} ) {
602 # Using Basic Authentication, no cookies required
603 $cookie = $query->cookie(
604 -name => 'CGISESSID',
610 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
611 my $session = get_session($sessionID);
612 C4::Context->_new_userenv($sessionID);
613 my ($ip, $lasttime, $sessiontype);
615 C4::Context::set_userenv(
616 $session->param('number'), $session->param('id'),
617 $session->param('cardnumber'), $session->param('firstname'),
618 $session->param('surname'), $session->param('branch'),
619 $session->param('branchname'), $session->param('flags'),
620 $session->param('emailaddress'), $session->param('branchprinter')
622 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
623 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
624 C4::Context::set_shelves_userenv('tot',$session->param('totshelves'));
625 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
626 $ip = $session->param('ip');
627 $lasttime = $session->param('lasttime');
628 $userid = $session->param('id');
629 $sessiontype = $session->param('sessiontype');
632 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
633 #if a user enters an id ne to the id in the current session, we need to log them in...
634 #first we need to clear the anonymous session...
635 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
638 C4::Context->_unset_userenv($sessionID);
643 # voluntary logout the user
646 C4::Context->_unset_userenv($sessionID);
647 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,(strftime "%c",localtime));
651 elsif ( $lasttime < time() - $timeout ) {
653 $info{'timed_out'} = 1;
655 C4::Context->_unset_userenv($sessionID);
656 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,(strftime "%c",localtime));
660 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
661 # Different ip than originally logged in from
662 $info{'oldip'} = $ip;
663 $info{'newip'} = $ENV{'REMOTE_ADDR'};
664 $info{'different_ip'} = 1;
666 C4::Context->_unset_userenv($sessionID);
667 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,(strftime "%c",localtime), $info{'newip'});
672 $cookie = $query->cookie( CGISESSID => $session->id );
673 $session->param('lasttime',time());
674 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...
675 $flags = haspermission($userid, $flagsrequired);
679 $info{'nopermission'} = 1;
684 unless ($userid || $sessionID) {
685 #we initiate a session prior to checking for a username to allow for anonymous sessions...
686 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
687 my $sessionID = $session->id;
688 C4::Context->_new_userenv($sessionID);
689 $cookie = $query->cookie(CGISESSID => $sessionID);
690 if ( $userid = $query->param('userid') ) {
691 my $password = $query->param('password');
692 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
694 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},(strftime "%c",localtime));
695 if ( $flags = haspermission($userid, $flagsrequired) ) {
699 $info{'nopermission'} = 1;
700 C4::Context->_unset_userenv($sessionID);
703 my ($borrowernumber, $firstname, $surname, $userflags,
704 $branchcode, $branchname, $branchprinter, $emailaddress);
706 if ( $return == 1 ) {
708 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
709 branches.branchname as branchname,
710 branches.branchprinter as branchprinter,
713 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
715 my $sth = $dbh->prepare("$select where userid=?");
716 $sth->execute($userid);
717 unless ($sth->rows) {
718 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
719 $sth = $dbh->prepare("$select where cardnumber=?");
720 $sth->execute($cardnumber);
721 unless ($sth->rows) {
722 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
723 $sth->execute($userid);
724 unless ($sth->rows) {
725 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
730 ($borrowernumber, $firstname, $surname, $userflags,
731 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
732 $debug and print STDERR "AUTH_3 results: " .
733 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
735 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
738 # launch a sequence to check if we have a ip for the branch, i
739 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
741 my $ip = $ENV{'REMOTE_ADDR'};
742 # if they specify at login, use that
743 if ($query->param('branch')) {
744 $branchcode = $query->param('branch');
745 $branchname = GetBranchName($branchcode);
747 my $branches = GetBranches();
748 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
749 # we have to check they are coming from the right ip range
750 my $domain = $branches->{$branchcode}->{'branchip'};
751 if ($ip !~ /^$domain/){
753 $info{'wrongip'} = 1;
758 foreach my $br ( keys %$branches ) {
759 # now we work with the treatment of ip
760 my $domain = $branches->{$br}->{'branchip'};
761 if ( $domain && $ip =~ /^$domain/ ) {
762 $branchcode = $branches->{$br}->{'branchcode'};
764 # new op dev : add the branchprinter and branchname in the cookie
765 $branchprinter = $branches->{$br}->{'branchprinter'};
766 $branchname = $branches->{$br}->{'branchname'};
769 $session->param('number',$borrowernumber);
770 $session->param('id',$userid);
771 $session->param('cardnumber',$cardnumber);
772 $session->param('firstname',$firstname);
773 $session->param('surname',$surname);
774 $session->param('branch',$branchcode);
775 $session->param('branchname',$branchname);
776 $session->param('flags',$userflags);
777 $session->param('emailaddress',$emailaddress);
778 $session->param('ip',$session->remote_addr());
779 $session->param('lasttime',time());
780 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
782 elsif ( $return == 2 ) {
783 #We suppose the user is the superlibrarian
785 $session->param('number',0);
786 $session->param('id',C4::Context->config('user'));
787 $session->param('cardnumber',C4::Context->config('user'));
788 $session->param('firstname',C4::Context->config('user'));
789 $session->param('surname',C4::Context->config('user'));
790 $session->param('branch','NO_LIBRARY_SET');
791 $session->param('branchname','NO_LIBRARY_SET');
792 $session->param('flags',1);
793 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
794 $session->param('ip',$session->remote_addr());
795 $session->param('lasttime',time());
797 C4::Context::set_userenv(
798 $session->param('number'), $session->param('id'),
799 $session->param('cardnumber'), $session->param('firstname'),
800 $session->param('surname'), $session->param('branch'),
801 $session->param('branchname'), $session->param('flags'),
802 $session->param('emailaddress'), $session->param('branchprinter')
805 # Grab borrower's shelves and public shelves and add them to the session
806 # $row_count determines how many records are returned from the db query
807 # and the number of lists to be displayed of each type in the 'Lists' button drop down
808 my $row_count = 10; # FIXME:This probably should be a syspref
809 my ($total, $totshelves, $barshelves, $pubshelves);
810 ($barshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(1, $row_count, $borrowernumber);
811 $total->{'bartotal'} = $totshelves;
812 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
813 $total->{'pubtotal'} = $totshelves;
814 $session->param('barshelves', $barshelves->[0]);
815 $session->param('pubshelves', $pubshelves->[0]);
816 $session->param('totshelves', $total);
818 C4::Context::set_shelves_userenv('bar',$barshelves->[0]);
819 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
820 C4::Context::set_shelves_userenv('tot',$total);
824 $info{'invalid_username_or_password'} = 1;
825 C4::Context->_unset_userenv($sessionID);
828 } # END if ( $userid = $query->param('userid') )
829 elsif ($type eq "opac") {
830 # if we are here this is an anonymous session; add public lists to it and a few other items...
831 # anonymous sessions are created only for the OPAC
832 $debug and warn "Initiating an anonymous session...";
834 # Grab the public shelves and add to the session...
835 my $row_count = 20; # FIXME:This probably should be a syspref
836 my ($total, $totshelves, $pubshelves);
837 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
838 $total->{'pubtotal'} = $totshelves;
839 $session->param('pubshelves', $pubshelves->[0]);
840 $session->param('totshelves', $total);
841 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
842 C4::Context::set_shelves_userenv('tot',$total);
844 # setting a couple of other session vars...
845 $session->param('ip',$session->remote_addr());
846 $session->param('lasttime',time());
847 $session->param('sessiontype','anon');
849 } # END unless ($userid)
850 my $insecure = C4::Context->boolean_preference('insecure');
852 # finished authentification, now respond
853 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
857 $cookie = $query->cookie( CGISESSID => '' );
859 return ( $userid, $cookie, $sessionID, $flags );
864 # AUTH rejected, show the login/password template, after checking the DB.
868 # get the inputs from the incoming query
870 foreach my $name ( param $query) {
871 (next) if ( $name eq 'userid' || $name eq 'password' );
872 my $value = $query->param($name);
873 push @inputs, { name => $name, value => $value };
875 # get the branchloop, which we need for authentication
876 my $branches = GetBranches();
878 for my $branch_hash (sort keys %$branches) {
879 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
882 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
883 my $template = gettemplate( $template_name, $type, $query );
884 $template->param(branchloop => \@branch_loop,);
888 suggestion => C4::Context->preference("suggestion"),
889 virtualshelves => C4::Context->preference("virtualshelves"),
890 LibraryName => C4::Context->preference("LibraryName"),
891 opacuserlogin => C4::Context->preference("opacuserlogin"),
892 OpacNav => C4::Context->preference("OpacNav"),
893 opaccredits => C4::Context->preference("opaccredits"),
894 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
895 opacsmallimage => C4::Context->preference("opacsmallimage"),
896 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
897 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
898 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
899 opacuserjs => C4::Context->preference("opacuserjs"),
900 opacbookbag => "" . C4::Context->preference("opacbookbag"),
901 OpacCloud => C4::Context->preference("OpacCloud"),
902 OpacTopissue => C4::Context->preference("OpacTopissue"),
903 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
904 OpacBrowser => C4::Context->preference("OpacBrowser"),
905 opacheader => C4::Context->preference("opacheader"),
906 TagsEnabled => C4::Context->preference("TagsEnabled"),
907 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
908 intranetcolorstylesheet =>
909 C4::Context->preference("intranetcolorstylesheet"),
910 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
911 IntranetNav => C4::Context->preference("IntranetNav"),
912 intranetuserjs => C4::Context->preference("intranetuserjs"),
913 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
914 IndependantBranches=> C4::Context->preference("IndependantBranches"),
915 AutoLocation => C4::Context->preference("AutoLocation"),
916 wrongip => $info{'wrongip'}
919 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
921 my $self_url = $query->url( -absolute => 1 );
924 LibraryName => C4::Context->preference("LibraryName"),
926 $template->param( \%info );
927 # $cookie = $query->cookie(CGISESSID => $session->id
929 print $query->header(
930 -type => 'text/html',
940 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
942 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
943 cookie, determine if the user has the privileges specified by C<$userflags>.
945 C<check_api_auth> is is meant for authenticating users of web services, and
946 consequently will always return and will not attempt to redirect the user
949 If a valid session cookie is already present, check_api_auth will return a status
950 of "ok", the cookie, and the Koha session ID.
952 If no session cookie is present, check_api_auth will check the 'userid' and 'password
953 parameters and create a session cookie and Koha session if the supplied credentials
956 Possible return values in C<$status> are:
960 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
962 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
964 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
966 =item "expired -- session cookie has expired; API user should resubmit userid and password
974 my $flagsrequired = shift;
976 my $dbh = C4::Context->dbh;
977 my $timeout = C4::Context->preference('timeout');
978 $timeout = 600 unless $timeout;
980 unless (C4::Context->preference('Version')) {
981 # database has not been installed yet
982 return ("maintenance", undef, undef);
984 my $kohaversion=C4::Context::KOHAVERSION;
985 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
986 if (C4::Context->preference('Version') < $kohaversion) {
987 # database in need of version update; assume that
988 # no API should be called while databsae is in
990 return ("maintenance", undef, undef);
993 # FIXME -- most of what follows is a copy-and-paste
994 # of code from checkauth. There is an obvious need
995 # for refactoring to separate the various parts of
996 # the authentication code, but as of 2007-11-19 this
997 # is deferred so as to not introduce bugs into the
998 # regular authentication code for Koha 3.0.
1000 # see if we have a valid session cookie already
1001 # however, if a userid parameter is present (i.e., from
1002 # a form submission, assume that any current cookie
1004 my $sessionID = undef;
1005 unless ($query->param('userid')) {
1006 $sessionID = $query->cookie("CGISESSID");
1009 my $session = get_session($sessionID);
1010 C4::Context->_new_userenv($sessionID);
1012 C4::Context::set_userenv(
1013 $session->param('number'), $session->param('id'),
1014 $session->param('cardnumber'), $session->param('firstname'),
1015 $session->param('surname'), $session->param('branch'),
1016 $session->param('branchname'), $session->param('flags'),
1017 $session->param('emailaddress'), $session->param('branchprinter')
1020 my $ip = $session->param('ip');
1021 my $lasttime = $session->param('lasttime');
1022 my $userid = $session->param('id');
1023 if ( $lasttime < time() - $timeout ) {
1026 C4::Context->_unset_userenv($sessionID);
1029 return ("expired", undef, undef);
1030 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1031 # IP address changed
1033 C4::Context->_unset_userenv($sessionID);
1036 return ("expired", undef, undef);
1038 my $cookie = $query->cookie( CGISESSID => $session->id );
1039 $session->param('lasttime',time());
1040 my $flags = haspermission($userid, $flagsrequired);
1042 return ("ok", $cookie, $sessionID);
1045 C4::Context->_unset_userenv($sessionID);
1048 return ("failed", undef, undef);
1052 return ("expired", undef, undef);
1056 my $userid = $query->param('userid');
1057 my $password = $query->param('password');
1058 unless ($userid and $password) {
1059 # caller did something wrong, fail the authenticateion
1060 return ("failed", undef, undef);
1062 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
1063 if ($return and haspermission($userid, $flagsrequired)) {
1064 my $session = get_session("");
1065 return ("failed", undef, undef) unless $session;
1067 my $sessionID = $session->id;
1068 C4::Context->_new_userenv($sessionID);
1069 my $cookie = $query->cookie(CGISESSID => $sessionID);
1070 if ( $return == 1 ) {
1072 $borrowernumber, $firstname, $surname,
1073 $userflags, $branchcode, $branchname,
1074 $branchprinter, $emailaddress
1078 "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=?"
1080 $sth->execute($userid);
1082 $borrowernumber, $firstname, $surname,
1083 $userflags, $branchcode, $branchname,
1084 $branchprinter, $emailaddress
1085 ) = $sth->fetchrow if ( $sth->rows );
1087 unless ($sth->rows ) {
1088 my $sth = $dbh->prepare(
1089 "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=?"
1091 $sth->execute($cardnumber);
1093 $borrowernumber, $firstname, $surname,
1094 $userflags, $branchcode, $branchname,
1095 $branchprinter, $emailaddress
1096 ) = $sth->fetchrow if ( $sth->rows );
1098 unless ( $sth->rows ) {
1099 $sth->execute($userid);
1101 $borrowernumber, $firstname, $surname, $userflags,
1102 $branchcode, $branchname, $branchprinter, $emailaddress
1103 ) = $sth->fetchrow if ( $sth->rows );
1107 my $ip = $ENV{'REMOTE_ADDR'};
1108 # if they specify at login, use that
1109 if ($query->param('branch')) {
1110 $branchcode = $query->param('branch');
1111 $branchname = GetBranchName($branchcode);
1113 my $branches = GetBranches();
1115 foreach my $br ( keys %$branches ) {
1116 # now we work with the treatment of ip
1117 my $domain = $branches->{$br}->{'branchip'};
1118 if ( $domain && $ip =~ /^$domain/ ) {
1119 $branchcode = $branches->{$br}->{'branchcode'};
1121 # new op dev : add the branchprinter and branchname in the cookie
1122 $branchprinter = $branches->{$br}->{'branchprinter'};
1123 $branchname = $branches->{$br}->{'branchname'};
1126 $session->param('number',$borrowernumber);
1127 $session->param('id',$userid);
1128 $session->param('cardnumber',$cardnumber);
1129 $session->param('firstname',$firstname);
1130 $session->param('surname',$surname);
1131 $session->param('branch',$branchcode);
1132 $session->param('branchname',$branchname);
1133 $session->param('flags',$userflags);
1134 $session->param('emailaddress',$emailaddress);
1135 $session->param('ip',$session->remote_addr());
1136 $session->param('lasttime',time());
1137 } elsif ( $return == 2 ) {
1138 #We suppose the user is the superlibrarian
1139 $session->param('number',0);
1140 $session->param('id',C4::Context->config('user'));
1141 $session->param('cardnumber',C4::Context->config('user'));
1142 $session->param('firstname',C4::Context->config('user'));
1143 $session->param('surname',C4::Context->config('user'));
1144 $session->param('branch','NO_LIBRARY_SET');
1145 $session->param('branchname','NO_LIBRARY_SET');
1146 $session->param('flags',1);
1147 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1148 $session->param('ip',$session->remote_addr());
1149 $session->param('lasttime',time());
1151 C4::Context::set_userenv(
1152 $session->param('number'), $session->param('id'),
1153 $session->param('cardnumber'), $session->param('firstname'),
1154 $session->param('surname'), $session->param('branch'),
1155 $session->param('branchname'), $session->param('flags'),
1156 $session->param('emailaddress'), $session->param('branchprinter')
1158 return ("ok", $cookie, $sessionID);
1160 return ("failed", undef, undef);
1165 =item check_cookie_auth
1167 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1169 Given a CGISESSID cookie set during a previous login to Koha, determine
1170 if the user has the privileges specified by C<$userflags>.
1172 C<check_cookie_auth> is meant for authenticating special services
1173 such as tools/upload-file.pl that are invoked by other pages that
1174 have been authenticated in the usual way.
1176 Possible return values in C<$status> are:
1180 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1182 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1184 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1186 =item "expired -- session cookie has expired; API user should resubmit userid and password
1192 sub check_cookie_auth {
1194 my $flagsrequired = shift;
1196 my $dbh = C4::Context->dbh;
1197 my $timeout = C4::Context->preference('timeout');
1198 $timeout = 600 unless $timeout;
1200 unless (C4::Context->preference('Version')) {
1201 # database has not been installed yet
1202 return ("maintenance", undef);
1204 my $kohaversion=C4::Context::KOHAVERSION;
1205 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1206 if (C4::Context->preference('Version') < $kohaversion) {
1207 # database in need of version update; assume that
1208 # no API should be called while databsae is in
1210 return ("maintenance", undef);
1213 # FIXME -- most of what follows is a copy-and-paste
1214 # of code from checkauth. There is an obvious need
1215 # for refactoring to separate the various parts of
1216 # the authentication code, but as of 2007-11-23 this
1217 # is deferred so as to not introduce bugs into the
1218 # regular authentication code for Koha 3.0.
1220 # see if we have a valid session cookie already
1221 # however, if a userid parameter is present (i.e., from
1222 # a form submission, assume that any current cookie
1224 unless (defined $cookie and $cookie) {
1225 return ("failed", undef);
1227 my $sessionID = $cookie;
1228 my $session = get_session($sessionID);
1229 C4::Context->_new_userenv($sessionID);
1231 C4::Context::set_userenv(
1232 $session->param('number'), $session->param('id'),
1233 $session->param('cardnumber'), $session->param('firstname'),
1234 $session->param('surname'), $session->param('branch'),
1235 $session->param('branchname'), $session->param('flags'),
1236 $session->param('emailaddress'), $session->param('branchprinter')
1239 my $ip = $session->param('ip');
1240 my $lasttime = $session->param('lasttime');
1241 my $userid = $session->param('id');
1242 if ( $lasttime < time() - $timeout ) {
1245 C4::Context->_unset_userenv($sessionID);
1248 return ("expired", undef);
1249 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1250 # IP address changed
1252 C4::Context->_unset_userenv($sessionID);
1255 return ("expired", undef);
1257 $session->param('lasttime',time());
1258 my $flags = haspermission($userid, $flagsrequired);
1260 return ("ok", $sessionID);
1263 C4::Context->_unset_userenv($sessionID);
1266 return ("failed", undef);
1270 return ("expired", undef);
1277 my $session = get_session($sessionID);
1279 Given a session ID, retrieve the CGI::Session object used to store
1280 the session's state. The session object can be used to store
1281 data that needs to be accessed by different scripts during a
1284 If the C<$sessionID> parameter is an empty string, a new session
1290 my $sessionID = shift;
1291 my $storage_method = C4::Context->preference('SessionStorage');
1292 my $dbh = C4::Context->dbh;
1294 if ($storage_method eq 'mysql'){
1295 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1297 elsif ($storage_method eq 'Pg') {
1298 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1301 # catch all defaults to tmp should work on all systems
1302 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1309 my ( $dbh, $userid, $password ) = @_;
1311 $debug and print STDERR "## checkpw - checking LDAP\n";
1312 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1313 ($retval) and return ($retval,$retcard);
1319 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1321 $sth->execute($userid);
1323 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1324 $surname, $branchcode, $flags )
1326 if ( md5_base64($password) eq $md5password ) {
1328 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1329 $firstname, $surname, $branchcode, $flags );
1330 return 1, $cardnumber;
1335 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1337 $sth->execute($userid);
1339 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1340 $surname, $branchcode, $flags )
1342 if ( md5_base64($password) eq $md5password ) {
1344 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1345 $firstname, $surname, $branchcode, $flags );
1349 if ( $userid && $userid eq C4::Context->config('user')
1350 && "$password" eq C4::Context->config('pass') )
1353 # Koha superuser account
1354 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1357 if ( $userid && $userid eq 'demo'
1358 && "$password" eq 'demo'
1359 && C4::Context->config('demo') )
1362 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1363 # some features won't be effective : modify systempref, modify MARC structure,
1371 my $authflags = getuserflags($flags, $userid, [$dbh]);
1373 Translates integer flags into permissions strings hash.
1375 C<$flags> is the integer userflags value ( borrowers.userflags )
1376 C<$userid> is the members.userid, used for building subpermissions
1377 C<$authflags> is a hashref of permissions
1384 my $dbh = @_ ? shift : C4::Context->dbh;
1386 $flags = 0 unless $flags;
1387 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1390 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1391 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1392 $userflags->{$flag} = 1;
1395 $userflags->{$flag} = 0;
1399 # get subpermissions and merge with top-level permissions
1400 my $user_subperms = get_user_subpermissions($userid);
1401 foreach my $module (keys %$user_subperms) {
1402 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1403 $userflags->{$module} = $user_subperms->{$module};
1409 =item get_user_subpermissions
1413 my $user_perm_hashref = get_user_subpermissions($userid);
1417 Given the userid (note, not the borrowernumber) of a staff user,
1418 return a hashref of hashrefs of the specific subpermissions
1419 accorded to the user. An example return is
1423 export_catalog => 1,
1424 import_patrons => 1,
1428 The top-level hash-key is a module or function code from
1429 userflags.flag, while the second-level key is a code
1432 The results of this function do not give a complete picture
1433 of the functions that a staff user can access; it is also
1434 necessary to check borrowers.flags.
1438 sub get_user_subpermissions {
1441 my $dbh = C4::Context->dbh;
1442 my $sth = $dbh->prepare("SELECT flag, user_permissions.code
1443 FROM user_permissions
1444 JOIN permissions USING (module_bit, code)
1445 JOIN userflags ON (module_bit = bit)
1446 JOIN borrowers USING (borrowernumber)
1448 $sth->execute($userid);
1450 my $user_perms = {};
1451 while (my $perm = $sth->fetchrow_hashref) {
1452 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1457 =item get_all_subpermissions
1461 my $perm_hashref = get_all_subpermissions();
1465 Returns a hashref of hashrefs defining all specific
1466 permissions currently defined. The return value
1467 has the same structure as that of C<get_user_subpermissions>,
1468 except that the innermost hash value is the description
1469 of the subpermission.
1473 sub get_all_subpermissions {
1474 my $dbh = C4::Context->dbh;
1475 my $sth = $dbh->prepare("SELECT flag, code, description
1477 JOIN userflags ON (module_bit = bit)");
1481 while (my $perm = $sth->fetchrow_hashref) {
1482 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1489 $flags = ($userid, $flagsrequired);
1491 C<$userid> the userid of the member
1492 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1494 Returns member's flags or 0 if a permission is not met.
1499 my ($userid, $flagsrequired) = @_;
1500 my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1501 $sth->execute($userid);
1502 my $flags = getuserflags( $sth->fetchrow(), $userid );
1503 if ( $userid eq C4::Context->config('user') ) {
1504 # Super User Account from /etc/koha.conf
1505 $flags->{'superlibrarian'} = 1;
1507 elsif ( $userid eq 'demo' && C4::Context->config('demo') ) {
1508 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1509 $flags->{'superlibrarian'} = 1;
1511 return $flags if $flags->{superlibrarian};
1512 foreach my $module ( keys %$flagsrequired ) {
1513 if (C4::Context->preference('GranularPermissions')) {
1514 my $subperm = $flagsrequired->{$module};
1515 if ($subperm eq '*') {
1516 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1518 return 0 unless ( $flags->{$module} == 1 or
1519 ( ref($flags->{$module}) and
1520 exists $flags->{$module}->{$subperm} and
1521 $flags->{$module}->{$subperm} == 1
1526 return 0 unless ( $flags->{$module} );
1530 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1534 sub getborrowernumber {
1536 my $userenv = C4::Context->userenv;
1537 if ( defined( $userenv ) && ref( $userenv ) eq 'HASH' && $userenv->{number} ) {
1538 return $userenv->{number};
1540 my $dbh = C4::Context->dbh;
1541 for my $field ( 'userid', 'cardnumber' ) {
1543 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1544 $sth->execute($userid);
1546 my ($bnumber) = $sth->fetchrow;
1553 END { } # module clean-up code here (global destructor)