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 warnings; FIXME - Bug 2505
22 use Digest::MD5 qw(md5_base64);
23 use Storable qw(thaw freeze);
29 use C4::Output; # to get the template
32 use C4::Branch; # GetBranches
33 use C4::VirtualShelves;
34 use POSIX qw/strftime/;
37 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap $cas $caslogout $servers $memcached);
40 $VERSION = 3.02; # set version for version checking
43 @EXPORT = qw(&checkauth &get_template_and_user &haspermission &get_user_subpermissions);
44 @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &get_all_subpermissions &get_user_subpermissions);
45 %EXPORT_TAGS = (EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)]);
46 $ldap = C4::Context->config('useldapserver') || 0;
47 $cas = C4::Context->preference('casAuthentication');
48 $caslogout = C4::Context->preference('casLogout');
50 require C4::Auth_with_ldap; # no import
51 import C4::Auth_with_ldap qw(checkpw_ldap);
54 require C4::Auth_with_cas; # no import
55 import C4::Auth_with_cas qw(checkpw_cas login_cas logout_cas login_cas_url);
57 $servers = C4::Context->config('memcached_servers');
59 require Cache::Memcached;
60 $memcached = Cache::Memcached->new({
61 servers => [ $servers ],
63 compress_threshold => 10_000,
64 namespace => C4::Context->config('memcached_namespace') || 'koha',
71 C4::Auth - Authenticates Koha users
81 my ($template, $borrowernumber, $cookie)
82 = get_template_and_user(
84 template_name => "opac-main.tmpl",
88 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
92 output_html_with_http_headers $query, $cookie, $template->output;
96 The main function of this module is to provide
97 authentification. However the get_template_and_user function has
98 been provided so that a users login information is passed along
99 automatically. This gets loaded into the template.
103 =head2 get_template_and_user
105 my ($template, $borrowernumber, $cookie)
106 = get_template_and_user(
108 template_name => "opac-main.tmpl",
111 authnotrequired => 1,
112 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
116 This call passes the C<query>, C<flagsrequired> and C<authnotrequired>
117 to C<&checkauth> (in this module) to perform authentification.
118 See C<&checkauth> for an explanation of these parameters.
120 The C<template_name> is then used to find the correct template for
121 the page. The authenticated users details are loaded onto the
122 template in the HTML::Template LOOP variable C<USER_INFO>. Also the
123 C<sessionID> is passed to the template. This can be used in templates
124 if cookies are disabled. It needs to be put as and input to every
127 More information on the C<gettemplate> sub can be found in the
132 my $SEARCH_HISTORY_INSERT_SQL =<<EOQ;
133 INSERT INTO search_history(userid, sessionid, query_desc, query_cgi, total, time )
134 VALUES ( ?, ?, ?, ?, ?, FROM_UNIXTIME(?))
136 sub get_template_and_user {
139 gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'} );
140 my ( $user, $cookie, $sessionID, $flags ) = checkauth(
142 $in->{'authnotrequired'},
143 $in->{'flagsrequired'},
145 ) unless ($in->{'template_name'}=~/maintenance/);
148 my $insecure = C4::Context->preference('insecure');
149 if ($user or $insecure) {
151 # load the template variables for stylesheets and JavaScript
152 $template->param( css_libs => $in->{'css_libs'} );
153 $template->param( css_module => $in->{'css_module'} );
154 $template->param( css_page => $in->{'css_page'} );
155 $template->param( css_widgets => $in->{'css_widgets'} );
157 $template->param( js_libs => $in->{'js_libs'} );
158 $template->param( js_module => $in->{'js_module'} );
159 $template->param( js_page => $in->{'js_page'} );
160 $template->param( js_widgets => $in->{'js_widgets'} );
163 $template->param( loggedinusername => $user );
164 $template->param( sessionID => $sessionID );
166 my ($total, $pubshelves, $barshelves) = C4::Context->get_shelves_userenv();
167 if (defined($pubshelves)) {
168 $template->param( pubshelves => scalar @{$pubshelves},
169 pubshelvesloop => $pubshelves,
171 $template->param( pubtotal => $total->{'pubtotal'}, ) if ($total->{'pubtotal'} > scalar @{$pubshelves});
173 if (defined($barshelves)) {
174 $template->param( barshelves => scalar @{$barshelves},
175 barshelvesloop => $barshelves,
177 $template->param( bartotal => $total->{'bartotal'}, ) if ($total->{'bartotal'} > scalar @{$barshelves});
180 $borrowernumber = getborrowernumber($user) if defined($user);
182 my ( $borr ) = GetMemberDetails( $borrowernumber );
185 $template->param( "USER_INFO" => \@bordat );
187 my $all_perms = get_all_subpermissions();
189 my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow
190 editcatalogue updatecharges management tools editauthorities serials reports acquisition);
191 # We are going to use the $flags returned by checkauth
192 # to create the template's parameters that will indicate
193 # which menus the user can access.
194 if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) {
195 $template->param( CAN_user_circulate => 1 );
196 $template->param( CAN_user_catalogue => 1 );
197 $template->param( CAN_user_parameters => 1 );
198 $template->param( CAN_user_borrowers => 1 );
199 $template->param( CAN_user_permissions => 1 );
200 $template->param( CAN_user_reserveforothers => 1 );
201 $template->param( CAN_user_borrow => 1 );
202 $template->param( CAN_user_editcatalogue => 1 );
203 $template->param( CAN_user_updatecharges => 1 );
204 $template->param( CAN_user_acquisition => 1 );
205 $template->param( CAN_user_management => 1 );
206 $template->param( CAN_user_tools => 1 );
207 $template->param( CAN_user_editauthorities => 1 );
208 $template->param( CAN_user_serials => 1 );
209 $template->param( CAN_user_reports => 1 );
210 $template->param( CAN_user_staffaccess => 1 );
211 foreach my $module (keys %$all_perms) {
212 foreach my $subperm (keys %{ $all_perms->{$module} }) {
213 $template->param( "CAN_user_${module}_${subperm}" => 1 );
219 foreach my $module (keys %$all_perms) {
220 if ( $flags->{$module} == 1) {
221 foreach my $subperm (keys %{ $all_perms->{$module} }) {
222 $template->param( "CAN_user_${module}_${subperm}" => 1 );
224 } elsif ( ref($flags->{$module}) ) {
225 foreach my $subperm (keys %{ $flags->{$module} } ) {
226 $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} eq '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 using_https => $in->{'query'}->https() ? 1 : 0,
350 noItemTypeImages => C4::Context->preference("noItemTypeImages"),
353 if ( $in->{'type'} eq "intranet" ) {
355 AmazonContent => C4::Context->preference("AmazonContent"),
356 AmazonCoverImages => C4::Context->preference("AmazonCoverImages"),
357 AmazonEnabled => C4::Context->preference("AmazonEnabled"),
358 AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
359 AutoLocation => C4::Context->preference("AutoLocation"),
360 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
361 CircAutocompl => C4::Context->preference("CircAutocompl"),
362 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
363 IndependantBranches => C4::Context->preference("IndependantBranches"),
364 IntranetNav => C4::Context->preference("IntranetNav"),
365 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
366 LibraryName => C4::Context->preference("LibraryName"),
367 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
368 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
369 canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
370 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
371 IntranetFavicon => C4::Context->preference("IntranetFavicon"),
372 intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
373 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
374 IntranetUserCSS => C4::Context->preference("IntranetUserCSS"),
375 intranetuserjs => C4::Context->preference("intranetuserjs"),
376 intranetbookbag => C4::Context->preference("intranetbookbag"),
377 suggestion => C4::Context->preference("suggestion"),
378 virtualshelves => C4::Context->preference("virtualshelves"),
379 StaffSerialIssueDisplayCount => C4::Context->preference("StaffSerialIssueDisplayCount"),
380 NoZebra => C4::Context->preference('NoZebra'),
384 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
385 #TODO : replace LibraryName syspref with 'system name', and remove this html processing
386 my $LibraryNameTitle = C4::Context->preference("LibraryName");
387 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
388 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
389 # variables passed from CGI: opac_css_override and opac_search_limits.
390 my $opac_search_limit = $ENV{'OPAC_SEARCH_LIMIT'};
391 my $opac_limit_override = $ENV{'OPAC_LIMIT_OVERRIDE'};
393 if (($opac_search_limit =~ /branch:(\w+)/ && $opac_limit_override) || $in->{'query'}->param('limit') =~ /branch:(\w+)/){
394 $opac_name = $1; # opac_search_limit is a branch, so we use it.
395 } elsif (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv && C4::Context->userenv->{'branch'}) {
396 $opac_name = C4::Context->userenv->{'branch'};
398 my $checkstyle = C4::Context->preference("opaccolorstylesheet");
399 if ($checkstyle =~ /http/)
401 $template->param( opacexternalsheet => $checkstyle);
404 my $opaccolorstylesheet = C4::Context->preference("opaccolorstylesheet");
405 $template->param( opaccolorstylesheet => $opaccolorstylesheet);
408 AmazonContent => "" . C4::Context->preference("AmazonContent"),
409 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
410 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
411 BranchesLoop => GetBranchesLoop($opac_name),
412 LibraryName => "" . C4::Context->preference("LibraryName"),
413 LibraryNameTitle => "" . $LibraryNameTitle,
414 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
415 OPACAmazonEnabled => C4::Context->preference("OPACAmazonEnabled"),
416 OPACAmazonSimilarItems => C4::Context->preference("OPACAmazonSimilarItems"),
417 OPACAmazonCoverImages => C4::Context->preference("OPACAmazonCoverImages"),
418 OPACAmazonReviews => C4::Context->preference("OPACAmazonReviews"),
419 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
420 OpacHighlightedWords => C4::Context->preference("OpacHighlightedWords"),
421 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
422 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
423 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
424 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
425 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
426 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
427 OPACBaseURL => ($in->{'query'}->https() ? "https://" : "http://") . $ENV{'SERVER_NAME'} .
428 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
429 opac_css_override => $ENV{'OPAC_CSS_OVERRIDE'},
430 opac_search_limit => $opac_search_limit,
431 opac_limit_override => $opac_limit_override,
432 OpacBrowser => C4::Context->preference("OpacBrowser"),
433 OpacCloud => C4::Context->preference("OpacCloud"),
434 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
435 OpacNav => "" . C4::Context->preference("OpacNav"),
436 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
437 OPACPatronDetails => C4::Context->preference("OPACPatronDetails"),
438 OPACPrivacy => C4::Context->preference("OPACPrivacy"),
439 OPACFinesTab => C4::Context->preference("OPACFinesTab"),
440 OpacTopissue => C4::Context->preference("OpacTopissue"),
441 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
442 'Version' => C4::Context->preference('Version'),
443 hidelostitems => C4::Context->preference("hidelostitems"),
444 mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '',
445 opaclayoutstylesheet => "" . C4::Context->preference("opaclayoutstylesheet"),
446 opacstylesheet => "" . C4::Context->preference("opacstylesheet"),
447 opacbookbag => "" . C4::Context->preference("opacbookbag"),
448 opaccredits => "" . C4::Context->preference("opaccredits"),
449 OpacFavicon => C4::Context->preference("OpacFavicon"),
450 opacheader => "" . C4::Context->preference("opacheader"),
451 opaclanguagesdisplay => "" . C4::Context->preference("opaclanguagesdisplay"),
452 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
453 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
454 opacuserjs => C4::Context->preference("opacuserjs"),
455 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
456 reviewson => C4::Context->preference("reviewson"),
457 ShowReviewer => C4::Context->preference("ShowReviewer"),
458 ShowReviewerPhoto => C4::Context->preference("ShowReviewerPhoto"),
459 suggestion => "" . C4::Context->preference("suggestion"),
460 virtualshelves => "" . C4::Context->preference("virtualshelves"),
461 OPACSerialIssueDisplayCount => C4::Context->preference("OPACSerialIssueDisplayCount"),
462 OpacAddMastheadLibraryPulldown => C4::Context->preference("OpacAddMastheadLibraryPulldown"),
463 OPACXSLTDetailsDisplay => C4::Context->preference("OPACXSLTDetailsDisplay"),
464 OPACXSLTResultsDisplay => C4::Context->preference("OPACXSLTResultsDisplay"),
465 SyndeticsClientCode => C4::Context->preference("SyndeticsClientCode"),
466 SyndeticsEnabled => C4::Context->preference("SyndeticsEnabled"),
467 SyndeticsCoverImages => C4::Context->preference("SyndeticsCoverImages"),
468 SyndeticsTOC => C4::Context->preference("SyndeticsTOC"),
469 SyndeticsSummary => C4::Context->preference("SyndeticsSummary"),
470 SyndeticsEditions => C4::Context->preference("SyndeticsEditions"),
471 SyndeticsExcerpt => C4::Context->preference("SyndeticsExcerpt"),
472 SyndeticsReviews => C4::Context->preference("SyndeticsReviews"),
473 SyndeticsAuthorNotes => C4::Context->preference("SyndeticsAuthorNotes"),
474 SyndeticsAwards => C4::Context->preference("SyndeticsAwards"),
475 SyndeticsSeries => C4::Context->preference("SyndeticsSeries"),
476 SyndeticsCoverImageSize => C4::Context->preference("SyndeticsCoverImageSize"),
479 $template->param(OpacPublic => '1') if ($user || C4::Context->preference("OpacPublic"));
481 $template->param(listloop=>[{shelfname=>"Freelist", shelfnumber=>110}]);
482 return ( $template, $borrowernumber, $cookie, $flags);
487 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
489 Verifies that the user is authorized to run this script. If
490 the user is authorized, a (userid, cookie, session-id, flags)
491 quadruple is returned. If the user is not authorized but does
492 not have the required privilege (see $flagsrequired below), it
493 displays an error page and exits. Otherwise, it displays the
494 login page and exits.
496 Note that C<&checkauth> will return if and only if the user
497 is authorized, so it should be called early on, before any
498 unfinished operations (e.g., if you've opened a file, then
499 C<&checkauth> won't close it for you).
501 C<$query> is the CGI object for the script calling C<&checkauth>.
503 The C<$noauth> argument is optional. If it is set, then no
504 authorization is required for the script.
506 C<&checkauth> fetches user and session information from C<$query> and
507 ensures that the user is authorized to run scripts that require
510 The C<$flagsrequired> argument specifies the required privileges
511 the user must have if the username and password are correct.
512 It should be specified as a reference-to-hash; keys in the hash
513 should be the "flags" for the user, as specified in the Members
514 intranet module. Any key specified must correspond to a "flag"
515 in the userflags table. E.g., { circulate => 1 } would specify
516 that the user must have the "circulate" privilege in order to
517 proceed. To make sure that access control is correct, the
518 C<$flagsrequired> parameter must be specified correctly.
520 Koha also has a concept of sub-permissions, also known as
521 granular permissions. This makes the value of each key
522 in the C<flagsrequired> hash take on an additional
527 The user must have access to all subfunctions of the module
528 specified by the hash key.
532 The user must have access to at least one subfunction of the module
533 specified by the hash key.
535 specific permission, e.g., 'export_catalog'
537 The user must have access to the specific subfunction list, which
538 must correspond to a row in the permissions table.
540 The C<$type> argument specifies whether the template should be
541 retrieved from the opac or intranet directory tree. "opac" is
542 assumed if it is not specified; however, if C<$type> is specified,
543 "intranet" is assumed if it is not "opac".
545 If C<$query> does not have a valid session ID associated with it
546 (i.e., the user has not logged in) or if the session has expired,
547 C<&checkauth> presents the user with a login page (from the point of
548 view of the original script, C<&checkauth> does not return). Once the
549 user has authenticated, C<&checkauth> restarts the original script
550 (this time, C<&checkauth> returns).
552 The login page is provided using a HTML::Template, which is set in the
553 systempreferences table or at the top of this file. The variable C<$type>
554 selects which template to use, either the opac or the intranet
555 authentification template.
557 C<&checkauth> returns a user ID, a cookie, and a session ID. The
558 cookie should be sent back to the browser; it verifies that the user
563 sub _version_check ($$) {
567 # If Version syspref is unavailable, it means Koha is beeing installed,
568 # and so we must redirect to OPAC maintenance page or to the WebInstaller
569 # also, if OpacMaintenance is ON, OPAC should redirect to maintenance
570 if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') {
571 warn "OPAC Install required, redirecting to maintenance";
572 print $query->redirect("/cgi-bin/koha/maintenance.pl");
574 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
575 if ($type ne 'opac') {
576 warn "Install required, redirecting to Installer";
577 print $query->redirect("/cgi-bin/koha/installer/install.pl");
580 warn "OPAC Install required, redirecting to maintenance";
581 print $query->redirect("/cgi-bin/koha/maintenance.pl");
586 # check that database and koha version are the same
587 # there is no DB version, it's a fresh install,
588 # go to web installer
589 # there is a DB version, compare it to the code version
590 my $kohaversion=C4::Context::KOHAVERSION;
591 # remove the 3 last . to have a Perl number
592 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
593 $debug and print STDERR "kohaversion : $kohaversion\n";
594 if ($version < $kohaversion){
595 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
596 if ($type ne 'opac'){
597 warn sprintf($warning, 'Installer');
598 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
600 warn sprintf("OPAC: " . $warning, 'maintenance');
601 print $query->redirect("/cgi-bin/koha/maintenance.pl");
609 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
610 printf L join("\n",@_);
616 $debug and warn "Checking Auth";
617 # $authnotrequired will be set for scripts which will run without authentication
618 my $authnotrequired = shift;
619 my $flagsrequired = shift;
621 $type = 'opac' unless $type;
623 my $dbh = C4::Context->dbh;
624 my $timeout = C4::Context->preference('timeout');
626 if ($timeout =~ /(\d+)[dD]/) {
627 $timeout = $1 * 86400;
629 $timeout = 600 unless $timeout;
631 _version_check($type,$query);
635 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
636 my $logout = $query->param('logout.x');
638 if ( $userid = $ENV{'REMOTE_USER'} ) {
639 # Using Basic Authentication, no cookies required
640 $cookie = $query->cookie(
641 -name => 'CGISESSID',
647 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
648 my $session = get_session($sessionID);
649 C4::Context->_new_userenv($sessionID);
650 my ($ip, $lasttime, $sessiontype);
652 C4::Context::set_userenv(
653 $session->param('number'), $session->param('id'),
654 $session->param('cardnumber'), $session->param('firstname'),
655 $session->param('surname'), $session->param('branch'),
656 $session->param('branchname'), $session->param('flags'),
657 $session->param('emailaddress'), $session->param('branchprinter')
659 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
660 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
661 C4::Context::set_shelves_userenv('tot',$session->param('totshelves'));
662 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
663 $ip = $session->param('ip');
664 $lasttime = $session->param('lasttime');
665 $userid = $session->param('id');
666 $sessiontype = $session->param('sessiontype');
668 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
669 #if a user enters an id ne to the id in the current session, we need to log them in...
670 #first we need to clear the anonymous session...
671 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
674 C4::Context->_unset_userenv($sessionID);
679 # voluntary logout the user
682 C4::Context->_unset_userenv($sessionID);
683 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,(strftime "%c",localtime));
687 if ($cas and $caslogout) {
691 elsif ( $lasttime < time() - $timeout ) {
693 $info{'timed_out'} = 1;
695 C4::Context->_unset_userenv($sessionID);
696 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,(strftime "%c",localtime));
700 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
701 # Different ip than originally logged in from
702 $info{'oldip'} = $ip;
703 $info{'newip'} = $ENV{'REMOTE_ADDR'};
704 $info{'different_ip'} = 1;
706 C4::Context->_unset_userenv($sessionID);
707 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,(strftime "%c",localtime), $info{'newip'});
712 $cookie = $query->cookie( CGISESSID => $session->id );
713 $session->param('lasttime',time());
714 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...
715 $flags = haspermission($userid, $flagsrequired);
719 $info{'nopermission'} = 1;
724 unless ($userid || $sessionID) {
725 #we initiate a session prior to checking for a username to allow for anonymous sessions...
726 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
727 my $sessionID = $session->id;
728 C4::Context->_new_userenv($sessionID);
729 $cookie = $query->cookie(CGISESSID => $sessionID);
730 $userid = $query->param('userid');
731 if ($cas || $userid) {
732 my $password = $query->param('password');
733 my ($return, $cardnumber);
734 if ($cas && $query->param('ticket')) {
736 ( $return, $cardnumber, $retuserid ) = checkpw( $dbh, $userid, $password, $query );
737 $userid = $retuserid;
738 $info{'invalidCasLogin'} = 1 unless ($return);
741 ( $return, $retuserid ) = checkpw( $dbh, $userid, $password, $query );
742 $userid = $retuserid if ($retuserid ne '');
745 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},(strftime '%c', localtime));
746 if ( $flags = haspermission( $userid, $flagsrequired ) ) {
750 $info{'nopermission'} = 1;
751 C4::Context->_unset_userenv($sessionID);
754 my ($borrowernumber, $firstname, $surname, $userflags,
755 $branchcode, $branchname, $branchprinter, $emailaddress);
757 if ( $return == 1 ) {
759 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
760 branches.branchname as branchname,
761 branches.branchprinter as branchprinter,
764 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
766 my $sth = $dbh->prepare("$select where userid=?");
767 $sth->execute($userid);
768 unless ($sth->rows) {
769 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
770 $sth = $dbh->prepare("$select where cardnumber=?");
771 $sth->execute($cardnumber);
773 unless ($sth->rows) {
774 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
775 $sth->execute($userid);
776 unless ($sth->rows) {
777 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
782 ($borrowernumber, $firstname, $surname, $userflags,
783 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
784 $debug and print STDERR "AUTH_3 results: " .
785 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
787 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
790 # launch a sequence to check if we have a ip for the branch, i
791 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
793 my $ip = $ENV{'REMOTE_ADDR'};
794 # if they specify at login, use that
795 if ($query->param('branch')) {
796 $branchcode = $query->param('branch');
797 $branchname = GetBranchName($branchcode);
799 my $branches = GetBranches();
800 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
801 # we have to check they are coming from the right ip range
802 my $domain = $branches->{$branchcode}->{'branchip'};
803 if ($ip !~ /^$domain/){
805 $info{'wrongip'} = 1;
810 foreach my $br ( keys %$branches ) {
811 # now we work with the treatment of ip
812 my $domain = $branches->{$br}->{'branchip'};
813 if ( $domain && $ip =~ /^$domain/ ) {
814 $branchcode = $branches->{$br}->{'branchcode'};
816 # new op dev : add the branchprinter and branchname in the cookie
817 $branchprinter = $branches->{$br}->{'branchprinter'};
818 $branchname = $branches->{$br}->{'branchname'};
821 $session->param('number',$borrowernumber);
822 $session->param('id',$userid);
823 $session->param('cardnumber',$cardnumber);
824 $session->param('firstname',$firstname);
825 $session->param('surname',$surname);
826 $session->param('branch',$branchcode);
827 $session->param('branchname',$branchname);
828 $session->param('flags',$userflags);
829 $session->param('emailaddress',$emailaddress);
830 $session->param('ip',$session->remote_addr());
831 $session->param('lasttime',time());
832 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
834 elsif ( $return == 2 ) {
835 #We suppose the user is the superlibrarian
837 $session->param('number',0);
838 $session->param('id',C4::Context->config('user'));
839 $session->param('cardnumber',C4::Context->config('user'));
840 $session->param('firstname',C4::Context->config('user'));
841 $session->param('surname',C4::Context->config('user'));
842 $session->param('branch','NO_LIBRARY_SET');
843 $session->param('branchname','NO_LIBRARY_SET');
844 $session->param('flags',1);
845 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
846 $session->param('ip',$session->remote_addr());
847 $session->param('lasttime',time());
849 C4::Context::set_userenv(
850 $session->param('number'), $session->param('id'),
851 $session->param('cardnumber'), $session->param('firstname'),
852 $session->param('surname'), $session->param('branch'),
853 $session->param('branchname'), $session->param('flags'),
854 $session->param('emailaddress'), $session->param('branchprinter')
857 # Grab borrower's shelves and public shelves and add them to the session
858 # $row_count determines how many records are returned from the db query
859 # and the number of lists to be displayed of each type in the 'Lists' button drop down
860 my $row_count = 10; # FIXME:This probably should be a syspref
861 my ($total, $totshelves, $barshelves, $pubshelves);
862 ($barshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(1, $row_count, $borrowernumber);
863 $total->{'bartotal'} = $totshelves;
864 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
865 $total->{'pubtotal'} = $totshelves;
866 $session->param('barshelves', $barshelves);
867 $session->param('pubshelves', $pubshelves);
868 $session->param('totshelves', $total);
870 C4::Context::set_shelves_userenv('bar',$barshelves);
871 C4::Context::set_shelves_userenv('pub',$pubshelves);
872 C4::Context::set_shelves_userenv('tot',$total);
876 $info{'invalid_username_or_password'} = 1;
877 C4::Context->_unset_userenv($sessionID);
880 } # END if ( $userid = $query->param('userid') )
881 elsif ($type eq "opac") {
882 # if we are here this is an anonymous session; add public lists to it and a few other items...
883 # anonymous sessions are created only for the OPAC
884 $debug and warn "Initiating an anonymous session...";
886 # Grab the public shelves and add to the session...
887 my $row_count = 20; # FIXME:This probably should be a syspref
888 my ($total, $totshelves, $pubshelves);
889 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
890 $total->{'pubtotal'} = $totshelves;
891 $session->param('pubshelves', $pubshelves);
892 $session->param('totshelves', $total);
893 C4::Context::set_shelves_userenv('pub',$pubshelves);
894 C4::Context::set_shelves_userenv('tot',$total);
896 # setting a couple of other session vars...
897 $session->param('ip',$session->remote_addr());
898 $session->param('lasttime',time());
899 $session->param('sessiontype','anon');
901 } # END unless ($userid)
902 my $insecure = C4::Context->boolean_preference('insecure');
904 # finished authentification, now respond
905 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
909 $cookie = $query->cookie( CGISESSID => '' );
911 return ( $userid, $cookie, $sessionID, $flags );
916 # AUTH rejected, show the login/password template, after checking the DB.
920 # get the inputs from the incoming query
922 foreach my $name ( param $query) {
923 (next) if ( $name eq 'userid' || $name eq 'password' || $name eq 'ticket' );
924 my $value = $query->param($name);
925 push @inputs, { name => $name, value => $value };
927 # get the branchloop, which we need for authentication
928 my $branches = GetBranches();
930 for my $branch_hash (sort keys %$branches) {
931 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
934 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
935 my $template = gettemplate( $template_name, $type, $query );
936 $template->param(branchloop => \@branch_loop,);
937 my $checkstyle = C4::Context->preference("opaccolorstylesheet");
938 if ($checkstyle =~ /\//)
940 $template->param( opacexternalsheet => $checkstyle);
943 my $opaccolorstylesheet = C4::Context->preference("opaccolorstylesheet");
944 $template->param( opaccolorstylesheet => $opaccolorstylesheet);
949 casAuthentication => C4::Context->preference("casAuthentication"),
950 suggestion => C4::Context->preference("suggestion"),
951 virtualshelves => C4::Context->preference("virtualshelves"),
952 LibraryName => C4::Context->preference("LibraryName"),
953 opacuserlogin => C4::Context->preference("opacuserlogin"),
954 OpacNav => C4::Context->preference("OpacNav"),
955 opaccredits => C4::Context->preference("opaccredits"),
956 OpacFavicon => C4::Context->preference("OpacFavicon"),
957 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
958 opacsmallimage => C4::Context->preference("opacsmallimage"),
959 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
960 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
961 opacuserjs => C4::Context->preference("opacuserjs"),
962 opacbookbag => "" . C4::Context->preference("opacbookbag"),
963 OpacCloud => C4::Context->preference("OpacCloud"),
964 OpacTopissue => C4::Context->preference("OpacTopissue"),
965 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
966 OpacBrowser => C4::Context->preference("OpacBrowser"),
967 opacheader => C4::Context->preference("opacheader"),
968 TagsEnabled => C4::Context->preference("TagsEnabled"),
969 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
970 opacstylesheet => C4::Context->preference("opacstylesheet"),
971 intranetcolorstylesheet =>
972 C4::Context->preference("intranetcolorstylesheet"),
973 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
974 intranetbookbag => C4::Context->preference("intranetbookbag"),
975 IntranetNav => C4::Context->preference("IntranetNav"),
976 intranetuserjs => C4::Context->preference("intranetuserjs"),
977 IndependantBranches=> C4::Context->preference("IndependantBranches"),
978 AutoLocation => C4::Context->preference("AutoLocation"),
979 wrongip => $info{'wrongip'},
982 $template->param( OpacPublic => C4::Context->preference("OpacPublic"));
983 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
987 casServerUrl => login_cas_url(),
988 invalidCasLogin => $info{'invalidCasLogin'}
992 my $self_url = $query->url( -absolute => 1 );
995 LibraryName => C4::Context->preference("LibraryName"),
997 $template->param( %info );
998 # $cookie = $query->cookie(CGISESSID => $session->id
1000 print $query->header(
1001 -type => 'text/html',
1002 -charset => 'utf-8',
1009 =head2 check_api_auth
1011 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
1013 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
1014 cookie, determine if the user has the privileges specified by C<$userflags>.
1016 C<check_api_auth> is is meant for authenticating users of web services, and
1017 consequently will always return and will not attempt to redirect the user
1020 If a valid session cookie is already present, check_api_auth will return a status
1021 of "ok", the cookie, and the Koha session ID.
1023 If no session cookie is present, check_api_auth will check the 'userid' and 'password
1024 parameters and create a session cookie and Koha session if the supplied credentials
1027 Possible return values in C<$status> are:
1031 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
1033 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
1035 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1037 =item "expired -- session cookie has expired; API user should resubmit userid and password
1043 sub check_api_auth {
1045 my $flagsrequired = shift;
1047 my $dbh = C4::Context->dbh;
1048 my $timeout = C4::Context->preference('timeout');
1049 $timeout = 600 unless $timeout;
1051 unless (C4::Context->preference('Version')) {
1052 # database has not been installed yet
1053 return ("maintenance", undef, undef);
1055 my $kohaversion=C4::Context::KOHAVERSION;
1056 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1057 if (C4::Context->preference('Version') < $kohaversion) {
1058 # database in need of version update; assume that
1059 # no API should be called while databsae is in
1061 return ("maintenance", undef, undef);
1064 # FIXME -- most of what follows is a copy-and-paste
1065 # of code from checkauth. There is an obvious need
1066 # for refactoring to separate the various parts of
1067 # the authentication code, but as of 2007-11-19 this
1068 # is deferred so as to not introduce bugs into the
1069 # regular authentication code for Koha 3.0.
1071 # see if we have a valid session cookie already
1072 # however, if a userid parameter is present (i.e., from
1073 # a form submission, assume that any current cookie
1075 my $sessionID = undef;
1076 unless ($query->param('userid')) {
1077 $sessionID = $query->cookie("CGISESSID");
1080 my $session = get_session($sessionID);
1081 C4::Context->_new_userenv($sessionID);
1083 C4::Context::set_userenv(
1084 $session->param('number'), $session->param('id'),
1085 $session->param('cardnumber'), $session->param('firstname'),
1086 $session->param('surname'), $session->param('branch'),
1087 $session->param('branchname'), $session->param('flags'),
1088 $session->param('emailaddress'), $session->param('branchprinter')
1091 my $ip = $session->param('ip');
1092 my $lasttime = $session->param('lasttime');
1093 my $userid = $session->param('id');
1094 if ( $lasttime < time() - $timeout ) {
1097 C4::Context->_unset_userenv($sessionID);
1100 return ("expired", undef, undef);
1101 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1102 # IP address changed
1104 C4::Context->_unset_userenv($sessionID);
1107 return ("expired", undef, undef);
1109 my $cookie = $query->cookie( CGISESSID => $session->id );
1110 $session->param('lasttime',time());
1111 my $flags = haspermission($userid, $flagsrequired);
1113 return ("ok", $cookie, $sessionID);
1116 C4::Context->_unset_userenv($sessionID);
1119 return ("failed", undef, undef);
1123 return ("expired", undef, undef);
1127 my $userid = $query->param('userid');
1128 my $password = $query->param('password');
1129 unless ($userid and $password) {
1130 # caller did something wrong, fail the authenticateion
1131 return ("failed", undef, undef);
1133 my ($return, $cardnumber);
1134 if ($cas && $query->param('ticket')) {
1136 ( $return, $cardnumber, $retuserid ) = checkpw( $dbh, $userid, $password, $query );
1137 $userid = $retuserid;
1139 ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password, $query );
1141 if ($return and haspermission( $userid, $flagsrequired)) {
1142 my $session = get_session("");
1143 return ("failed", undef, undef) unless $session;
1145 my $sessionID = $session->id;
1146 C4::Context->_new_userenv($sessionID);
1147 my $cookie = $query->cookie(CGISESSID => $sessionID);
1148 if ( $return == 1 ) {
1150 $borrowernumber, $firstname, $surname,
1151 $userflags, $branchcode, $branchname,
1152 $branchprinter, $emailaddress
1156 "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=?"
1158 $sth->execute($userid);
1160 $borrowernumber, $firstname, $surname,
1161 $userflags, $branchcode, $branchname,
1162 $branchprinter, $emailaddress
1163 ) = $sth->fetchrow if ( $sth->rows );
1165 unless ($sth->rows ) {
1166 my $sth = $dbh->prepare(
1167 "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=?"
1169 $sth->execute($cardnumber);
1171 $borrowernumber, $firstname, $surname,
1172 $userflags, $branchcode, $branchname,
1173 $branchprinter, $emailaddress
1174 ) = $sth->fetchrow if ( $sth->rows );
1176 unless ( $sth->rows ) {
1177 $sth->execute($userid);
1179 $borrowernumber, $firstname, $surname, $userflags,
1180 $branchcode, $branchname, $branchprinter, $emailaddress
1181 ) = $sth->fetchrow if ( $sth->rows );
1185 my $ip = $ENV{'REMOTE_ADDR'};
1186 # if they specify at login, use that
1187 if ($query->param('branch')) {
1188 $branchcode = $query->param('branch');
1189 $branchname = GetBranchName($branchcode);
1191 my $branches = GetBranches();
1193 foreach my $br ( keys %$branches ) {
1194 # now we work with the treatment of ip
1195 my $domain = $branches->{$br}->{'branchip'};
1196 if ( $domain && $ip =~ /^$domain/ ) {
1197 $branchcode = $branches->{$br}->{'branchcode'};
1199 # new op dev : add the branchprinter and branchname in the cookie
1200 $branchprinter = $branches->{$br}->{'branchprinter'};
1201 $branchname = $branches->{$br}->{'branchname'};
1204 $session->param('number',$borrowernumber);
1205 $session->param('id',$userid);
1206 $session->param('cardnumber',$cardnumber);
1207 $session->param('firstname',$firstname);
1208 $session->param('surname',$surname);
1209 $session->param('branch',$branchcode);
1210 $session->param('branchname',$branchname);
1211 $session->param('flags',$userflags);
1212 $session->param('emailaddress',$emailaddress);
1213 $session->param('ip',$session->remote_addr());
1214 $session->param('lasttime',time());
1215 } elsif ( $return == 2 ) {
1216 #We suppose the user is the superlibrarian
1217 $session->param('number',0);
1218 $session->param('id',C4::Context->config('user'));
1219 $session->param('cardnumber',C4::Context->config('user'));
1220 $session->param('firstname',C4::Context->config('user'));
1221 $session->param('surname',C4::Context->config('user'));
1222 $session->param('branch','NO_LIBRARY_SET');
1223 $session->param('branchname','NO_LIBRARY_SET');
1224 $session->param('flags',1);
1225 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1226 $session->param('ip',$session->remote_addr());
1227 $session->param('lasttime',time());
1229 C4::Context::set_userenv(
1230 $session->param('number'), $session->param('id'),
1231 $session->param('cardnumber'), $session->param('firstname'),
1232 $session->param('surname'), $session->param('branch'),
1233 $session->param('branchname'), $session->param('flags'),
1234 $session->param('emailaddress'), $session->param('branchprinter')
1236 return ("ok", $cookie, $sessionID);
1238 return ("failed", undef, undef);
1243 =head2 check_cookie_auth
1245 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1247 Given a CGISESSID cookie set during a previous login to Koha, determine
1248 if the user has the privileges specified by C<$userflags>.
1250 C<check_cookie_auth> is meant for authenticating special services
1251 such as tools/upload-file.pl that are invoked by other pages that
1252 have been authenticated in the usual way.
1254 Possible return values in C<$status> are:
1258 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1260 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1262 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1264 =item "expired -- session cookie has expired; API user should resubmit userid and password
1270 sub check_cookie_auth {
1272 my $flagsrequired = shift;
1274 my $dbh = C4::Context->dbh;
1275 my $timeout = C4::Context->preference('timeout');
1276 $timeout = 600 unless $timeout;
1278 unless (C4::Context->preference('Version')) {
1279 # database has not been installed yet
1280 return ("maintenance", undef);
1282 my $kohaversion=C4::Context::KOHAVERSION;
1283 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1284 if (C4::Context->preference('Version') < $kohaversion) {
1285 # database in need of version update; assume that
1286 # no API should be called while databsae is in
1288 return ("maintenance", undef);
1291 # FIXME -- most of what follows is a copy-and-paste
1292 # of code from checkauth. There is an obvious need
1293 # for refactoring to separate the various parts of
1294 # the authentication code, but as of 2007-11-23 this
1295 # is deferred so as to not introduce bugs into the
1296 # regular authentication code for Koha 3.0.
1298 # see if we have a valid session cookie already
1299 # however, if a userid parameter is present (i.e., from
1300 # a form submission, assume that any current cookie
1302 unless (defined $cookie and $cookie) {
1303 return ("failed", undef);
1305 my $sessionID = $cookie;
1306 my $session = get_session($sessionID);
1307 C4::Context->_new_userenv($sessionID);
1309 C4::Context::set_userenv(
1310 $session->param('number'), $session->param('id'),
1311 $session->param('cardnumber'), $session->param('firstname'),
1312 $session->param('surname'), $session->param('branch'),
1313 $session->param('branchname'), $session->param('flags'),
1314 $session->param('emailaddress'), $session->param('branchprinter')
1317 my $ip = $session->param('ip');
1318 my $lasttime = $session->param('lasttime');
1319 my $userid = $session->param('id');
1320 if ( $lasttime < time() - $timeout ) {
1323 C4::Context->_unset_userenv($sessionID);
1326 return ("expired", undef);
1327 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1328 # IP address changed
1330 C4::Context->_unset_userenv($sessionID);
1333 return ("expired", undef);
1335 $session->param('lasttime',time());
1336 my $flags = haspermission($userid, $flagsrequired);
1338 return ("ok", $sessionID);
1341 C4::Context->_unset_userenv($sessionID);
1344 return ("failed", undef);
1348 return ("expired", undef);
1355 my $session = get_session($sessionID);
1357 Given a session ID, retrieve the CGI::Session object used to store
1358 the session's state. The session object can be used to store
1359 data that needs to be accessed by different scripts during a
1362 If the C<$sessionID> parameter is an empty string, a new session
1368 my $sessionID = shift;
1369 my $storage_method = C4::Context->preference('SessionStorage');
1370 my $dbh = C4::Context->dbh;
1372 if ($storage_method eq 'mysql'){
1373 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1375 elsif ($storage_method eq 'Pg') {
1376 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1378 elsif ($storage_method eq 'memcached' && $servers){
1379 $session = new CGI::Session("driver:memcached;serializer:yaml;id:md5", $sessionID, { Memcached => $memcached } );
1382 # catch all defaults to tmp should work on all systems
1383 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1390 my ( $dbh, $userid, $password, $query ) = @_;
1392 $debug and print "## checkpw - checking LDAP\n";
1393 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1394 ($retval) and return ($retval,$retcard);
1397 if ($cas && $query->param('ticket')) {
1398 $debug and print STDERR "## checkpw - checking CAS\n";
1399 # In case of a CAS authentication, we use the ticket instead of the password
1400 my $ticket = $query->param('ticket');
1401 my ($retval,$retcard,$retuserid) = checkpw_cas($dbh, $ticket, $query); # EXTERNAL AUTH
1402 ($retval) and return ($retval,$retcard,$retuserid);
1409 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1411 $sth->execute($userid);
1413 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1414 $surname, $branchcode, $flags )
1416 if ( md5_base64($password) eq $md5password and $md5password ne "!") {
1418 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1419 $firstname, $surname, $branchcode, $flags );
1425 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1427 $sth->execute($userid);
1429 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1430 $surname, $branchcode, $flags )
1432 if ( md5_base64($password) eq $md5password ) {
1434 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1435 $firstname, $surname, $branchcode, $flags );
1439 if ( $userid && $userid eq C4::Context->config('user')
1440 && "$password" eq C4::Context->config('pass') )
1443 # Koha superuser account
1444 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1447 if ( $userid && $userid eq 'demo'
1448 && "$password" eq 'demo'
1449 && C4::Context->config('demo') )
1452 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1453 # some features won't be effective : modify systempref, modify MARC structure,
1461 my $authflags = getuserflags($flags, $userid, [$dbh]);
1463 Translates integer flags into permissions strings hash.
1465 C<$flags> is the integer userflags value ( borrowers.userflags )
1466 C<$userid> is the members.userid, used for building subpermissions
1467 C<$authflags> is a hashref of permissions
1474 my $dbh = @_ ? shift : C4::Context->dbh;
1476 $flags = 0 unless $flags;
1477 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1480 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1481 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1482 $userflags->{$flag} = 1;
1485 $userflags->{$flag} = 0;
1489 # get subpermissions and merge with top-level permissions
1490 my $user_subperms = get_user_subpermissions($userid);
1491 foreach my $module (keys %$user_subperms) {
1492 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1493 $userflags->{$module} = $user_subperms->{$module};
1499 =head2 get_user_subpermissions
1501 $user_perm_hashref = get_user_subpermissions($userid);
1503 Given the userid (note, not the borrowernumber) of a staff user,
1504 return a hashref of hashrefs of the specific subpermissions
1505 accorded to the user. An example return is
1509 export_catalog => 1,
1510 import_patrons => 1,
1514 The top-level hash-key is a module or function code from
1515 userflags.flag, while the second-level key is a code
1518 The results of this function do not give a complete picture
1519 of the functions that a staff user can access; it is also
1520 necessary to check borrowers.flags.
1524 sub get_user_subpermissions {
1527 my $dbh = C4::Context->dbh;
1528 my $sth = $dbh->prepare("SELECT flag, user_permissions.code
1529 FROM user_permissions
1530 JOIN permissions USING (module_bit, code)
1531 JOIN userflags ON (module_bit = bit)
1532 JOIN borrowers USING (borrowernumber)
1534 $sth->execute($userid);
1536 my $user_perms = {};
1537 while (my $perm = $sth->fetchrow_hashref) {
1538 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1543 =head2 get_all_subpermissions
1545 my $perm_hashref = get_all_subpermissions();
1547 Returns a hashref of hashrefs defining all specific
1548 permissions currently defined. The return value
1549 has the same structure as that of C<get_user_subpermissions>,
1550 except that the innermost hash value is the description
1551 of the subpermission.
1555 sub get_all_subpermissions {
1556 my $dbh = C4::Context->dbh;
1557 my $sth = $dbh->prepare("SELECT flag, code, description
1559 JOIN userflags ON (module_bit = bit)");
1563 while (my $perm = $sth->fetchrow_hashref) {
1564 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1569 =head2 haspermission
1571 $flags = ($userid, $flagsrequired);
1573 C<$userid> the userid of the member
1574 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1576 Returns member's flags or 0 if a permission is not met.
1581 my ($userid, $flagsrequired) = @_;
1582 my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1583 $sth->execute($userid);
1584 my $flags = getuserflags($sth->fetchrow(), $userid);
1585 if ( $userid eq C4::Context->config('user') ) {
1586 # Super User Account from /etc/koha.conf
1587 $flags->{'superlibrarian'} = 1;
1589 elsif ( $userid eq 'demo' && C4::Context->config('demo') ) {
1590 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1591 $flags->{'superlibrarian'} = 1;
1594 return $flags if $flags->{superlibrarian};
1596 foreach my $module ( keys %$flagsrequired ) {
1597 my $subperm = $flagsrequired->{$module};
1598 if ($subperm eq '*') {
1599 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1601 return 0 unless ( $flags->{$module} == 1 or
1602 ( ref($flags->{$module}) and
1603 exists $flags->{$module}->{$subperm} and
1604 $flags->{$module}->{$subperm} == 1
1610 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1614 sub getborrowernumber {
1616 my $userenv = C4::Context->userenv;
1617 if ( defined( $userenv ) && ref( $userenv ) eq 'HASH' && $userenv->{number} ) {
1618 return $userenv->{number};
1620 my $dbh = C4::Context->dbh;
1621 for my $field ( 'userid', 'cardnumber' ) {
1623 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1624 $sth->execute($userid);
1626 my ($bnumber) = $sth->fetchrow;
1633 END { } # module clean-up code here (global destructor)