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
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 $cas $caslogout);
39 $VERSION = 3.02; # set version for version checking
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;
46 $cas = C4::Context->preference('casAuthentication');
47 $caslogout = C4::Context->preference('casLogout');
49 require C4::Auth_with_ldap; # no import
50 import C4::Auth_with_ldap qw(checkpw_ldap);
53 require C4::Auth_with_cas; # no import
54 import C4::Auth_with_cas qw(checkpw_cas login_cas logout_cas login_cas_url);
61 C4::Auth - Authenticates Koha users
71 my ($template, $borrowernumber, $cookie)
72 = get_template_and_user(
74 template_name => "opac-main.tmpl",
78 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
82 output_html_with_http_headers $query, $cookie, $template->output;
86 The main function of this module is to provide
87 authentification. However the get_template_and_user function has
88 been provided so that a users login information is passed along
89 automatically. This gets loaded into the template.
95 =item get_template_and_user
97 my ($template, $borrowernumber, $cookie)
98 = get_template_and_user(
100 template_name => "opac-main.tmpl",
103 authnotrequired => 1,
104 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
108 This call passes the C<query>, C<flagsrequired> and C<authnotrequired>
109 to C<&checkauth> (in this module) to perform authentification.
110 See C<&checkauth> for an explanation of these parameters.
112 The C<template_name> is then used to find the correct template for
113 the page. The authenticated users details are loaded onto the
114 template in the HTML::Template LOOP variable C<USER_INFO>. Also the
115 C<sessionID> is passed to the template. This can be used in templates
116 if cookies are disabled. It needs to be put as and input to every
119 More information on the C<gettemplate> sub can be found in the
124 my $SEARCH_HISTORY_INSERT_SQL =<<EOQ;
125 INSERT INTO search_history(userid, sessionid, query_desc, query_cgi, total, time )
126 VALUES ( ?, ?, ?, ?, ?, FROM_UNIXTIME(?))
128 sub get_template_and_user {
131 gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'} );
132 my ( $user, $cookie, $sessionID, $flags ) = checkauth(
134 $in->{'authnotrequired'},
135 $in->{'flagsrequired'},
137 ) unless ($in->{'template_name'}=~/maintenance/);
140 my $insecure = C4::Context->preference('insecure');
141 if ($user or $insecure) {
143 # load the template variables for stylesheets and JavaScript
144 $template->param( css_libs => $in->{'css_libs'} );
145 $template->param( css_module => $in->{'css_module'} );
146 $template->param( css_page => $in->{'css_page'} );
147 $template->param( css_widgets => $in->{'css_widgets'} );
149 $template->param( js_libs => $in->{'js_libs'} );
150 $template->param( js_module => $in->{'js_module'} );
151 $template->param( js_page => $in->{'js_page'} );
152 $template->param( js_widgets => $in->{'js_widgets'} );
155 $template->param( loggedinusername => $user );
156 $template->param( sessionID => $sessionID );
158 my ($total, $pubshelves, $barshelves) = C4::Context->get_shelves_userenv();
159 if (defined($pubshelves)) {
160 $template->param( pubshelves => scalar (@$pubshelves),
161 pubshelvesloop => $pubshelves,
163 $template->param( pubtotal => $total->{'pubtotal'}, ) if ($total->{'pubtotal'} > scalar (@$pubshelves));
165 if (defined($barshelves)) {
166 $template->param( barshelves => scalar (@$barshelves),
167 barshelvesloop => $barshelves,
169 $template->param( bartotal => $total->{'bartotal'}, ) if ($total->{'bartotal'} > scalar (@$barshelves));
172 $borrowernumber = getborrowernumber($user) if defined($user);
174 my ( $borr ) = GetMemberDetails( $borrowernumber );
177 $template->param( "USER_INFO" => \@bordat );
179 my $all_perms = get_all_subpermissions();
181 my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow
182 editcatalogue updatecharges management tools editauthorities serials reports acquisition);
183 # We are going to use the $flags returned by checkauth
184 # to create the template's parameters that will indicate
185 # which menus the user can access.
186 if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) {
187 $template->param( CAN_user_circulate => 1 );
188 $template->param( CAN_user_catalogue => 1 );
189 $template->param( CAN_user_parameters => 1 );
190 $template->param( CAN_user_borrowers => 1 );
191 $template->param( CAN_user_permissions => 1 );
192 $template->param( CAN_user_reserveforothers => 1 );
193 $template->param( CAN_user_borrow => 1 );
194 $template->param( CAN_user_editcatalogue => 1 );
195 $template->param( CAN_user_updatecharges => 1 );
196 $template->param( CAN_user_acquisition => 1 );
197 $template->param( CAN_user_management => 1 );
198 $template->param( CAN_user_tools => 1 );
199 $template->param( CAN_user_editauthorities => 1 );
200 $template->param( CAN_user_serials => 1 );
201 $template->param( CAN_user_reports => 1 );
202 $template->param( CAN_user_staffaccess => 1 );
203 foreach my $module (keys %$all_perms) {
204 foreach my $subperm (keys %{ $all_perms->{$module} }) {
205 $template->param( "CAN_user_${module}_${subperm}" => 1 );
210 if (C4::Context->preference('GranularPermissions')) {
212 foreach my $module (keys %$all_perms) {
213 if ( $flags->{$module} == 1) {
214 foreach my $subperm (keys %{ $all_perms->{$module} }) {
215 $template->param( "CAN_user_${module}_${subperm}" => 1 );
217 } elsif ( ref($flags->{$module}) ) {
218 foreach my $subperm (keys %{ $flags->{$module} } ) {
219 $template->param( "CAN_user_${module}_${subperm}" => 1 );
225 foreach my $module (keys %$all_perms) {
226 foreach my $subperm (keys %{ $all_perms->{$module} }) {
227 $template->param( "CAN_user_${module}_${subperm}" => 1 );
233 foreach my $module (keys %$flags) {
234 if ( $flags->{$module} == 1 or ref($flags->{$module}) ) {
235 $template->param( "CAN_user_$module" => 1 );
236 if ($module eq "parameters") {
237 $template->param( CAN_user_management => 1 );
242 # Logged-in opac search history
243 # If the requested template is an opac one and opac search history is enabled
244 if ($in->{'type'} == "opac" && C4::Context->preference('EnableOpacSearchHistory')) {
245 my $dbh = C4::Context->dbh;
246 my $query = "SELECT COUNT(*) FROM search_history WHERE userid=?";
247 my $sth = $dbh->prepare($query);
248 $sth->execute($borrowernumber);
250 # If at least one search has already been performed
251 if ($sth->fetchrow_array > 0) {
252 # We show the link in opac
253 $template->param(ShowOpacRecentSearchLink => 1);
256 # And if there's a cookie with searches performed when the user was not logged in,
257 # we add them to the logged-in search history
258 my $searchcookie = $in->{'query'}->cookie('KohaOpacRecentSearches');
260 $searchcookie = uri_unescape($searchcookie);
261 my @recentSearches = @{thaw($searchcookie) || []};
262 if (@recentSearches) {
263 my $sth = $dbh->prepare($SEARCH_HISTORY_INSERT_SQL);
264 $sth->execute( $borrowernumber,
265 $in->{'query'}->cookie("CGISESSID"),
270 ) foreach @recentSearches;
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')) {
310 my $searchcookie = $in->{'query'}->cookie('KohaOpacRecentSearches');
312 $searchcookie = uri_unescape($searchcookie);
313 my @recentSearches = @{thaw($searchcookie) || []};
314 # We show the link in opac
315 if (@recentSearches) {
316 $template->param(ShowOpacRecentSearchLink => 1);
321 if(C4::Context->preference('dateformat')){
322 if(C4::Context->preference('dateformat') eq "metric"){
323 $template->param(dateformat_metric => 1);
324 } elsif(C4::Context->preference('dateformat') eq "us"){
325 $template->param(dateformat_us => 1);
327 $template->param(dateformat_iso => 1);
330 $template->param(dateformat_iso => 1);
333 # these template parameters are set the same regardless of $in->{'type'}
335 "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
336 EnhancedMessagingPreferences => C4::Context->preference('EnhancedMessagingPreferences'),
337 GoogleJackets => C4::Context->preference("GoogleJackets"),
338 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
339 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
340 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
341 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
342 TagsEnabled => C4::Context->preference("TagsEnabled"),
343 hide_marc => C4::Context->preference("hide_marc"),
344 'item-level_itypes' => C4::Context->preference('item-level_itypes'),
345 patronimages => C4::Context->preference("patronimages"),
346 singleBranchMode => C4::Context->preference("singleBranchMode"),
347 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
348 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
349 BranchesLoop => GetBranchesLoop(),
350 using_https => $in->{'query'}->https() ? 1 : 0,
353 if ( $in->{'type'} eq "intranet" ) {
355 AmazonContent => C4::Context->preference("AmazonContent"),
356 AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
357 AutoLocation => C4::Context->preference("AutoLocation"),
358 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
359 CircAutocompl => C4::Context->preference("CircAutocompl"),
360 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
361 IndependantBranches => C4::Context->preference("IndependantBranches"),
362 IntranetNav => C4::Context->preference("IntranetNav"),
363 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
364 LibraryName => C4::Context->preference("LibraryName"),
365 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
366 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
367 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
368 canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
369 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
370 intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
371 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
372 intranetuserjs => C4::Context->preference("intranetuserjs"),
373 intranetbookbag => C4::Context->preference("intranetbookbag"),
374 noItemTypeImages => C4::Context->preference("noItemTypeImages"),
375 suggestion => C4::Context->preference("suggestion"),
376 virtualshelves => C4::Context->preference("virtualshelves"),
377 StaffSerialIssueDisplayCount => C4::Context->preference("StaffSerialIssueDisplayCount"),
378 NoZebra => C4::Context->preference('NoZebra'),
382 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
383 #TODO : replace LibraryName syspref with 'system name', and remove this html processing
384 my $LibraryNameTitle = C4::Context->preference("LibraryName");
385 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
386 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
387 # variables passed from CGI: opac_css_override and opac_search_limits.
388 my $opac_search_limit = $ENV{'OPAC_SEARCH_LIMIT'};
389 my $opac_limit_override = $ENV{'OPAC_LIMIT_OVERRIDE'};
390 my $mylibraryfirst = C4::Context->preference("SearchMyLibraryFirst");
392 if($opac_limit_override && ($opac_search_limit =~ /branch:(\w+)/) ){
393 $opac_name = C4::Branch::GetBranchName($1) # opac_search_limit is a branch, so we use it.
394 } elsif($mylibraryfirst){
395 $opac_name = C4::Branch::GetBranchName($mylibraryfirst);
398 AmazonContent => "" . C4::Context->preference("AmazonContent"),
399 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
400 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
401 LibraryName => "" . C4::Context->preference("LibraryName"),
402 LibraryNameTitle => "" . $LibraryNameTitle,
403 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
404 OPACAmazonEnabled => C4::Context->preference("OPACAmazonEnabled"),
405 OPACAmazonSimilarItems => C4::Context->preference("OPACAmazonSimilarItems"),
406 OPACAmazonCoverImages => C4::Context->preference("OPACAmazonCoverImages"),
407 OPACAmazonReviews => C4::Context->preference("OPACAmazonReviews"),
408 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
409 OpacHighlightedWords => C4::Context->preference("OpacHighlightedWords"),
410 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
411 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
412 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
413 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
414 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
415 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
416 OPACBaseURL => ($in->{'query'}->https() ? "https://" : "http://") . $ENV{'SERVER_NAME'} .
417 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
418 opac_name => $opac_name,
419 opac_css_override => $ENV{'OPAC_CSS_OVERRIDE'},
420 opac_search_limit => $opac_search_limit,
421 opac_limit_override => $opac_limit_override,
422 OpacBrowser => C4::Context->preference("OpacBrowser"),
423 OpacCloud => C4::Context->preference("OpacCloud"),
424 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
425 OpacNav => "" . C4::Context->preference("OpacNav"),
426 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
427 OPACPatronDetails => C4::Context->preference("OPACPatronDetails"),
428 OPACFinesTab => C4::Context->preference("OPACFinesTab"),
429 OpacTopissue => C4::Context->preference("OpacTopissue"),
430 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
431 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
432 'Version' => C4::Context->preference('Version'),
433 hidelostitems => C4::Context->preference("hidelostitems"),
434 mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '',
435 opaclayoutstylesheet => "" . C4::Context->preference("opaclayoutstylesheet"),
436 opaccolorstylesheet => "" . C4::Context->preference("opaccolorstylesheet"),
437 opacstylesheet => "" . C4::Context->preference("opacstylesheet"),
438 opacbookbag => "" . C4::Context->preference("opacbookbag"),
439 opaccredits => "" . C4::Context->preference("opaccredits"),
440 opacheader => "" . C4::Context->preference("opacheader"),
441 opaclanguagesdisplay => "" . C4::Context->preference("opaclanguagesdisplay"),
442 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
443 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
444 opacuserjs => C4::Context->preference("opacuserjs"),
445 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
446 reviewson => C4::Context->preference("reviewson"),
447 suggestion => "" . C4::Context->preference("suggestion"),
448 virtualshelves => "" . C4::Context->preference("virtualshelves"),
449 OPACSerialIssueDisplayCount => C4::Context->preference("OPACSerialIssueDisplayCount"),
450 OpacAddMastheadLibraryPulldown => C4::Context->preference("OpacAddMastheadLibraryPulldown"),
451 OPACXSLTDetailsDisplay => C4::Context->preference("OPACXSLTDetailsDisplay"),
452 OPACXSLTResultsDisplay => C4::Context->preference("OPACXSLTResultsDisplay")
455 $template->param(listloop=>[{shelfname=>"Freelist", shelfnumber=>110}]);
456 return ( $template, $borrowernumber, $cookie, $flags);
461 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
463 Verifies that the user is authorized to run this script. If
464 the user is authorized, a (userid, cookie, session-id, flags)
465 quadruple is returned. If the user is not authorized but does
466 not have the required privilege (see $flagsrequired below), it
467 displays an error page and exits. Otherwise, it displays the
468 login page and exits.
470 Note that C<&checkauth> will return if and only if the user
471 is authorized, so it should be called early on, before any
472 unfinished operations (e.g., if you've opened a file, then
473 C<&checkauth> won't close it for you).
475 C<$query> is the CGI object for the script calling C<&checkauth>.
477 The C<$noauth> argument is optional. If it is set, then no
478 authorization is required for the script.
480 C<&checkauth> fetches user and session information from C<$query> and
481 ensures that the user is authorized to run scripts that require
484 The C<$flagsrequired> argument specifies the required privileges
485 the user must have if the username and password are correct.
486 It should be specified as a reference-to-hash; keys in the hash
487 should be the "flags" for the user, as specified in the Members
488 intranet module. Any key specified must correspond to a "flag"
489 in the userflags table. E.g., { circulate => 1 } would specify
490 that the user must have the "circulate" privilege in order to
491 proceed. To make sure that access control is correct, the
492 C<$flagsrequired> parameter must be specified correctly.
494 If the GranularPermissions system preference is ON, the
495 value of each key in the C<flagsrequired> hash takes on an additional
500 The user must have access to all subfunctions of the module
501 specified by the hash key.
505 The user must have access to at least one subfunction of the module
506 specified by the hash key.
508 =item specific permission, e.g., 'export_catalog'
510 The user must have access to the specific subfunction list, which
511 must correspond to a row in the permissions table.
513 The C<$type> argument specifies whether the template should be
514 retrieved from the opac or intranet directory tree. "opac" is
515 assumed if it is not specified; however, if C<$type> is specified,
516 "intranet" is assumed if it is not "opac".
518 If C<$query> does not have a valid session ID associated with it
519 (i.e., the user has not logged in) or if the session has expired,
520 C<&checkauth> presents the user with a login page (from the point of
521 view of the original script, C<&checkauth> does not return). Once the
522 user has authenticated, C<&checkauth> restarts the original script
523 (this time, C<&checkauth> returns).
525 The login page is provided using a HTML::Template, which is set in the
526 systempreferences table or at the top of this file. The variable C<$type>
527 selects which template to use, either the opac or the intranet
528 authentification template.
530 C<&checkauth> returns a user ID, a cookie, and a session ID. The
531 cookie should be sent back to the browser; it verifies that the user
536 sub _version_check ($$) {
540 # If Version syspref is unavailable, it means Koha is beeing installed,
541 # and so we must redirect to OPAC maintenance page or to the WebInstaller
542 # also, if OpacMaintenance is ON, OPAC should redirect to maintenance
543 if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') {
544 warn "OPAC Install required, redirecting to maintenance";
545 print $query->redirect("/cgi-bin/koha/maintenance.pl");
547 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
548 if ($type ne 'opac') {
549 warn "Install required, redirecting to Installer";
550 print $query->redirect("/cgi-bin/koha/installer/install.pl");
553 warn "OPAC Install required, redirecting to maintenance";
554 print $query->redirect("/cgi-bin/koha/maintenance.pl");
559 # check that database and koha version are the same
560 # there is no DB version, it's a fresh install,
561 # go to web installer
562 # there is a DB version, compare it to the code version
563 my $kohaversion=C4::Context::KOHAVERSION;
564 # remove the 3 last . to have a Perl number
565 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
566 $debug and print STDERR "kohaversion : $kohaversion\n";
567 if ($version < $kohaversion){
568 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
569 if ($type ne 'opac'){
570 warn sprintf($warning, 'Installer');
571 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
573 warn sprintf("OPAC: " . $warning, 'maintenance');
574 print $query->redirect("/cgi-bin/koha/maintenance.pl");
582 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
583 printf L join("\n",@_);
589 $debug and warn "Checking Auth";
590 # $authnotrequired will be set for scripts which will run without authentication
591 my $authnotrequired = shift;
592 my $flagsrequired = shift;
594 $type = 'opac' unless $type;
596 my $dbh = C4::Context->dbh;
597 my $timeout = C4::Context->preference('timeout');
599 if ($timeout =~ /(\d+)[dD]/) {
600 $timeout = $1 * 86400;
602 $timeout = 600 unless $timeout;
604 _version_check($type,$query);
608 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
609 my $logout = $query->param('logout.x');
611 if ( $userid = $ENV{'REMOTE_USER'} ) {
612 # Using Basic Authentication, no cookies required
613 $cookie = $query->cookie(
614 -name => 'CGISESSID',
620 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
621 my $session = get_session($sessionID);
622 C4::Context->_new_userenv($sessionID);
623 my ($ip, $lasttime, $sessiontype);
625 C4::Context::set_userenv(
626 $session->param('number'), $session->param('id'),
627 $session->param('cardnumber'), $session->param('firstname'),
628 $session->param('surname'), $session->param('branch'),
629 $session->param('branchname'), $session->param('flags'),
630 $session->param('emailaddress'), $session->param('branchprinter')
632 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
633 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
634 C4::Context::set_shelves_userenv('tot',$session->param('totshelves'));
635 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
636 $ip = $session->param('ip');
637 $lasttime = $session->param('lasttime');
638 $userid = $session->param('id');
639 $sessiontype = $session->param('sessiontype');
641 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
642 #if a user enters an id ne to the id in the current session, we need to log them in...
643 #first we need to clear the anonymous session...
644 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
647 C4::Context->_unset_userenv($sessionID);
652 # voluntary logout the user
655 C4::Context->_unset_userenv($sessionID);
656 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,(strftime "%c",localtime));
660 if ($cas and $caslogout) {
664 elsif ( $lasttime < time() - $timeout ) {
666 $info{'timed_out'} = 1;
668 C4::Context->_unset_userenv($sessionID);
669 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,(strftime "%c",localtime));
673 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
674 # Different ip than originally logged in from
675 $info{'oldip'} = $ip;
676 $info{'newip'} = $ENV{'REMOTE_ADDR'};
677 $info{'different_ip'} = 1;
679 C4::Context->_unset_userenv($sessionID);
680 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,(strftime "%c",localtime), $info{'newip'});
685 $cookie = $query->cookie( CGISESSID => $session->id );
686 $session->param('lasttime',time());
687 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...
688 $flags = haspermission($userid, $flagsrequired);
692 $info{'nopermission'} = 1;
697 unless ($userid || $sessionID) {
698 #we initiate a session prior to checking for a username to allow for anonymous sessions...
699 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
700 my $sessionID = $session->id;
701 C4::Context->_new_userenv($sessionID);
702 $cookie = $query->cookie(CGISESSID => $sessionID);
703 $userid = $query->param('userid');
704 if ($cas || $userid) {
705 my $password = $query->param('password');
706 my ($return, $cardnumber);
707 if ($cas && $query->param('ticket')) {
709 ( $return, $cardnumber, $retuserid ) = checkpw( $dbh, $userid, $password, $query );
710 $userid = $retuserid;
711 $info{'invalidCasLogin'} = 1 unless ($return);
713 ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password, $query );
716 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},localtime);
717 if ( $flags = haspermission( $userid, $flagsrequired ) ) {
721 $info{'nopermission'} = 1;
722 C4::Context->_unset_userenv($sessionID);
725 my ($borrowernumber, $firstname, $surname, $userflags,
726 $branchcode, $branchname, $branchprinter, $emailaddress);
728 if ( $return == 1 ) {
730 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
731 branches.branchname as branchname,
732 branches.branchprinter as branchprinter,
735 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
737 my $sth = $dbh->prepare("$select where userid=?");
738 $sth->execute($userid);
739 unless ($sth->rows) {
740 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
741 $sth = $dbh->prepare("$select where cardnumber=?");
742 $sth->execute($cardnumber);
743 unless ($sth->rows) {
744 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
745 $sth->execute($userid);
746 unless ($sth->rows) {
747 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
752 ($borrowernumber, $firstname, $surname, $userflags,
753 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
754 $debug and print STDERR "AUTH_3 results: " .
755 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
757 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
760 # launch a sequence to check if we have a ip for the branch, i
761 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
763 my $ip = $ENV{'REMOTE_ADDR'};
764 # if they specify at login, use that
765 if ($query->param('branch')) {
766 $branchcode = $query->param('branch');
767 $branchname = GetBranchName($branchcode);
769 my $branches = GetBranches();
770 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
771 # we have to check they are coming from the right ip range
772 my $domain = $branches->{$branchcode}->{'branchip'};
773 if ($ip !~ /^$domain/){
775 $info{'wrongip'} = 1;
780 foreach my $br ( keys %$branches ) {
781 # now we work with the treatment of ip
782 my $domain = $branches->{$br}->{'branchip'};
783 if ( $domain && $ip =~ /^$domain/ ) {
784 $branchcode = $branches->{$br}->{'branchcode'};
786 # new op dev : add the branchprinter and branchname in the cookie
787 $branchprinter = $branches->{$br}->{'branchprinter'};
788 $branchname = $branches->{$br}->{'branchname'};
791 $session->param('number',$borrowernumber);
792 $session->param('id',$userid);
793 $session->param('cardnumber',$cardnumber);
794 $session->param('firstname',$firstname);
795 $session->param('surname',$surname);
796 $session->param('branch',$branchcode);
797 $session->param('branchname',$branchname);
798 $session->param('flags',$userflags);
799 $session->param('emailaddress',$emailaddress);
800 $session->param('ip',$session->remote_addr());
801 $session->param('lasttime',time());
802 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
804 elsif ( $return == 2 ) {
805 #We suppose the user is the superlibrarian
807 $session->param('number',0);
808 $session->param('id',C4::Context->config('user'));
809 $session->param('cardnumber',C4::Context->config('user'));
810 $session->param('firstname',C4::Context->config('user'));
811 $session->param('surname',C4::Context->config('user'));
812 $session->param('branch','NO_LIBRARY_SET');
813 $session->param('branchname','NO_LIBRARY_SET');
814 $session->param('flags',1);
815 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
816 $session->param('ip',$session->remote_addr());
817 $session->param('lasttime',time());
819 C4::Context::set_userenv(
820 $session->param('number'), $session->param('id'),
821 $session->param('cardnumber'), $session->param('firstname'),
822 $session->param('surname'), $session->param('branch'),
823 $session->param('branchname'), $session->param('flags'),
824 $session->param('emailaddress'), $session->param('branchprinter')
827 # Grab borrower's shelves and public shelves and add them to the session
828 # $row_count determines how many records are returned from the db query
829 # and the number of lists to be displayed of each type in the 'Lists' button drop down
830 my $row_count = 10; # FIXME:This probably should be a syspref
831 my ($total, $totshelves, $barshelves, $pubshelves);
832 ($barshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(1, $row_count, $borrowernumber);
833 $total->{'bartotal'} = $totshelves;
834 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
835 $total->{'pubtotal'} = $totshelves;
836 $session->param('barshelves', $barshelves->[0]);
837 $session->param('pubshelves', $pubshelves->[0]);
838 $session->param('totshelves', $total);
840 C4::Context::set_shelves_userenv('bar',$barshelves->[0]);
841 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
842 C4::Context::set_shelves_userenv('tot',$total);
846 $info{'invalid_username_or_password'} = 1;
847 C4::Context->_unset_userenv($sessionID);
850 } # END if ( $userid = $query->param('userid') )
851 elsif ($type eq "opac") {
852 # if we are here this is an anonymous session; add public lists to it and a few other items...
853 # anonymous sessions are created only for the OPAC
854 $debug and warn "Initiating an anonymous session...";
856 # Grab the public shelves and add to the session...
857 my $row_count = 20; # FIXME:This probably should be a syspref
858 my ($total, $totshelves, $pubshelves);
859 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
860 $total->{'pubtotal'} = $totshelves;
861 $session->param('pubshelves', $pubshelves->[0]);
862 $session->param('totshelves', $total);
863 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
864 C4::Context::set_shelves_userenv('tot',$total);
866 # setting a couple of other session vars...
867 $session->param('ip',$session->remote_addr());
868 $session->param('lasttime',time());
869 $session->param('sessiontype','anon');
871 } # END unless ($userid)
872 my $insecure = C4::Context->boolean_preference('insecure');
874 # finished authentification, now respond
875 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
879 $cookie = $query->cookie( CGISESSID => '' );
881 return ( $userid, $cookie, $sessionID, $flags );
886 # AUTH rejected, show the login/password template, after checking the DB.
890 # get the inputs from the incoming query
892 foreach my $name ( param $query) {
893 (next) if ( $name eq 'userid' || $name eq 'password' || $name eq 'ticket' );
894 my $value = $query->param($name);
895 push @inputs, { name => $name, value => $value };
897 # get the branchloop, which we need for authentication
898 my $branches = GetBranches();
900 for my $branch_hash (sort keys %$branches) {
901 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
904 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
905 my $template = gettemplate( $template_name, $type, $query );
906 $template->param(branchloop => \@branch_loop,);
910 casAuthentication => C4::Context->preference("casAuthentication"),
911 suggestion => C4::Context->preference("suggestion"),
912 virtualshelves => C4::Context->preference("virtualshelves"),
913 LibraryName => C4::Context->preference("LibraryName"),
914 opacuserlogin => C4::Context->preference("opacuserlogin"),
915 OpacNav => C4::Context->preference("OpacNav"),
916 opaccredits => C4::Context->preference("opaccredits"),
917 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
918 opacsmallimage => C4::Context->preference("opacsmallimage"),
919 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
920 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
921 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
922 opacuserjs => C4::Context->preference("opacuserjs"),
923 opacbookbag => "" . C4::Context->preference("opacbookbag"),
924 OpacCloud => C4::Context->preference("OpacCloud"),
925 OpacTopissue => C4::Context->preference("OpacTopissue"),
926 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
927 OpacBrowser => C4::Context->preference("OpacBrowser"),
928 opacheader => C4::Context->preference("opacheader"),
929 TagsEnabled => C4::Context->preference("TagsEnabled"),
930 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
931 intranetcolorstylesheet =>
932 C4::Context->preference("intranetcolorstylesheet"),
933 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
934 intranetbookbag => C4::Context->preference("intranetbookbag"),
935 IntranetNav => C4::Context->preference("IntranetNav"),
936 intranetuserjs => C4::Context->preference("intranetuserjs"),
937 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
938 IndependantBranches=> C4::Context->preference("IndependantBranches"),
939 AutoLocation => C4::Context->preference("AutoLocation"),
940 wrongip => $info{'wrongip'}
942 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
946 casServerUrl => login_cas_url(),
947 invalidCasLogin => $info{'invalidCasLogin'}
951 my $self_url = $query->url( -absolute => 1 );
954 LibraryName => C4::Context->preference("LibraryName"),
956 $template->param( \%info );
957 # $cookie = $query->cookie(CGISESSID => $session->id
959 print $query->header(
960 -type => 'text/html',
970 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
972 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
973 cookie, determine if the user has the privileges specified by C<$userflags>.
975 C<check_api_auth> is is meant for authenticating users of web services, and
976 consequently will always return and will not attempt to redirect the user
979 If a valid session cookie is already present, check_api_auth will return a status
980 of "ok", the cookie, and the Koha session ID.
982 If no session cookie is present, check_api_auth will check the 'userid' and 'password
983 parameters and create a session cookie and Koha session if the supplied credentials
986 Possible return values in C<$status> are:
990 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
992 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
994 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
996 =item "expired -- session cookie has expired; API user should resubmit userid and password
1002 sub check_api_auth {
1004 my $flagsrequired = shift;
1006 my $dbh = C4::Context->dbh;
1007 my $timeout = C4::Context->preference('timeout');
1008 $timeout = 600 unless $timeout;
1010 unless (C4::Context->preference('Version')) {
1011 # database has not been installed yet
1012 return ("maintenance", undef, undef);
1014 my $kohaversion=C4::Context::KOHAVERSION;
1015 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1016 if (C4::Context->preference('Version') < $kohaversion) {
1017 # database in need of version update; assume that
1018 # no API should be called while databsae is in
1020 return ("maintenance", undef, undef);
1023 # FIXME -- most of what follows is a copy-and-paste
1024 # of code from checkauth. There is an obvious need
1025 # for refactoring to separate the various parts of
1026 # the authentication code, but as of 2007-11-19 this
1027 # is deferred so as to not introduce bugs into the
1028 # regular authentication code for Koha 3.0.
1030 # see if we have a valid session cookie already
1031 # however, if a userid parameter is present (i.e., from
1032 # a form submission, assume that any current cookie
1034 my $sessionID = undef;
1035 unless ($query->param('userid')) {
1036 $sessionID = $query->cookie("CGISESSID");
1039 my $session = get_session($sessionID);
1040 C4::Context->_new_userenv($sessionID);
1042 C4::Context::set_userenv(
1043 $session->param('number'), $session->param('id'),
1044 $session->param('cardnumber'), $session->param('firstname'),
1045 $session->param('surname'), $session->param('branch'),
1046 $session->param('branchname'), $session->param('flags'),
1047 $session->param('emailaddress'), $session->param('branchprinter')
1050 my $ip = $session->param('ip');
1051 my $lasttime = $session->param('lasttime');
1052 my $userid = $session->param('id');
1053 if ( $lasttime < time() - $timeout ) {
1056 C4::Context->_unset_userenv($sessionID);
1059 return ("expired", undef, undef);
1060 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1061 # IP address changed
1063 C4::Context->_unset_userenv($sessionID);
1066 return ("expired", undef, undef);
1068 my $cookie = $query->cookie( CGISESSID => $session->id );
1069 $session->param('lasttime',time());
1070 my $flags = haspermission($userid, $flagsrequired);
1072 return ("ok", $cookie, $sessionID);
1075 C4::Context->_unset_userenv($sessionID);
1078 return ("failed", undef, undef);
1082 return ("expired", undef, undef);
1086 my $userid = $query->param('userid');
1087 my $password = $query->param('password');
1088 unless ($userid and $password) {
1089 # caller did something wrong, fail the authenticateion
1090 return ("failed", undef, undef);
1092 my ($return, $cardnumber);
1093 if ($cas && $query->param('ticket')) {
1095 ( $return, $cardnumber, $retuserid ) = checkpw( $dbh, $userid, $password, $query );
1096 $userid = $retuserid;
1098 ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password, $query );
1100 if ($return and haspermission( $userid, $flagsrequired)) {
1101 my $session = get_session("");
1102 return ("failed", undef, undef) unless $session;
1104 my $sessionID = $session->id;
1105 C4::Context->_new_userenv($sessionID);
1106 my $cookie = $query->cookie(CGISESSID => $sessionID);
1107 if ( $return == 1 ) {
1109 $borrowernumber, $firstname, $surname,
1110 $userflags, $branchcode, $branchname,
1111 $branchprinter, $emailaddress
1115 "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=?"
1117 $sth->execute($userid);
1119 $borrowernumber, $firstname, $surname,
1120 $userflags, $branchcode, $branchname,
1121 $branchprinter, $emailaddress
1122 ) = $sth->fetchrow if ( $sth->rows );
1124 unless ($sth->rows ) {
1125 my $sth = $dbh->prepare(
1126 "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=?"
1128 $sth->execute($cardnumber);
1130 $borrowernumber, $firstname, $surname,
1131 $userflags, $branchcode, $branchname,
1132 $branchprinter, $emailaddress
1133 ) = $sth->fetchrow if ( $sth->rows );
1135 unless ( $sth->rows ) {
1136 $sth->execute($userid);
1138 $borrowernumber, $firstname, $surname, $userflags,
1139 $branchcode, $branchname, $branchprinter, $emailaddress
1140 ) = $sth->fetchrow if ( $sth->rows );
1144 my $ip = $ENV{'REMOTE_ADDR'};
1145 # if they specify at login, use that
1146 if ($query->param('branch')) {
1147 $branchcode = $query->param('branch');
1148 $branchname = GetBranchName($branchcode);
1150 my $branches = GetBranches();
1152 foreach my $br ( keys %$branches ) {
1153 # now we work with the treatment of ip
1154 my $domain = $branches->{$br}->{'branchip'};
1155 if ( $domain && $ip =~ /^$domain/ ) {
1156 $branchcode = $branches->{$br}->{'branchcode'};
1158 # new op dev : add the branchprinter and branchname in the cookie
1159 $branchprinter = $branches->{$br}->{'branchprinter'};
1160 $branchname = $branches->{$br}->{'branchname'};
1163 $session->param('number',$borrowernumber);
1164 $session->param('id',$userid);
1165 $session->param('cardnumber',$cardnumber);
1166 $session->param('firstname',$firstname);
1167 $session->param('surname',$surname);
1168 $session->param('branch',$branchcode);
1169 $session->param('branchname',$branchname);
1170 $session->param('flags',$userflags);
1171 $session->param('emailaddress',$emailaddress);
1172 $session->param('ip',$session->remote_addr());
1173 $session->param('lasttime',time());
1174 } elsif ( $return == 2 ) {
1175 #We suppose the user is the superlibrarian
1176 $session->param('number',0);
1177 $session->param('id',C4::Context->config('user'));
1178 $session->param('cardnumber',C4::Context->config('user'));
1179 $session->param('firstname',C4::Context->config('user'));
1180 $session->param('surname',C4::Context->config('user'));
1181 $session->param('branch','NO_LIBRARY_SET');
1182 $session->param('branchname','NO_LIBRARY_SET');
1183 $session->param('flags',1);
1184 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1185 $session->param('ip',$session->remote_addr());
1186 $session->param('lasttime',time());
1188 C4::Context::set_userenv(
1189 $session->param('number'), $session->param('id'),
1190 $session->param('cardnumber'), $session->param('firstname'),
1191 $session->param('surname'), $session->param('branch'),
1192 $session->param('branchname'), $session->param('flags'),
1193 $session->param('emailaddress'), $session->param('branchprinter')
1195 return ("ok", $cookie, $sessionID);
1197 return ("failed", undef, undef);
1202 =item check_cookie_auth
1204 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1206 Given a CGISESSID cookie set during a previous login to Koha, determine
1207 if the user has the privileges specified by C<$userflags>.
1209 C<check_cookie_auth> is meant for authenticating special services
1210 such as tools/upload-file.pl that are invoked by other pages that
1211 have been authenticated in the usual way.
1213 Possible return values in C<$status> are:
1217 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1219 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1221 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1223 =item "expired -- session cookie has expired; API user should resubmit userid and password
1229 sub check_cookie_auth {
1231 my $flagsrequired = shift;
1233 my $dbh = C4::Context->dbh;
1234 my $timeout = C4::Context->preference('timeout');
1235 $timeout = 600 unless $timeout;
1237 unless (C4::Context->preference('Version')) {
1238 # database has not been installed yet
1239 return ("maintenance", undef);
1241 my $kohaversion=C4::Context::KOHAVERSION;
1242 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1243 if (C4::Context->preference('Version') < $kohaversion) {
1244 # database in need of version update; assume that
1245 # no API should be called while databsae is in
1247 return ("maintenance", undef);
1250 # FIXME -- most of what follows is a copy-and-paste
1251 # of code from checkauth. There is an obvious need
1252 # for refactoring to separate the various parts of
1253 # the authentication code, but as of 2007-11-23 this
1254 # is deferred so as to not introduce bugs into the
1255 # regular authentication code for Koha 3.0.
1257 # see if we have a valid session cookie already
1258 # however, if a userid parameter is present (i.e., from
1259 # a form submission, assume that any current cookie
1261 unless (defined $cookie and $cookie) {
1262 return ("failed", undef);
1264 my $sessionID = $cookie;
1265 my $session = get_session($sessionID);
1266 C4::Context->_new_userenv($sessionID);
1268 C4::Context::set_userenv(
1269 $session->param('number'), $session->param('id'),
1270 $session->param('cardnumber'), $session->param('firstname'),
1271 $session->param('surname'), $session->param('branch'),
1272 $session->param('branchname'), $session->param('flags'),
1273 $session->param('emailaddress'), $session->param('branchprinter')
1276 my $ip = $session->param('ip');
1277 my $lasttime = $session->param('lasttime');
1278 my $userid = $session->param('id');
1279 if ( $lasttime < time() - $timeout ) {
1282 C4::Context->_unset_userenv($sessionID);
1285 return ("expired", undef);
1286 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1287 # IP address changed
1289 C4::Context->_unset_userenv($sessionID);
1292 return ("expired", undef);
1294 $session->param('lasttime',time());
1295 my $flags = haspermission($userid, $flagsrequired);
1297 return ("ok", $sessionID);
1300 C4::Context->_unset_userenv($sessionID);
1303 return ("failed", undef);
1307 return ("expired", undef);
1314 my $session = get_session($sessionID);
1316 Given a session ID, retrieve the CGI::Session object used to store
1317 the session's state. The session object can be used to store
1318 data that needs to be accessed by different scripts during a
1321 If the C<$sessionID> parameter is an empty string, a new session
1327 my $sessionID = shift;
1328 my $storage_method = C4::Context->preference('SessionStorage');
1329 my $dbh = C4::Context->dbh;
1331 if ($storage_method eq 'mysql'){
1332 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1334 elsif ($storage_method eq 'Pg') {
1335 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1338 # catch all defaults to tmp should work on all systems
1339 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1346 my ( $dbh, $userid, $password, $query ) = @_;
1348 $debug and print "## checkpw - checking LDAP\n";
1349 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1350 ($retval) and return ($retval,$retcard);
1353 if ($cas && $query->param('ticket')) {
1354 $debug and print STDERR "## checkpw - checking CAS\n";
1355 # In case of a CAS authentication, we use the ticket instead of the password
1356 my $ticket = $query->param('ticket');
1357 my ($retval,$retcard,$retuserid) = checkpw_cas($dbh, $ticket, $query); # EXTERNAL AUTH
1358 ($retval) and return ($retval,$retcard,$retuserid);
1365 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1367 $sth->execute($userid);
1369 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1370 $surname, $branchcode, $flags )
1372 if ( md5_base64($password) eq $md5password and $md5password ne "!") {
1374 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1375 $firstname, $surname, $branchcode, $flags );
1376 return 1, $cardnumber;
1381 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1383 $sth->execute($userid);
1385 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1386 $surname, $branchcode, $flags )
1388 if ( md5_base64($password) eq $md5password ) {
1390 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1391 $firstname, $surname, $branchcode, $flags );
1395 if ( $userid && $userid eq C4::Context->config('user')
1396 && "$password" eq C4::Context->config('pass') )
1399 # Koha superuser account
1400 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1403 if ( $userid && $userid eq 'demo'
1404 && "$password" eq 'demo'
1405 && C4::Context->config('demo') )
1408 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1409 # some features won't be effective : modify systempref, modify MARC structure,
1417 my $authflags = getuserflags($flags, $userid, [$dbh]);
1419 Translates integer flags into permissions strings hash.
1421 C<$flags> is the integer userflags value ( borrowers.userflags )
1422 C<$userid> is the members.userid, used for building subpermissions
1423 C<$authflags> is a hashref of permissions
1430 my $dbh = @_ ? shift : C4::Context->dbh;
1432 $flags = 0 unless $flags;
1433 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1436 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1437 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1438 $userflags->{$flag} = 1;
1441 $userflags->{$flag} = 0;
1445 # get subpermissions and merge with top-level permissions
1446 my $user_subperms = get_user_subpermissions($userid);
1447 foreach my $module (keys %$user_subperms) {
1448 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1449 $userflags->{$module} = $user_subperms->{$module};
1455 =item get_user_subpermissions
1459 my $user_perm_hashref = get_user_subpermissions($userid);
1463 Given the userid (note, not the borrowernumber) of a staff user,
1464 return a hashref of hashrefs of the specific subpermissions
1465 accorded to the user. An example return is
1469 export_catalog => 1,
1470 import_patrons => 1,
1474 The top-level hash-key is a module or function code from
1475 userflags.flag, while the second-level key is a code
1478 The results of this function do not give a complete picture
1479 of the functions that a staff user can access; it is also
1480 necessary to check borrowers.flags.
1484 sub get_user_subpermissions {
1487 my $dbh = C4::Context->dbh;
1488 my $sth = $dbh->prepare("SELECT flag, user_permissions.code
1489 FROM user_permissions
1490 JOIN permissions USING (module_bit, code)
1491 JOIN userflags ON (module_bit = bit)
1492 JOIN borrowers USING (borrowernumber)
1494 $sth->execute($userid);
1496 my $user_perms = {};
1497 while (my $perm = $sth->fetchrow_hashref) {
1498 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1503 =item get_all_subpermissions
1507 my $perm_hashref = get_all_subpermissions();
1511 Returns a hashref of hashrefs defining all specific
1512 permissions currently defined. The return value
1513 has the same structure as that of C<get_user_subpermissions>,
1514 except that the innermost hash value is the description
1515 of the subpermission.
1519 sub get_all_subpermissions {
1520 my $dbh = C4::Context->dbh;
1521 my $sth = $dbh->prepare("SELECT flag, code, description
1523 JOIN userflags ON (module_bit = bit)");
1527 while (my $perm = $sth->fetchrow_hashref) {
1528 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1535 $flags = ($userid, $flagsrequired);
1537 C<$userid> the userid of the member
1538 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1540 Returns member's flags or 0 if a permission is not met.
1545 my ($userid, $flagsrequired) = @_;
1546 my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1547 $sth->execute($userid);
1548 my $flags = getuserflags( $sth->fetchrow(), $userid );
1549 if ( $userid eq C4::Context->config('user') ) {
1550 # Super User Account from /etc/koha.conf
1551 $flags->{'superlibrarian'} = 1;
1553 elsif ( $userid eq 'demo' && C4::Context->config('demo') ) {
1554 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1555 $flags->{'superlibrarian'} = 1;
1557 return $flags if $flags->{superlibrarian};
1558 foreach my $module ( keys %$flagsrequired ) {
1559 if (C4::Context->preference('GranularPermissions')) {
1560 my $subperm = $flagsrequired->{$module};
1561 if ($subperm eq '*') {
1562 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1564 return 0 unless ( $flags->{$module} == 1 or
1565 ( ref($flags->{$module}) and
1566 exists $flags->{$module}->{$subperm} and
1567 $flags->{$module}->{$subperm} == 1
1572 return 0 unless ( $flags->{$module} );
1576 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1580 sub getborrowernumber {
1582 my $userenv = C4::Context->userenv;
1583 if ( defined( $userenv ) && ref( $userenv ) eq 'HASH' && $userenv->{number} ) {
1584 return $userenv->{number};
1586 my $dbh = C4::Context->dbh;
1587 for my $field ( 'userid', 'cardnumber' ) {
1589 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1590 $sth->execute($userid);
1592 my ($bnumber) = $sth->fetchrow;
1599 END { } # module clean-up code here (global destructor)