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::Templates; # 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 C4::Templates::gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'} );
140 my ( $user, $cookie, $sessionID, $flags );
141 if ( $in->{'template_name'} !~m/maintenance/ ) {
142 ( $user, $cookie, $sessionID, $flags ) = checkauth(
144 $in->{'authnotrequired'},
145 $in->{'flagsrequired'},
151 my $insecure = C4::Context->preference('insecure');
152 if ($user or $insecure) {
154 # load the template variables for stylesheets and JavaScript
155 $template->param( css_libs => $in->{'css_libs'} );
156 $template->param( css_module => $in->{'css_module'} );
157 $template->param( css_page => $in->{'css_page'} );
158 $template->param( css_widgets => $in->{'css_widgets'} );
160 $template->param( js_libs => $in->{'js_libs'} );
161 $template->param( js_module => $in->{'js_module'} );
162 $template->param( js_page => $in->{'js_page'} );
163 $template->param( js_widgets => $in->{'js_widgets'} );
166 $template->param( loggedinusername => $user );
167 $template->param( sessionID => $sessionID );
169 my ($total, $pubshelves, $barshelves) = C4::Context->get_shelves_userenv();
170 if (defined($pubshelves)) {
171 $template->param( pubshelves => scalar @{$pubshelves},
172 pubshelvesloop => $pubshelves,
174 $template->param( pubtotal => $total->{'pubtotal'}, ) if ($total->{'pubtotal'} > scalar @{$pubshelves});
176 if (defined($barshelves)) {
177 $template->param( barshelves => scalar @{$barshelves},
178 barshelvesloop => $barshelves,
180 $template->param( bartotal => $total->{'bartotal'}, ) if ($total->{'bartotal'} > scalar @{$barshelves});
183 $borrowernumber = getborrowernumber($user) if defined($user);
185 my ( $borr ) = GetMemberDetails( $borrowernumber );
188 $template->param( "USER_INFO" => \@bordat );
190 my $all_perms = get_all_subpermissions();
192 my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow
193 editcatalogue updatecharges management tools editauthorities serials reports acquisition);
194 # We are going to use the $flags returned by checkauth
195 # to create the template's parameters that will indicate
196 # which menus the user can access.
197 if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) {
198 $template->param( CAN_user_circulate => 1 );
199 $template->param( CAN_user_catalogue => 1 );
200 $template->param( CAN_user_parameters => 1 );
201 $template->param( CAN_user_borrowers => 1 );
202 $template->param( CAN_user_permissions => 1 );
203 $template->param( CAN_user_reserveforothers => 1 );
204 $template->param( CAN_user_borrow => 1 );
205 $template->param( CAN_user_editcatalogue => 1 );
206 $template->param( CAN_user_updatecharges => 1 );
207 $template->param( CAN_user_acquisition => 1 );
208 $template->param( CAN_user_management => 1 );
209 $template->param( CAN_user_tools => 1 );
210 $template->param( CAN_user_editauthorities => 1 );
211 $template->param( CAN_user_serials => 1 );
212 $template->param( CAN_user_reports => 1 );
213 $template->param( CAN_user_staffaccess => 1 );
214 foreach my $module (keys %$all_perms) {
215 foreach my $subperm (keys %{ $all_perms->{$module} }) {
216 $template->param( "CAN_user_${module}_${subperm}" => 1 );
222 foreach my $module (keys %$all_perms) {
223 if ( $flags->{$module} == 1) {
224 foreach my $subperm (keys %{ $all_perms->{$module} }) {
225 $template->param( "CAN_user_${module}_${subperm}" => 1 );
227 } elsif ( ref($flags->{$module}) ) {
228 foreach my $subperm (keys %{ $flags->{$module} } ) {
229 $template->param( "CAN_user_${module}_${subperm}" => 1 );
236 foreach my $module (keys %$flags) {
237 if ( $flags->{$module} == 1 or ref($flags->{$module}) ) {
238 $template->param( "CAN_user_$module" => 1 );
239 if ($module eq "parameters") {
240 $template->param( CAN_user_management => 1 );
245 # Logged-in opac search history
246 # If the requested template is an opac one and opac search history is enabled
247 if ($in->{type} eq 'opac' && C4::Context->preference('EnableOpacSearchHistory')) {
248 my $dbh = C4::Context->dbh;
249 my $query = "SELECT COUNT(*) FROM search_history WHERE userid=?";
250 my $sth = $dbh->prepare($query);
251 $sth->execute($borrowernumber);
253 # If at least one search has already been performed
254 if ($sth->fetchrow_array > 0) {
255 # We show the link in opac
256 $template->param(ShowOpacRecentSearchLink => 1);
259 # And if there's a cookie with searches performed when the user was not logged in,
260 # we add them to the logged-in search history
261 my $searchcookie = $in->{'query'}->cookie('KohaOpacRecentSearches');
263 $searchcookie = uri_unescape($searchcookie);
264 my @recentSearches = @{thaw($searchcookie) || []};
265 if (@recentSearches) {
266 my $sth = $dbh->prepare($SEARCH_HISTORY_INSERT_SQL);
267 $sth->execute( $borrowernumber,
268 $in->{'query'}->cookie("CGISESSID"),
273 ) foreach @recentSearches;
275 # And then, delete the cookie's content
276 my $newsearchcookie = $in->{'query'}->cookie(
277 -name => 'KohaOpacRecentSearches',
278 -value => freeze([]),
281 $cookie = [$cookie, $newsearchcookie];
286 else { # if this is an anonymous session, setup to display public lists...
288 # load the template variables for stylesheets and JavaScript
289 $template->param( css_libs => $in->{'css_libs'} );
290 $template->param( css_module => $in->{'css_module'} );
291 $template->param( css_page => $in->{'css_page'} );
292 $template->param( css_widgets => $in->{'css_widgets'} );
294 $template->param( js_libs => $in->{'js_libs'} );
295 $template->param( js_module => $in->{'js_module'} );
296 $template->param( js_page => $in->{'js_page'} );
297 $template->param( js_widgets => $in->{'js_widgets'} );
299 $template->param( sessionID => $sessionID );
301 my ($total, $pubshelves) = C4::Context->get_shelves_userenv(); # an anonymous user has no 'barshelves'...
302 if (defined $pubshelves) {
303 $template->param( pubshelves => scalar @{$pubshelves},
304 pubshelvesloop => $pubshelves,
306 $template->param( pubtotal => $total->{'pubtotal'}, ) if ($total->{'pubtotal'} > scalar @{$pubshelves});
310 # Anonymous opac search history
311 # If opac search history is enabled and at least one search has already been performed
312 if (C4::Context->preference('EnableOpacSearchHistory')) {
313 my $searchcookie = $in->{'query'}->cookie('KohaOpacRecentSearches');
315 $searchcookie = uri_unescape($searchcookie);
316 my @recentSearches = @{thaw($searchcookie) || []};
317 # We show the link in opac
318 if (@recentSearches) {
319 $template->param(ShowOpacRecentSearchLink => 1);
324 if(C4::Context->preference('dateformat')){
325 if(C4::Context->preference('dateformat') eq "metric"){
326 $template->param(dateformat_metric => 1);
327 } elsif(C4::Context->preference('dateformat') eq "us"){
328 $template->param(dateformat_us => 1);
330 $template->param(dateformat_iso => 1);
333 $template->param(dateformat_iso => 1);
336 # these template parameters are set the same regardless of $in->{'type'}
338 "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
339 EnhancedMessagingPreferences => C4::Context->preference('EnhancedMessagingPreferences'),
340 GoogleJackets => C4::Context->preference("GoogleJackets"),
341 OpenLibraryCovers => C4::Context->preference("OpenLibraryCovers"),
342 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
343 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
344 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
345 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
346 TagsEnabled => C4::Context->preference("TagsEnabled"),
347 hide_marc => C4::Context->preference("hide_marc"),
348 item_level_itypes => C4::Context->preference('item-level_itypes'),
349 patronimages => C4::Context->preference("patronimages"),
350 singleBranchMode => C4::Context->preference("singleBranchMode"),
351 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
352 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
353 using_https => $in->{'query'}->https() ? 1 : 0,
354 noItemTypeImages => C4::Context->preference("noItemTypeImages"),
357 if ( $in->{'type'} eq "intranet" ) {
359 AmazonContent => C4::Context->preference("AmazonContent"),
360 AmazonCoverImages => C4::Context->preference("AmazonCoverImages"),
361 AmazonEnabled => C4::Context->preference("AmazonEnabled"),
362 AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
363 AutoLocation => C4::Context->preference("AutoLocation"),
364 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
365 CircAutocompl => C4::Context->preference("CircAutocompl"),
366 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
367 IndependantBranches => C4::Context->preference("IndependantBranches"),
368 IntranetNav => C4::Context->preference("IntranetNav"),
369 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
370 LibraryName => C4::Context->preference("LibraryName"),
371 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
372 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
373 canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
374 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
375 IntranetFavicon => C4::Context->preference("IntranetFavicon"),
376 intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
377 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
378 IntranetUserCSS => C4::Context->preference("IntranetUserCSS"),
379 intranetuserjs => C4::Context->preference("intranetuserjs"),
380 intranetbookbag => C4::Context->preference("intranetbookbag"),
381 suggestion => C4::Context->preference("suggestion"),
382 virtualshelves => C4::Context->preference("virtualshelves"),
383 StaffSerialIssueDisplayCount => C4::Context->preference("StaffSerialIssueDisplayCount"),
384 NoZebra => C4::Context->preference('NoZebra'),
388 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
389 #TODO : replace LibraryName syspref with 'system name', and remove this html processing
390 my $LibraryNameTitle = C4::Context->preference("LibraryName");
391 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
392 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
393 # variables passed from CGI: opac_css_override and opac_search_limits.
394 my $opac_search_limit = $ENV{'OPAC_SEARCH_LIMIT'};
395 my $opac_limit_override = $ENV{'OPAC_LIMIT_OVERRIDE'};
397 if (($opac_search_limit =~ /branch:(\w+)/ && $opac_limit_override) || $in->{'query'}->param('limit') =~ /branch:(\w+)/){
398 $opac_name = $1; # opac_search_limit is a branch, so we use it.
399 } elsif (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv && C4::Context->userenv->{'branch'}) {
400 $opac_name = C4::Context->userenv->{'branch'};
402 my $checkstyle = C4::Context->preference("opaccolorstylesheet");
403 if ($checkstyle =~ /http/)
405 $template->param( opacexternalsheet => $checkstyle);
408 my $opaccolorstylesheet = C4::Context->preference("opaccolorstylesheet");
409 $template->param( opaccolorstylesheet => $opaccolorstylesheet);
412 AmazonContent => "" . C4::Context->preference("AmazonContent"),
413 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
414 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
415 BranchesLoop => GetBranchesLoop($opac_name),
416 LibraryName => "" . C4::Context->preference("LibraryName"),
417 LibraryNameTitle => "" . $LibraryNameTitle,
418 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
419 OPACAmazonEnabled => C4::Context->preference("OPACAmazonEnabled"),
420 OPACAmazonSimilarItems => C4::Context->preference("OPACAmazonSimilarItems"),
421 OPACAmazonCoverImages => C4::Context->preference("OPACAmazonCoverImages"),
422 OPACAmazonReviews => C4::Context->preference("OPACAmazonReviews"),
423 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
424 OpacHighlightedWords => C4::Context->preference("OpacHighlightedWords"),
425 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
426 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
427 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
428 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
429 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
430 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
431 OPACBaseURL => ($in->{'query'}->https() ? "https://" : "http://") . $ENV{'SERVER_NAME'} .
432 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
433 opac_css_override => $ENV{'OPAC_CSS_OVERRIDE'},
434 opac_search_limit => $opac_search_limit,
435 opac_limit_override => $opac_limit_override,
436 OpacBrowser => C4::Context->preference("OpacBrowser"),
437 OpacCloud => C4::Context->preference("OpacCloud"),
438 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
439 OpacNav => "" . C4::Context->preference("OpacNav"),
440 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
441 OPACPatronDetails => C4::Context->preference("OPACPatronDetails"),
442 OPACPrivacy => C4::Context->preference("OPACPrivacy"),
443 OPACFinesTab => C4::Context->preference("OPACFinesTab"),
444 OpacTopissue => C4::Context->preference("OpacTopissue"),
445 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
446 'Version' => C4::Context->preference('Version'),
447 hidelostitems => C4::Context->preference("hidelostitems"),
448 mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '',
449 opaclayoutstylesheet => "" . C4::Context->preference("opaclayoutstylesheet"),
450 opacstylesheet => "" . C4::Context->preference("opacstylesheet"),
451 opacbookbag => "" . C4::Context->preference("opacbookbag"),
452 opaccredits => "" . C4::Context->preference("opaccredits"),
453 OpacFavicon => C4::Context->preference("OpacFavicon"),
454 opacheader => "" . C4::Context->preference("opacheader"),
455 opaclanguagesdisplay => "" . C4::Context->preference("opaclanguagesdisplay"),
456 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
457 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
458 opacuserjs => C4::Context->preference("opacuserjs"),
459 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
460 reviewson => C4::Context->preference("reviewson"),
461 ShowReviewer => C4::Context->preference("ShowReviewer"),
462 ShowReviewerPhoto => C4::Context->preference("ShowReviewerPhoto"),
463 suggestion => "" . C4::Context->preference("suggestion"),
464 virtualshelves => "" . C4::Context->preference("virtualshelves"),
465 OPACSerialIssueDisplayCount => C4::Context->preference("OPACSerialIssueDisplayCount"),
466 OpacAddMastheadLibraryPulldown => C4::Context->preference("OpacAddMastheadLibraryPulldown"),
467 OPACXSLTDetailsDisplay => C4::Context->preference("OPACXSLTDetailsDisplay"),
468 OPACXSLTResultsDisplay => C4::Context->preference("OPACXSLTResultsDisplay"),
469 SyndeticsClientCode => C4::Context->preference("SyndeticsClientCode"),
470 SyndeticsEnabled => C4::Context->preference("SyndeticsEnabled"),
471 SyndeticsCoverImages => C4::Context->preference("SyndeticsCoverImages"),
472 SyndeticsTOC => C4::Context->preference("SyndeticsTOC"),
473 SyndeticsSummary => C4::Context->preference("SyndeticsSummary"),
474 SyndeticsEditions => C4::Context->preference("SyndeticsEditions"),
475 SyndeticsExcerpt => C4::Context->preference("SyndeticsExcerpt"),
476 SyndeticsReviews => C4::Context->preference("SyndeticsReviews"),
477 SyndeticsAuthorNotes => C4::Context->preference("SyndeticsAuthorNotes"),
478 SyndeticsAwards => C4::Context->preference("SyndeticsAwards"),
479 SyndeticsSeries => C4::Context->preference("SyndeticsSeries"),
480 SyndeticsCoverImageSize => C4::Context->preference("SyndeticsCoverImageSize"),
483 $template->param(OpacPublic => '1') if ($user || C4::Context->preference("OpacPublic"));
485 $template->param(listloop=>[{shelfname=>"Freelist", shelfnumber=>110}]);
486 return ( $template, $borrowernumber, $cookie, $flags);
491 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
493 Verifies that the user is authorized to run this script. If
494 the user is authorized, a (userid, cookie, session-id, flags)
495 quadruple is returned. If the user is not authorized but does
496 not have the required privilege (see $flagsrequired below), it
497 displays an error page and exits. Otherwise, it displays the
498 login page and exits.
500 Note that C<&checkauth> will return if and only if the user
501 is authorized, so it should be called early on, before any
502 unfinished operations (e.g., if you've opened a file, then
503 C<&checkauth> won't close it for you).
505 C<$query> is the CGI object for the script calling C<&checkauth>.
507 The C<$noauth> argument is optional. If it is set, then no
508 authorization is required for the script.
510 C<&checkauth> fetches user and session information from C<$query> and
511 ensures that the user is authorized to run scripts that require
514 The C<$flagsrequired> argument specifies the required privileges
515 the user must have if the username and password are correct.
516 It should be specified as a reference-to-hash; keys in the hash
517 should be the "flags" for the user, as specified in the Members
518 intranet module. Any key specified must correspond to a "flag"
519 in the userflags table. E.g., { circulate => 1 } would specify
520 that the user must have the "circulate" privilege in order to
521 proceed. To make sure that access control is correct, the
522 C<$flagsrequired> parameter must be specified correctly.
524 Koha also has a concept of sub-permissions, also known as
525 granular permissions. This makes the value of each key
526 in the C<flagsrequired> hash take on an additional
531 The user must have access to all subfunctions of the module
532 specified by the hash key.
536 The user must have access to at least one subfunction of the module
537 specified by the hash key.
539 specific permission, e.g., 'export_catalog'
541 The user must have access to the specific subfunction list, which
542 must correspond to a row in the permissions table.
544 The C<$type> argument specifies whether the template should be
545 retrieved from the opac or intranet directory tree. "opac" is
546 assumed if it is not specified; however, if C<$type> is specified,
547 "intranet" is assumed if it is not "opac".
549 If C<$query> does not have a valid session ID associated with it
550 (i.e., the user has not logged in) or if the session has expired,
551 C<&checkauth> presents the user with a login page (from the point of
552 view of the original script, C<&checkauth> does not return). Once the
553 user has authenticated, C<&checkauth> restarts the original script
554 (this time, C<&checkauth> returns).
556 The login page is provided using a HTML::Template, which is set in the
557 systempreferences table or at the top of this file. The variable C<$type>
558 selects which template to use, either the opac or the intranet
559 authentification template.
561 C<&checkauth> returns a user ID, a cookie, and a session ID. The
562 cookie should be sent back to the browser; it verifies that the user
567 sub _version_check ($$) {
571 # If Version syspref is unavailable, it means Koha is beeing installed,
572 # and so we must redirect to OPAC maintenance page or to the WebInstaller
573 # also, if OpacMaintenance is ON, OPAC should redirect to maintenance
574 if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') {
575 warn "OPAC Install required, redirecting to maintenance";
576 print $query->redirect("/cgi-bin/koha/maintenance.pl");
578 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
579 if ($type ne 'opac') {
580 warn "Install required, redirecting to Installer";
581 print $query->redirect("/cgi-bin/koha/installer/install.pl");
584 warn "OPAC Install required, redirecting to maintenance";
585 print $query->redirect("/cgi-bin/koha/maintenance.pl");
590 # check that database and koha version are the same
591 # there is no DB version, it's a fresh install,
592 # go to web installer
593 # there is a DB version, compare it to the code version
594 my $kohaversion=C4::Context::KOHAVERSION;
595 # remove the 3 last . to have a Perl number
596 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
597 $debug and print STDERR "kohaversion : $kohaversion\n";
598 if ($version < $kohaversion){
599 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
600 if ($type ne 'opac'){
601 warn sprintf($warning, 'Installer');
602 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
604 warn sprintf("OPAC: " . $warning, 'maintenance');
605 print $query->redirect("/cgi-bin/koha/maintenance.pl");
613 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
614 printf L join("\n",@_);
620 $debug and warn "Checking Auth";
621 # $authnotrequired will be set for scripts which will run without authentication
622 my $authnotrequired = shift;
623 my $flagsrequired = shift;
625 $type = 'opac' unless $type;
627 my $dbh = C4::Context->dbh;
628 my $timeout = C4::Context->preference('timeout');
630 if ($timeout =~ /(\d+)[dD]/) {
631 $timeout = $1 * 86400;
633 $timeout = 600 unless $timeout;
635 _version_check($type,$query);
639 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
640 my $logout = $query->param('logout.x');
642 if ( $userid = $ENV{'REMOTE_USER'} ) {
643 # Using Basic Authentication, no cookies required
644 $cookie = $query->cookie(
645 -name => 'CGISESSID',
651 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
652 my $session = get_session($sessionID);
653 C4::Context->_new_userenv($sessionID);
654 my ($ip, $lasttime, $sessiontype);
656 C4::Context::set_userenv(
657 $session->param('number'), $session->param('id'),
658 $session->param('cardnumber'), $session->param('firstname'),
659 $session->param('surname'), $session->param('branch'),
660 $session->param('branchname'), $session->param('flags'),
661 $session->param('emailaddress'), $session->param('branchprinter')
663 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
664 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
665 C4::Context::set_shelves_userenv('tot',$session->param('totshelves'));
666 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
667 $ip = $session->param('ip');
668 $lasttime = $session->param('lasttime');
669 $userid = $session->param('id');
670 $sessiontype = $session->param('sessiontype');
672 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
673 #if a user enters an id ne to the id in the current session, we need to log them in...
674 #first we need to clear the anonymous session...
675 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
678 C4::Context->_unset_userenv($sessionID);
683 # voluntary logout the user
686 C4::Context->_unset_userenv($sessionID);
687 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,(strftime "%c",localtime));
691 if ($cas and $caslogout) {
695 elsif ( $lasttime < time() - $timeout ) {
697 $info{'timed_out'} = 1;
699 C4::Context->_unset_userenv($sessionID);
700 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,(strftime "%c",localtime));
704 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
705 # Different ip than originally logged in from
706 $info{'oldip'} = $ip;
707 $info{'newip'} = $ENV{'REMOTE_ADDR'};
708 $info{'different_ip'} = 1;
710 C4::Context->_unset_userenv($sessionID);
711 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,(strftime "%c",localtime), $info{'newip'});
716 $cookie = $query->cookie( CGISESSID => $session->id );
717 $session->param('lasttime',time());
718 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...
719 $flags = haspermission($userid, $flagsrequired);
723 $info{'nopermission'} = 1;
728 unless ($userid || $sessionID) {
729 #we initiate a session prior to checking for a username to allow for anonymous sessions...
730 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
731 my $sessionID = $session->id;
732 C4::Context->_new_userenv($sessionID);
733 $cookie = $query->cookie(CGISESSID => $sessionID);
734 $userid = $query->param('userid');
735 if ($cas || $userid) {
736 my $password = $query->param('password');
737 my ($return, $cardnumber);
738 if ($cas && $query->param('ticket')) {
740 ( $return, $cardnumber, $retuserid ) = checkpw( $dbh, $userid, $password, $query );
741 $userid = $retuserid;
742 $info{'invalidCasLogin'} = 1 unless ($return);
745 ( $return, $retuserid ) = checkpw( $dbh, $userid, $password, $query );
746 $userid = $retuserid if ($retuserid ne '');
749 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},(strftime '%c', localtime));
750 if ( $flags = haspermission( $userid, $flagsrequired ) ) {
754 $info{'nopermission'} = 1;
755 C4::Context->_unset_userenv($sessionID);
758 my ($borrowernumber, $firstname, $surname, $userflags,
759 $branchcode, $branchname, $branchprinter, $emailaddress);
761 if ( $return == 1 ) {
763 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
764 branches.branchname as branchname,
765 branches.branchprinter as branchprinter,
768 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
770 my $sth = $dbh->prepare("$select where userid=?");
771 $sth->execute($userid);
772 unless ($sth->rows) {
773 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
774 $sth = $dbh->prepare("$select where cardnumber=?");
775 $sth->execute($cardnumber);
777 unless ($sth->rows) {
778 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
779 $sth->execute($userid);
780 unless ($sth->rows) {
781 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
786 ($borrowernumber, $firstname, $surname, $userflags,
787 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
788 $debug and print STDERR "AUTH_3 results: " .
789 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
791 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
794 # launch a sequence to check if we have a ip for the branch, i
795 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
797 my $ip = $ENV{'REMOTE_ADDR'};
798 # if they specify at login, use that
799 if ($query->param('branch')) {
800 $branchcode = $query->param('branch');
801 $branchname = GetBranchName($branchcode);
803 my $branches = GetBranches();
804 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
805 # we have to check they are coming from the right ip range
806 my $domain = $branches->{$branchcode}->{'branchip'};
807 if ($ip !~ /^$domain/){
809 $info{'wrongip'} = 1;
814 foreach my $br ( keys %$branches ) {
815 # now we work with the treatment of ip
816 my $domain = $branches->{$br}->{'branchip'};
817 if ( $domain && $ip =~ /^$domain/ ) {
818 $branchcode = $branches->{$br}->{'branchcode'};
820 # new op dev : add the branchprinter and branchname in the cookie
821 $branchprinter = $branches->{$br}->{'branchprinter'};
822 $branchname = $branches->{$br}->{'branchname'};
825 $session->param('number',$borrowernumber);
826 $session->param('id',$userid);
827 $session->param('cardnumber',$cardnumber);
828 $session->param('firstname',$firstname);
829 $session->param('surname',$surname);
830 $session->param('branch',$branchcode);
831 $session->param('branchname',$branchname);
832 $session->param('flags',$userflags);
833 $session->param('emailaddress',$emailaddress);
834 $session->param('ip',$session->remote_addr());
835 $session->param('lasttime',time());
836 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
838 elsif ( $return == 2 ) {
839 #We suppose the user is the superlibrarian
841 $session->param('number',0);
842 $session->param('id',C4::Context->config('user'));
843 $session->param('cardnumber',C4::Context->config('user'));
844 $session->param('firstname',C4::Context->config('user'));
845 $session->param('surname',C4::Context->config('user'));
846 $session->param('branch','NO_LIBRARY_SET');
847 $session->param('branchname','NO_LIBRARY_SET');
848 $session->param('flags',1);
849 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
850 $session->param('ip',$session->remote_addr());
851 $session->param('lasttime',time());
853 C4::Context::set_userenv(
854 $session->param('number'), $session->param('id'),
855 $session->param('cardnumber'), $session->param('firstname'),
856 $session->param('surname'), $session->param('branch'),
857 $session->param('branchname'), $session->param('flags'),
858 $session->param('emailaddress'), $session->param('branchprinter')
861 # Grab borrower's shelves and public shelves and add them to the session
862 # $row_count determines how many records are returned from the db query
863 # and the number of lists to be displayed of each type in the 'Lists' button drop down
864 my $row_count = 10; # FIXME:This probably should be a syspref
865 my ($total, $totshelves, $barshelves, $pubshelves);
866 ($barshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(1, $row_count, $borrowernumber);
867 $total->{'bartotal'} = $totshelves;
868 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
869 $total->{'pubtotal'} = $totshelves;
870 $session->param('barshelves', $barshelves);
871 $session->param('pubshelves', $pubshelves);
872 $session->param('totshelves', $total);
874 C4::Context::set_shelves_userenv('bar',$barshelves);
875 C4::Context::set_shelves_userenv('pub',$pubshelves);
876 C4::Context::set_shelves_userenv('tot',$total);
880 $info{'invalid_username_or_password'} = 1;
881 C4::Context->_unset_userenv($sessionID);
884 } # END if ( $userid = $query->param('userid') )
885 elsif ($type eq "opac") {
886 # if we are here this is an anonymous session; add public lists to it and a few other items...
887 # anonymous sessions are created only for the OPAC
888 $debug and warn "Initiating an anonymous session...";
890 # Grab the public shelves and add to the session...
891 my $row_count = 20; # FIXME:This probably should be a syspref
892 my ($total, $totshelves, $pubshelves);
893 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
894 $total->{'pubtotal'} = $totshelves;
895 $session->param('pubshelves', $pubshelves);
896 $session->param('totshelves', $total);
897 C4::Context::set_shelves_userenv('pub',$pubshelves);
898 C4::Context::set_shelves_userenv('tot',$total);
900 # setting a couple of other session vars...
901 $session->param('ip',$session->remote_addr());
902 $session->param('lasttime',time());
903 $session->param('sessiontype','anon');
905 } # END unless ($userid)
906 my $insecure = C4::Context->boolean_preference('insecure');
908 # finished authentification, now respond
909 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
913 $cookie = $query->cookie( CGISESSID => '' );
915 return ( $userid, $cookie, $sessionID, $flags );
920 # AUTH rejected, show the login/password template, after checking the DB.
924 # get the inputs from the incoming query
926 foreach my $name ( param $query) {
927 (next) if ( $name eq 'userid' || $name eq 'password' || $name eq 'ticket' );
928 my $value = $query->param($name);
929 push @inputs, { name => $name, value => $value };
931 # get the branchloop, which we need for authentication
932 my $branches = GetBranches();
934 for my $branch_hash (sort keys %$branches) {
935 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
938 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
939 my $template = C4::Templates::gettemplate( $template_name, $type, $query );
940 $template->param(branchloop => \@branch_loop,);
941 my $checkstyle = C4::Context->preference("opaccolorstylesheet");
942 if ($checkstyle =~ /\//)
944 $template->param( opacexternalsheet => $checkstyle);
947 my $opaccolorstylesheet = C4::Context->preference("opaccolorstylesheet");
948 $template->param( opaccolorstylesheet => $opaccolorstylesheet);
953 casAuthentication => C4::Context->preference("casAuthentication"),
954 suggestion => C4::Context->preference("suggestion"),
955 virtualshelves => C4::Context->preference("virtualshelves"),
956 LibraryName => C4::Context->preference("LibraryName"),
957 opacuserlogin => C4::Context->preference("opacuserlogin"),
958 OpacNav => C4::Context->preference("OpacNav"),
959 opaccredits => C4::Context->preference("opaccredits"),
960 OpacFavicon => C4::Context->preference("OpacFavicon"),
961 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
962 opacsmallimage => C4::Context->preference("opacsmallimage"),
963 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
964 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
965 opacuserjs => C4::Context->preference("opacuserjs"),
966 opacbookbag => "" . C4::Context->preference("opacbookbag"),
967 OpacCloud => C4::Context->preference("OpacCloud"),
968 OpacTopissue => C4::Context->preference("OpacTopissue"),
969 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
970 OpacBrowser => C4::Context->preference("OpacBrowser"),
971 opacheader => C4::Context->preference("opacheader"),
972 TagsEnabled => C4::Context->preference("TagsEnabled"),
973 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
974 opacstylesheet => C4::Context->preference("opacstylesheet"),
975 intranetcolorstylesheet =>
976 C4::Context->preference("intranetcolorstylesheet"),
977 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
978 intranetbookbag => C4::Context->preference("intranetbookbag"),
979 IntranetNav => C4::Context->preference("IntranetNav"),
980 intranetuserjs => C4::Context->preference("intranetuserjs"),
981 IndependantBranches=> C4::Context->preference("IndependantBranches"),
982 AutoLocation => C4::Context->preference("AutoLocation"),
983 wrongip => $info{'wrongip'},
986 $template->param( OpacPublic => C4::Context->preference("OpacPublic"));
987 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
991 casServerUrl => login_cas_url(),
992 invalidCasLogin => $info{'invalidCasLogin'}
996 my $self_url = $query->url( -absolute => 1 );
999 LibraryName => C4::Context->preference("LibraryName"),
1001 $template->param( %info );
1002 # $cookie = $query->cookie(CGISESSID => $session->id
1004 print $query->header(
1005 -type => 'text/html',
1006 -charset => 'utf-8',
1013 =head2 check_api_auth
1015 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
1017 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
1018 cookie, determine if the user has the privileges specified by C<$userflags>.
1020 C<check_api_auth> is is meant for authenticating users of web services, and
1021 consequently will always return and will not attempt to redirect the user
1024 If a valid session cookie is already present, check_api_auth will return a status
1025 of "ok", the cookie, and the Koha session ID.
1027 If no session cookie is present, check_api_auth will check the 'userid' and 'password
1028 parameters and create a session cookie and Koha session if the supplied credentials
1031 Possible return values in C<$status> are:
1035 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
1037 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
1039 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1041 =item "expired -- session cookie has expired; API user should resubmit userid and password
1047 sub check_api_auth {
1049 my $flagsrequired = shift;
1051 my $dbh = C4::Context->dbh;
1052 my $timeout = C4::Context->preference('timeout');
1053 $timeout = 600 unless $timeout;
1055 unless (C4::Context->preference('Version')) {
1056 # database has not been installed yet
1057 return ("maintenance", undef, undef);
1059 my $kohaversion=C4::Context::KOHAVERSION;
1060 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1061 if (C4::Context->preference('Version') < $kohaversion) {
1062 # database in need of version update; assume that
1063 # no API should be called while databsae is in
1065 return ("maintenance", undef, undef);
1068 # FIXME -- most of what follows is a copy-and-paste
1069 # of code from checkauth. There is an obvious need
1070 # for refactoring to separate the various parts of
1071 # the authentication code, but as of 2007-11-19 this
1072 # is deferred so as to not introduce bugs into the
1073 # regular authentication code for Koha 3.0.
1075 # see if we have a valid session cookie already
1076 # however, if a userid parameter is present (i.e., from
1077 # a form submission, assume that any current cookie
1079 my $sessionID = undef;
1080 unless ($query->param('userid')) {
1081 $sessionID = $query->cookie("CGISESSID");
1084 my $session = get_session($sessionID);
1085 C4::Context->_new_userenv($sessionID);
1087 C4::Context::set_userenv(
1088 $session->param('number'), $session->param('id'),
1089 $session->param('cardnumber'), $session->param('firstname'),
1090 $session->param('surname'), $session->param('branch'),
1091 $session->param('branchname'), $session->param('flags'),
1092 $session->param('emailaddress'), $session->param('branchprinter')
1095 my $ip = $session->param('ip');
1096 my $lasttime = $session->param('lasttime');
1097 my $userid = $session->param('id');
1098 if ( $lasttime < time() - $timeout ) {
1101 C4::Context->_unset_userenv($sessionID);
1104 return ("expired", undef, undef);
1105 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1106 # IP address changed
1108 C4::Context->_unset_userenv($sessionID);
1111 return ("expired", undef, undef);
1113 my $cookie = $query->cookie( CGISESSID => $session->id );
1114 $session->param('lasttime',time());
1115 my $flags = haspermission($userid, $flagsrequired);
1117 return ("ok", $cookie, $sessionID);
1120 C4::Context->_unset_userenv($sessionID);
1123 return ("failed", undef, undef);
1127 return ("expired", undef, undef);
1131 my $userid = $query->param('userid');
1132 my $password = $query->param('password');
1133 unless ($userid and $password) {
1134 # caller did something wrong, fail the authenticateion
1135 return ("failed", undef, undef);
1137 my ($return, $cardnumber);
1138 if ($cas && $query->param('ticket')) {
1140 ( $return, $cardnumber, $retuserid ) = checkpw( $dbh, $userid, $password, $query );
1141 $userid = $retuserid;
1143 ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password, $query );
1145 if ($return and haspermission( $userid, $flagsrequired)) {
1146 my $session = get_session("");
1147 return ("failed", undef, undef) unless $session;
1149 my $sessionID = $session->id;
1150 C4::Context->_new_userenv($sessionID);
1151 my $cookie = $query->cookie(CGISESSID => $sessionID);
1152 if ( $return == 1 ) {
1154 $borrowernumber, $firstname, $surname,
1155 $userflags, $branchcode, $branchname,
1156 $branchprinter, $emailaddress
1160 "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=?"
1162 $sth->execute($userid);
1164 $borrowernumber, $firstname, $surname,
1165 $userflags, $branchcode, $branchname,
1166 $branchprinter, $emailaddress
1167 ) = $sth->fetchrow if ( $sth->rows );
1169 unless ($sth->rows ) {
1170 my $sth = $dbh->prepare(
1171 "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=?"
1173 $sth->execute($cardnumber);
1175 $borrowernumber, $firstname, $surname,
1176 $userflags, $branchcode, $branchname,
1177 $branchprinter, $emailaddress
1178 ) = $sth->fetchrow if ( $sth->rows );
1180 unless ( $sth->rows ) {
1181 $sth->execute($userid);
1183 $borrowernumber, $firstname, $surname, $userflags,
1184 $branchcode, $branchname, $branchprinter, $emailaddress
1185 ) = $sth->fetchrow if ( $sth->rows );
1189 my $ip = $ENV{'REMOTE_ADDR'};
1190 # if they specify at login, use that
1191 if ($query->param('branch')) {
1192 $branchcode = $query->param('branch');
1193 $branchname = GetBranchName($branchcode);
1195 my $branches = GetBranches();
1197 foreach my $br ( keys %$branches ) {
1198 # now we work with the treatment of ip
1199 my $domain = $branches->{$br}->{'branchip'};
1200 if ( $domain && $ip =~ /^$domain/ ) {
1201 $branchcode = $branches->{$br}->{'branchcode'};
1203 # new op dev : add the branchprinter and branchname in the cookie
1204 $branchprinter = $branches->{$br}->{'branchprinter'};
1205 $branchname = $branches->{$br}->{'branchname'};
1208 $session->param('number',$borrowernumber);
1209 $session->param('id',$userid);
1210 $session->param('cardnumber',$cardnumber);
1211 $session->param('firstname',$firstname);
1212 $session->param('surname',$surname);
1213 $session->param('branch',$branchcode);
1214 $session->param('branchname',$branchname);
1215 $session->param('flags',$userflags);
1216 $session->param('emailaddress',$emailaddress);
1217 $session->param('ip',$session->remote_addr());
1218 $session->param('lasttime',time());
1219 } elsif ( $return == 2 ) {
1220 #We suppose the user is the superlibrarian
1221 $session->param('number',0);
1222 $session->param('id',C4::Context->config('user'));
1223 $session->param('cardnumber',C4::Context->config('user'));
1224 $session->param('firstname',C4::Context->config('user'));
1225 $session->param('surname',C4::Context->config('user'));
1226 $session->param('branch','NO_LIBRARY_SET');
1227 $session->param('branchname','NO_LIBRARY_SET');
1228 $session->param('flags',1);
1229 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1230 $session->param('ip',$session->remote_addr());
1231 $session->param('lasttime',time());
1233 C4::Context::set_userenv(
1234 $session->param('number'), $session->param('id'),
1235 $session->param('cardnumber'), $session->param('firstname'),
1236 $session->param('surname'), $session->param('branch'),
1237 $session->param('branchname'), $session->param('flags'),
1238 $session->param('emailaddress'), $session->param('branchprinter')
1240 return ("ok", $cookie, $sessionID);
1242 return ("failed", undef, undef);
1247 =head2 check_cookie_auth
1249 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1251 Given a CGISESSID cookie set during a previous login to Koha, determine
1252 if the user has the privileges specified by C<$userflags>.
1254 C<check_cookie_auth> is meant for authenticating special services
1255 such as tools/upload-file.pl that are invoked by other pages that
1256 have been authenticated in the usual way.
1258 Possible return values in C<$status> are:
1262 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1264 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1266 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1268 =item "expired -- session cookie has expired; API user should resubmit userid and password
1274 sub check_cookie_auth {
1276 my $flagsrequired = shift;
1278 my $dbh = C4::Context->dbh;
1279 my $timeout = C4::Context->preference('timeout');
1280 $timeout = 600 unless $timeout;
1282 unless (C4::Context->preference('Version')) {
1283 # database has not been installed yet
1284 return ("maintenance", undef);
1286 my $kohaversion=C4::Context::KOHAVERSION;
1287 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1288 if (C4::Context->preference('Version') < $kohaversion) {
1289 # database in need of version update; assume that
1290 # no API should be called while databsae is in
1292 return ("maintenance", undef);
1295 # FIXME -- most of what follows is a copy-and-paste
1296 # of code from checkauth. There is an obvious need
1297 # for refactoring to separate the various parts of
1298 # the authentication code, but as of 2007-11-23 this
1299 # is deferred so as to not introduce bugs into the
1300 # regular authentication code for Koha 3.0.
1302 # see if we have a valid session cookie already
1303 # however, if a userid parameter is present (i.e., from
1304 # a form submission, assume that any current cookie
1306 unless (defined $cookie and $cookie) {
1307 return ("failed", undef);
1309 my $sessionID = $cookie;
1310 my $session = get_session($sessionID);
1311 C4::Context->_new_userenv($sessionID);
1313 C4::Context::set_userenv(
1314 $session->param('number'), $session->param('id'),
1315 $session->param('cardnumber'), $session->param('firstname'),
1316 $session->param('surname'), $session->param('branch'),
1317 $session->param('branchname'), $session->param('flags'),
1318 $session->param('emailaddress'), $session->param('branchprinter')
1321 my $ip = $session->param('ip');
1322 my $lasttime = $session->param('lasttime');
1323 my $userid = $session->param('id');
1324 if ( $lasttime < time() - $timeout ) {
1327 C4::Context->_unset_userenv($sessionID);
1330 return ("expired", undef);
1331 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1332 # IP address changed
1334 C4::Context->_unset_userenv($sessionID);
1337 return ("expired", undef);
1339 $session->param('lasttime',time());
1340 my $flags = haspermission($userid, $flagsrequired);
1342 return ("ok", $sessionID);
1345 C4::Context->_unset_userenv($sessionID);
1348 return ("failed", undef);
1352 return ("expired", undef);
1359 my $session = get_session($sessionID);
1361 Given a session ID, retrieve the CGI::Session object used to store
1362 the session's state. The session object can be used to store
1363 data that needs to be accessed by different scripts during a
1366 If the C<$sessionID> parameter is an empty string, a new session
1372 my $sessionID = shift;
1373 my $storage_method = C4::Context->preference('SessionStorage');
1374 my $dbh = C4::Context->dbh;
1376 if ($storage_method eq 'mysql'){
1377 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1379 elsif ($storage_method eq 'Pg') {
1380 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1382 elsif ($storage_method eq 'memcached' && $servers){
1383 $session = new CGI::Session("driver:memcached;serializer:yaml;id:md5", $sessionID, { Memcached => $memcached } );
1386 # catch all defaults to tmp should work on all systems
1387 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1394 my ( $dbh, $userid, $password, $query ) = @_;
1396 $debug and print "## checkpw - checking LDAP\n";
1397 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1398 ($retval) and return ($retval,$retcard);
1401 if ($cas && $query->param('ticket')) {
1402 $debug and print STDERR "## checkpw - checking CAS\n";
1403 # In case of a CAS authentication, we use the ticket instead of the password
1404 my $ticket = $query->param('ticket');
1405 my ($retval,$retcard,$retuserid) = checkpw_cas($dbh, $ticket, $query); # EXTERNAL AUTH
1406 ($retval) and return ($retval,$retcard,$retuserid);
1413 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1415 $sth->execute($userid);
1417 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1418 $surname, $branchcode, $flags )
1420 if ( md5_base64($password) eq $md5password and $md5password ne "!") {
1422 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1423 $firstname, $surname, $branchcode, $flags );
1429 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1431 $sth->execute($userid);
1433 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1434 $surname, $branchcode, $flags )
1436 if ( md5_base64($password) eq $md5password ) {
1438 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1439 $firstname, $surname, $branchcode, $flags );
1443 if ( $userid && $userid eq C4::Context->config('user')
1444 && "$password" eq C4::Context->config('pass') )
1447 # Koha superuser account
1448 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1451 if ( $userid && $userid eq 'demo'
1452 && "$password" eq 'demo'
1453 && C4::Context->config('demo') )
1456 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1457 # some features won't be effective : modify systempref, modify MARC structure,
1465 my $authflags = getuserflags($flags, $userid, [$dbh]);
1467 Translates integer flags into permissions strings hash.
1469 C<$flags> is the integer userflags value ( borrowers.userflags )
1470 C<$userid> is the members.userid, used for building subpermissions
1471 C<$authflags> is a hashref of permissions
1478 my $dbh = @_ ? shift : C4::Context->dbh;
1480 $flags = 0 unless $flags;
1481 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1484 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1485 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1486 $userflags->{$flag} = 1;
1489 $userflags->{$flag} = 0;
1493 # get subpermissions and merge with top-level permissions
1494 my $user_subperms = get_user_subpermissions($userid);
1495 foreach my $module (keys %$user_subperms) {
1496 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1497 $userflags->{$module} = $user_subperms->{$module};
1503 =head2 get_user_subpermissions
1505 $user_perm_hashref = get_user_subpermissions($userid);
1507 Given the userid (note, not the borrowernumber) of a staff user,
1508 return a hashref of hashrefs of the specific subpermissions
1509 accorded to the user. An example return is
1513 export_catalog => 1,
1514 import_patrons => 1,
1518 The top-level hash-key is a module or function code from
1519 userflags.flag, while the second-level key is a code
1522 The results of this function do not give a complete picture
1523 of the functions that a staff user can access; it is also
1524 necessary to check borrowers.flags.
1528 sub get_user_subpermissions {
1531 my $dbh = C4::Context->dbh;
1532 my $sth = $dbh->prepare("SELECT flag, user_permissions.code
1533 FROM user_permissions
1534 JOIN permissions USING (module_bit, code)
1535 JOIN userflags ON (module_bit = bit)
1536 JOIN borrowers USING (borrowernumber)
1538 $sth->execute($userid);
1540 my $user_perms = {};
1541 while (my $perm = $sth->fetchrow_hashref) {
1542 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1547 =head2 get_all_subpermissions
1549 my $perm_hashref = get_all_subpermissions();
1551 Returns a hashref of hashrefs defining all specific
1552 permissions currently defined. The return value
1553 has the same structure as that of C<get_user_subpermissions>,
1554 except that the innermost hash value is the description
1555 of the subpermission.
1559 sub get_all_subpermissions {
1560 my $dbh = C4::Context->dbh;
1561 my $sth = $dbh->prepare("SELECT flag, code, description
1563 JOIN userflags ON (module_bit = bit)");
1567 while (my $perm = $sth->fetchrow_hashref) {
1568 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1573 =head2 haspermission
1575 $flags = ($userid, $flagsrequired);
1577 C<$userid> the userid of the member
1578 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1580 Returns member's flags or 0 if a permission is not met.
1585 my ($userid, $flagsrequired) = @_;
1586 my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1587 $sth->execute($userid);
1588 my $flags = getuserflags($sth->fetchrow(), $userid);
1589 if ( $userid eq C4::Context->config('user') ) {
1590 # Super User Account from /etc/koha.conf
1591 $flags->{'superlibrarian'} = 1;
1593 elsif ( $userid eq 'demo' && C4::Context->config('demo') ) {
1594 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1595 $flags->{'superlibrarian'} = 1;
1598 return $flags if $flags->{superlibrarian};
1600 foreach my $module ( keys %$flagsrequired ) {
1601 my $subperm = $flagsrequired->{$module};
1602 if ($subperm eq '*') {
1603 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1605 return 0 unless ( $flags->{$module} == 1 or
1606 ( ref($flags->{$module}) and
1607 exists $flags->{$module}->{$subperm} and
1608 $flags->{$module}->{$subperm} == 1
1614 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1618 sub getborrowernumber {
1620 my $userenv = C4::Context->userenv;
1621 if ( defined( $userenv ) && ref( $userenv ) eq 'HASH' && $userenv->{number} ) {
1622 return $userenv->{number};
1624 my $dbh = C4::Context->dbh;
1625 for my $field ( 'userid', 'cardnumber' ) {
1627 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1628 $sth->execute($userid);
1630 my ($bnumber) = $sth->fetchrow;
1637 END { } # module clean-up code here (global destructor)