Bug 12539: PROG/CCSR deprecation: Remove hardcoded theme from C4/Templates.pm
[koha.git] / C4 / Auth.pm
blobc6dda85f69759bc44118d48272759ba481ef48f4
1 package C4::Auth;
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
10 # version.
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.
20 use strict;
21 use warnings;
22 use Digest::MD5 qw(md5_base64);
23 use JSON qw/encode_json/;
24 use URI::Escape;
25 use CGI::Session;
27 require Exporter;
28 use C4::Context;
29 use C4::Templates; # to get the template
30 use C4::Languages;
31 use C4::Branch; # GetBranches
32 use C4::Search::History;
33 use C4::VirtualShelves;
34 use Koha::AuthUtils qw(hash_password);
35 use POSIX qw/strftime/;
36 use List::MoreUtils qw/ any /;
38 # use utf8;
39 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap $cas $caslogout $shib $shib_login);
41 BEGIN {
42 sub psgi_env { any { /^psgi\./ } keys %ENV }
43 sub safe_exit {
44 if ( psgi_env ) { die 'psgi:exit' }
45 else { exit }
47 $VERSION = 3.07.00.049; # set version for version checking
49 $debug = $ENV{DEBUG};
50 @ISA = qw(Exporter);
51 @EXPORT = qw(&checkauth &get_template_and_user &haspermission &get_user_subpermissions);
52 @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &checkpw_internal &checkpw_hash
53 &get_all_subpermissions &get_user_subpermissions
55 %EXPORT_TAGS = ( EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)] );
56 $ldap = C4::Context->config('useldapserver') || 0;
57 $cas = C4::Context->preference('casAuthentication');
58 $shib = C4::Context->config('useshibboleth') || 0;
59 $caslogout = C4::Context->preference('casLogout');
60 require C4::Auth_with_cas; # no import
61 if ($ldap) {
62 require C4::Auth_with_ldap;
63 import C4::Auth_with_ldap qw(checkpw_ldap);
65 if ($shib) {
66 require C4::Auth_with_shibboleth;
67 import C4::Auth_with_shibboleth
68 qw(shib_ok checkpw_shib logout_shib login_shib_url get_login_shib);
70 # Check for good config
71 if ( shib_ok() ) {
72 # Get shibboleth login attribute
73 $shib_login = get_login_shib();
75 # Bad config, disable shibboleth
76 else {
77 $shib = 0;
80 if ($cas) {
81 import C4::Auth_with_cas qw(check_api_auth_cas checkpw_cas login_cas logout_cas login_cas_url);
86 =head1 NAME
88 C4::Auth - Authenticates Koha users
90 =head1 SYNOPSIS
92 use CGI;
93 use C4::Auth;
94 use C4::Output;
96 my $query = new CGI;
98 my ($template, $borrowernumber, $cookie)
99 = get_template_and_user(
101 template_name => "opac-main.tt",
102 query => $query,
103 type => "opac",
104 authnotrequired => 0,
105 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
109 output_html_with_http_headers $query, $cookie, $template->output;
111 =head1 DESCRIPTION
113 The main function of this module is to provide
114 authentification. However the get_template_and_user function has
115 been provided so that a users login information is passed along
116 automatically. This gets loaded into the template.
118 =head1 FUNCTIONS
120 =head2 get_template_and_user
122 my ($template, $borrowernumber, $cookie)
123 = get_template_and_user(
125 template_name => "opac-main.tt",
126 query => $query,
127 type => "opac",
128 authnotrequired => 0,
129 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
133 This call passes the C<query>, C<flagsrequired> and C<authnotrequired>
134 to C<&checkauth> (in this module) to perform authentification.
135 See C<&checkauth> for an explanation of these parameters.
137 The C<template_name> is then used to find the correct template for
138 the page. The authenticated users details are loaded onto the
139 template in the HTML::Template LOOP variable C<USER_INFO>. Also the
140 C<sessionID> is passed to the template. This can be used in templates
141 if cookies are disabled. It needs to be put as and input to every
142 authenticated page.
144 More information on the C<gettemplate> sub can be found in the
145 Output.pm module.
147 =cut
149 sub get_template_and_user {
151 my $in = shift;
152 my ( $user, $cookie, $sessionID, $flags );
154 C4::Context->interface($in->{type});
156 $in->{'authnotrequired'} ||= 0;
157 my $template = C4::Templates::gettemplate(
158 $in->{'template_name'},
159 $in->{'type'},
160 $in->{'query'},
161 $in->{'is_plugin'}
164 if ( $in->{'template_name'} !~m/maintenance/ ) {
165 ( $user, $cookie, $sessionID, $flags ) = checkauth(
166 $in->{'query'},
167 $in->{'authnotrequired'},
168 $in->{'flagsrequired'},
169 $in->{'type'}
173 my $borrowernumber;
174 if ($user) {
175 require C4::Members;
176 # It's possible for $user to be the borrowernumber if they don't have a
177 # userid defined (and are logging in through some other method, such
178 # as SSL certs against an email address)
179 $borrowernumber = getborrowernumber($user) if defined($user);
180 if (!defined($borrowernumber) && defined($user)) {
181 my $borrower = C4::Members::GetMember(borrowernumber => $user);
182 if ($borrower) {
183 $borrowernumber = $user;
184 # A bit of a hack, but I don't know there's a nicer way
185 # to do it.
186 $user = $borrower->{firstname} . ' ' . $borrower->{surname};
190 # user info
191 $template->param( loggedinusername => $user );
192 $template->param( sessionID => $sessionID );
194 my ($total, $pubshelves, $barshelves) = C4::VirtualShelves::GetSomeShelfNames($borrowernumber, 'MASTHEAD');
195 $template->param(
196 pubshelves => $total->{pubtotal},
197 pubshelvesloop => $pubshelves,
198 barshelves => $total->{bartotal},
199 barshelvesloop => $barshelves,
202 my ( $borr ) = C4::Members::GetMemberDetails( $borrowernumber );
203 my @bordat;
204 $bordat[0] = $borr;
205 $template->param( "USER_INFO" => \@bordat );
207 my $all_perms = get_all_subpermissions();
209 my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow
210 editcatalogue updatecharges management tools editauthorities serials reports acquisition);
211 # We are going to use the $flags returned by checkauth
212 # to create the template's parameters that will indicate
213 # which menus the user can access.
214 if ( $flags && $flags->{superlibrarian}==1 ) {
215 $template->param( CAN_user_circulate => 1 );
216 $template->param( CAN_user_catalogue => 1 );
217 $template->param( CAN_user_parameters => 1 );
218 $template->param( CAN_user_borrowers => 1 );
219 $template->param( CAN_user_permissions => 1 );
220 $template->param( CAN_user_reserveforothers => 1 );
221 $template->param( CAN_user_borrow => 1 );
222 $template->param( CAN_user_editcatalogue => 1 );
223 $template->param( CAN_user_updatecharges => 1 );
224 $template->param( CAN_user_acquisition => 1 );
225 $template->param( CAN_user_management => 1 );
226 $template->param( CAN_user_tools => 1 );
227 $template->param( CAN_user_editauthorities => 1 );
228 $template->param( CAN_user_serials => 1 );
229 $template->param( CAN_user_reports => 1 );
230 $template->param( CAN_user_staffaccess => 1 );
231 $template->param( CAN_user_plugins => 1 );
232 $template->param( CAN_user_coursereserves => 1 );
233 foreach my $module (keys %$all_perms) {
234 foreach my $subperm (keys %{ $all_perms->{$module} }) {
235 $template->param( "CAN_user_${module}_${subperm}" => 1 );
240 if ( $flags ) {
241 foreach my $module (keys %$all_perms) {
242 if ( $flags->{$module} == 1) {
243 foreach my $subperm (keys %{ $all_perms->{$module} }) {
244 $template->param( "CAN_user_${module}_${subperm}" => 1 );
246 } elsif ( ref($flags->{$module}) ) {
247 foreach my $subperm (keys %{ $flags->{$module} } ) {
248 $template->param( "CAN_user_${module}_${subperm}" => 1 );
254 if ($flags) {
255 foreach my $module (keys %$flags) {
256 if ( $flags->{$module} == 1 or ref($flags->{$module}) ) {
257 $template->param( "CAN_user_$module" => 1 );
258 if ($module eq "parameters") {
259 $template->param( CAN_user_management => 1 );
264 # Logged-in opac search history
265 # If the requested template is an opac one and opac search history is enabled
266 if ($in->{type} eq 'opac' && C4::Context->preference('EnableOpacSearchHistory')) {
267 my $dbh = C4::Context->dbh;
268 my $query = "SELECT COUNT(*) FROM search_history WHERE userid=?";
269 my $sth = $dbh->prepare($query);
270 $sth->execute($borrowernumber);
272 # If at least one search has already been performed
273 if ($sth->fetchrow_array > 0) {
274 # We show the link in opac
275 $template->param( EnableOpacSearchHistory => 1 );
278 # And if there are searches performed when the user was not logged in,
279 # we add them to the logged-in search history
280 my @recentSearches = C4::Search::History::get_from_session({ cgi => $in->{'query'} });
281 if (@recentSearches) {
282 my $dbh = C4::Context->dbh;
283 my $query = q{
284 INSERT INTO search_history(userid, sessionid, query_desc, query_cgi, type, total, time )
285 VALUES (?, ?, ?, ?, ?, ?, ?)
288 my $sth = $dbh->prepare($query);
289 $sth->execute( $borrowernumber,
290 $in->{query}->cookie("CGISESSID"),
291 $_->{query_desc},
292 $_->{query_cgi},
293 $_->{type} || 'biblio',
294 $_->{total},
295 $_->{time},
296 ) foreach @recentSearches;
298 # clear out the search history from the session now that
299 # we've saved it to the database
300 C4::Search::History::set_to_session({ cgi => $in->{'query'}, search_history => [] });
302 } elsif ( $in->{type} eq 'intranet' and C4::Context->preference('EnableSearchHistory') ) {
303 $template->param( EnableSearchHistory => 1 );
306 else { # if this is an anonymous session, setup to display public lists...
308 # If shibboleth is enabled, and we're in an anonymous session, we should allow
309 # the user to attemp login via shibboleth.
310 if ( $shib ) {
311 $template->param( shibbolethAuthentication => $shib,
312 shibbolethLoginUrl => login_shib_url($in->{'query'}),
314 # If shibboleth is enabled and we have a shibboleth login attribute,
315 # but we are in an anonymous session, then we clearly have an invalid
316 # shibboleth koha account.
317 if ( $shib_login ) {
318 $template->param( invalidShibLogin => '1');
322 $template->param( sessionID => $sessionID );
324 my ($total, $pubshelves) = C4::VirtualShelves::GetSomeShelfNames(undef, 'MASTHEAD');
325 $template->param(
326 pubshelves => $total->{pubtotal},
327 pubshelvesloop => $pubshelves,
330 # Anonymous opac search history
331 # If opac search history is enabled and at least one search has already been performed
332 if (C4::Context->preference('EnableOpacSearchHistory')) {
333 my @recentSearches = C4::Search::History::get_from_session({ cgi => $in->{'query'} });
334 if (@recentSearches) {
335 $template->param(EnableOpacSearchHistory => 1);
339 if(C4::Context->preference('dateformat')){
340 $template->param(dateformat => C4::Context->preference('dateformat'))
343 # these template parameters are set the same regardless of $in->{'type'}
345 # Set the using_https variable for templates
346 # FIXME Under Plack the CGI->https method always returns 'OFF'
347 my $https = $in->{query}->https();
348 my $using_https = (defined $https and $https ne 'OFF') ? 1 : 0;
350 $template->param(
351 "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
352 EnhancedMessagingPreferences => C4::Context->preference('EnhancedMessagingPreferences'),
353 GoogleJackets => C4::Context->preference("GoogleJackets"),
354 OpenLibraryCovers => C4::Context->preference("OpenLibraryCovers"),
355 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
356 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:undef),
357 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
358 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
359 emailaddress => C4::Context->userenv?C4::Context->userenv->{"emailaddress"}:undef,
360 loggedinpersona => C4::Context->userenv?C4::Context->userenv->{"persona"}:undef,
361 TagsEnabled => C4::Context->preference("TagsEnabled"),
362 hide_marc => C4::Context->preference("hide_marc"),
363 item_level_itypes => C4::Context->preference('item-level_itypes'),
364 patronimages => C4::Context->preference("patronimages"),
365 singleBranchMode => C4::Context->preference("singleBranchMode"),
366 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
367 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
368 using_https => $using_https,
369 noItemTypeImages => C4::Context->preference("noItemTypeImages"),
370 marcflavour => C4::Context->preference("marcflavour"),
371 persona => C4::Context->preference("persona"),
373 if ( $in->{'type'} eq "intranet" ) {
374 $template->param(
375 AmazonCoverImages => C4::Context->preference("AmazonCoverImages"),
376 AutoLocation => C4::Context->preference("AutoLocation"),
377 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
378 CalendarFirstDayOfWeek => (C4::Context->preference("CalendarFirstDayOfWeek") eq "Sunday")?0:1,
379 CircAutocompl => C4::Context->preference("CircAutocompl"),
380 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
381 IndependentBranches => C4::Context->preference("IndependentBranches"),
382 IntranetNav => C4::Context->preference("IntranetNav"),
383 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
384 LibraryName => C4::Context->preference("LibraryName"),
385 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:undef),
386 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
387 canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
388 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
389 IntranetFavicon => C4::Context->preference("IntranetFavicon"),
390 intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
391 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
392 IntranetUserCSS => C4::Context->preference("IntranetUserCSS"),
393 intranetuserjs => C4::Context->preference("intranetuserjs"),
394 intranetbookbag => C4::Context->preference("intranetbookbag"),
395 suggestion => C4::Context->preference("suggestion"),
396 virtualshelves => C4::Context->preference("virtualshelves"),
397 StaffSerialIssueDisplayCount => C4::Context->preference("StaffSerialIssueDisplayCount"),
398 EasyAnalyticalRecords => C4::Context->preference('EasyAnalyticalRecords'),
399 LocalCoverImages => C4::Context->preference('LocalCoverImages'),
400 OPACLocalCoverImages => C4::Context->preference('OPACLocalCoverImages'),
401 AllowMultipleCovers => C4::Context->preference('AllowMultipleCovers'),
402 EnableBorrowerFiles => C4::Context->preference('EnableBorrowerFiles'),
403 UseKohaPlugins => C4::Context->preference('UseKohaPlugins'),
404 UseCourseReserves => C4::Context->preference("UseCourseReserves"),
407 else {
408 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
409 #TODO : replace LibraryName syspref with 'system name', and remove this html processing
410 my $LibraryNameTitle = C4::Context->preference("LibraryName");
411 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
412 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
413 # clean up the busc param in the session if the page is not opac-detail and not the "add to list" page
414 if ( C4::Context->preference("OpacBrowseResults")
415 && $in->{'template_name'} =~ /opac-(.+)\.(?:tt|tmpl)$/ ) {
416 my $pagename = $1;
417 unless ( $pagename =~ /^(?:MARC|ISBD)?detail$/
418 or $pagename =~ /^addbybiblionumber$/ ) {
419 my $sessionSearch = get_session($sessionID || $in->{'query'}->cookie("CGISESSID"));
420 $sessionSearch->clear(["busc"]) if ($sessionSearch->param("busc"));
423 # variables passed from CGI: opac_css_override and opac_search_limits.
424 my $opac_search_limit = $ENV{'OPAC_SEARCH_LIMIT'};
425 my $opac_limit_override = $ENV{'OPAC_LIMIT_OVERRIDE'};
426 my $opac_name = '';
427 if (
428 ($opac_limit_override && $opac_search_limit && $opac_search_limit =~ /branch:(\w+)/) ||
429 ($in->{'query'}->param('limit') && $in->{'query'}->param('limit') =~ /branch:(\w+)/) ||
430 ($in->{'query'}->param('multibranchlimit') && $in->{'query'}->param('multibranchlimit') =~ /multibranchlimit-(\w+)/)
432 $opac_name = $1; # opac_search_limit is a branch, so we use it.
433 } elsif ( $in->{'query'}->param('multibranchlimit') ) {
434 $opac_name = $in->{'query'}->param('multibranchlimit');
435 } elsif (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv && C4::Context->userenv->{'branch'}) {
436 $opac_name = C4::Context->userenv->{'branch'};
438 # FIXME Under Plack the CGI->https method always returns 'OFF' ($using_https will be set to 0 in this case)
439 my $opac_base_url = C4::Context->preference("OPACBaseURL"); #FIXME uses $using_https below as well
440 if (!$opac_base_url){
441 $opac_base_url = $ENV{'SERVER_NAME'} . ($ENV{'SERVER_PORT'} eq ($using_https ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}");
443 $template->param(
444 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
445 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
446 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
447 BranchesLoop => GetBranchesLoop($opac_name),
448 BranchCategoriesLoop => GetBranchCategories( 'searchdomain', 1, $opac_name ),
449 CalendarFirstDayOfWeek => (C4::Context->preference("CalendarFirstDayOfWeek") eq "Sunday")?0:1,
450 LibraryName => "" . C4::Context->preference("LibraryName"),
451 LibraryNameTitle => "" . $LibraryNameTitle,
452 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
453 OPACAmazonCoverImages => C4::Context->preference("OPACAmazonCoverImages"),
454 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
455 OpacHighlightedWords => C4::Context->preference("OpacHighlightedWords"),
456 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
457 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
458 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
459 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
460 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
461 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
462 OPACBaseURL => ($using_https ? "https://" : "http://") . $opac_base_url,
463 opac_css_override => $ENV{'OPAC_CSS_OVERRIDE'},
464 opac_search_limit => $opac_search_limit,
465 opac_limit_override => $opac_limit_override,
466 OpacBrowser => C4::Context->preference("OpacBrowser"),
467 OpacCloud => C4::Context->preference("OpacCloud"),
468 OpacKohaUrl => C4::Context->preference("OpacKohaUrl"),
469 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
470 OpacMainUserBlockMobile => "" . C4::Context->preference("OpacMainUserBlockMobile"),
471 OpacNav => "" . C4::Context->preference("OpacNav"),
472 OpacNavRight => "" . C4::Context->preference("OpacNavRight"),
473 OpacNavBottom => "" . C4::Context->preference("OpacNavBottom"),
474 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
475 OPACPatronDetails => C4::Context->preference("OPACPatronDetails"),
476 OPACPrivacy => C4::Context->preference("OPACPrivacy"),
477 OPACFinesTab => C4::Context->preference("OPACFinesTab"),
478 OpacTopissue => C4::Context->preference("OpacTopissue"),
479 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
480 'Version' => C4::Context->preference('Version'),
481 hidelostitems => C4::Context->preference("hidelostitems"),
482 mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '',
483 opaclayoutstylesheet => "" . C4::Context->preference("opaclayoutstylesheet"),
484 opacbookbag => "" . C4::Context->preference("opacbookbag"),
485 opaccredits => "" . C4::Context->preference("opaccredits"),
486 OpacFavicon => C4::Context->preference("OpacFavicon"),
487 opacheader => "" . C4::Context->preference("opacheader"),
488 opaclanguagesdisplay => "" . C4::Context->preference("opaclanguagesdisplay"),
489 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
490 opacuserjs => C4::Context->preference("opacuserjs"),
491 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
492 ShowReviewer => C4::Context->preference("ShowReviewer"),
493 ShowReviewerPhoto => C4::Context->preference("ShowReviewerPhoto"),
494 suggestion => "" . C4::Context->preference("suggestion"),
495 virtualshelves => "" . C4::Context->preference("virtualshelves"),
496 OPACSerialIssueDisplayCount => C4::Context->preference("OPACSerialIssueDisplayCount"),
497 OPACXSLTDetailsDisplay => C4::Context->preference("OPACXSLTDetailsDisplay"),
498 OPACXSLTResultsDisplay => C4::Context->preference("OPACXSLTResultsDisplay"),
499 SyndeticsClientCode => C4::Context->preference("SyndeticsClientCode"),
500 SyndeticsEnabled => C4::Context->preference("SyndeticsEnabled"),
501 SyndeticsCoverImages => C4::Context->preference("SyndeticsCoverImages"),
502 SyndeticsTOC => C4::Context->preference("SyndeticsTOC"),
503 SyndeticsSummary => C4::Context->preference("SyndeticsSummary"),
504 SyndeticsEditions => C4::Context->preference("SyndeticsEditions"),
505 SyndeticsExcerpt => C4::Context->preference("SyndeticsExcerpt"),
506 SyndeticsReviews => C4::Context->preference("SyndeticsReviews"),
507 SyndeticsAuthorNotes => C4::Context->preference("SyndeticsAuthorNotes"),
508 SyndeticsAwards => C4::Context->preference("SyndeticsAwards"),
509 SyndeticsSeries => C4::Context->preference("SyndeticsSeries"),
510 SyndeticsCoverImageSize => C4::Context->preference("SyndeticsCoverImageSize"),
511 OPACLocalCoverImages => C4::Context->preference("OPACLocalCoverImages"),
512 PatronSelfRegistration => C4::Context->preference("PatronSelfRegistration"),
513 PatronSelfRegistrationDefaultCategory => C4::Context->preference("PatronSelfRegistrationDefaultCategory"),
516 $template->param(OpacPublic => '1') if ($user || C4::Context->preference("OpacPublic"));
519 # Check if we were asked using parameters to force a specific language
520 if ( defined $in->{'query'}->param('language') ) {
521 # Extract the language, let C4::Languages::getlanguage choose
522 # what to do
523 my $language = C4::Languages::getlanguage($in->{'query'});
524 my $languagecookie = C4::Templates::getlanguagecookie($in->{'query'},$language);
525 if ( ref $cookie eq 'ARRAY' ) {
526 push @{ $cookie }, $languagecookie;
527 } else {
528 $cookie = [$cookie, $languagecookie];
532 return ( $template, $borrowernumber, $cookie, $flags);
535 =head2 checkauth
537 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
539 Verifies that the user is authorized to run this script. If
540 the user is authorized, a (userid, cookie, session-id, flags)
541 quadruple is returned. If the user is not authorized but does
542 not have the required privilege (see $flagsrequired below), it
543 displays an error page and exits. Otherwise, it displays the
544 login page and exits.
546 Note that C<&checkauth> will return if and only if the user
547 is authorized, so it should be called early on, before any
548 unfinished operations (e.g., if you've opened a file, then
549 C<&checkauth> won't close it for you).
551 C<$query> is the CGI object for the script calling C<&checkauth>.
553 The C<$noauth> argument is optional. If it is set, then no
554 authorization is required for the script.
556 C<&checkauth> fetches user and session information from C<$query> and
557 ensures that the user is authorized to run scripts that require
558 authorization.
560 The C<$flagsrequired> argument specifies the required privileges
561 the user must have if the username and password are correct.
562 It should be specified as a reference-to-hash; keys in the hash
563 should be the "flags" for the user, as specified in the Members
564 intranet module. Any key specified must correspond to a "flag"
565 in the userflags table. E.g., { circulate => 1 } would specify
566 that the user must have the "circulate" privilege in order to
567 proceed. To make sure that access control is correct, the
568 C<$flagsrequired> parameter must be specified correctly.
570 Koha also has a concept of sub-permissions, also known as
571 granular permissions. This makes the value of each key
572 in the C<flagsrequired> hash take on an additional
573 meaning, i.e.,
577 The user must have access to all subfunctions of the module
578 specified by the hash key.
582 The user must have access to at least one subfunction of the module
583 specified by the hash key.
585 specific permission, e.g., 'export_catalog'
587 The user must have access to the specific subfunction list, which
588 must correspond to a row in the permissions table.
590 The C<$type> argument specifies whether the template should be
591 retrieved from the opac or intranet directory tree. "opac" is
592 assumed if it is not specified; however, if C<$type> is specified,
593 "intranet" is assumed if it is not "opac".
595 If C<$query> does not have a valid session ID associated with it
596 (i.e., the user has not logged in) or if the session has expired,
597 C<&checkauth> presents the user with a login page (from the point of
598 view of the original script, C<&checkauth> does not return). Once the
599 user has authenticated, C<&checkauth> restarts the original script
600 (this time, C<&checkauth> returns).
602 The login page is provided using a HTML::Template, which is set in the
603 systempreferences table or at the top of this file. The variable C<$type>
604 selects which template to use, either the opac or the intranet
605 authentification template.
607 C<&checkauth> returns a user ID, a cookie, and a session ID. The
608 cookie should be sent back to the browser; it verifies that the user
609 has authenticated.
611 =cut
613 sub _version_check {
614 my $type = shift;
615 my $query = shift;
616 my $version;
617 # If Version syspref is unavailable, it means Koha is beeing installed,
618 # and so we must redirect to OPAC maintenance page or to the WebInstaller
619 # also, if OpacMaintenance is ON, OPAC should redirect to maintenance
620 if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') {
621 warn "OPAC Install required, redirecting to maintenance";
622 print $query->redirect("/cgi-bin/koha/maintenance.pl");
623 safe_exit;
625 unless ( $version = C4::Context->preference('Version') ) { # assignment, not comparison
626 if ( $type ne 'opac' ) {
627 warn "Install required, redirecting to Installer";
628 print $query->redirect("/cgi-bin/koha/installer/install.pl");
629 } else {
630 warn "OPAC Install required, redirecting to maintenance";
631 print $query->redirect("/cgi-bin/koha/maintenance.pl");
633 safe_exit;
636 # check that database and koha version are the same
637 # there is no DB version, it's a fresh install,
638 # go to web installer
639 # there is a DB version, compare it to the code version
640 my $kohaversion=C4::Context::KOHAVERSION;
641 # remove the 3 last . to have a Perl number
642 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
643 $debug and print STDERR "kohaversion : $kohaversion\n";
644 if ($version < $kohaversion){
645 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
646 if ($type ne 'opac'){
647 warn sprintf($warning, 'Installer');
648 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
649 } else {
650 warn sprintf("OPAC: " . $warning, 'maintenance');
651 print $query->redirect("/cgi-bin/koha/maintenance.pl");
653 safe_exit;
657 sub _session_log {
658 (@_) or return 0;
659 open my $fh, '>>', "/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
660 printf $fh join("\n",@_);
661 close $fh;
664 sub _timeout_syspref {
665 my $timeout = C4::Context->preference('timeout') || 600;
666 # value in days, convert in seconds
667 if ($timeout =~ /(\d+)[dD]/) {
668 $timeout = $1 * 86400;
670 return $timeout;
673 sub checkauth {
674 my $query = shift;
675 $debug and warn "Checking Auth";
676 # $authnotrequired will be set for scripts which will run without authentication
677 my $authnotrequired = shift;
678 my $flagsrequired = shift;
679 my $type = shift;
680 my $persona = shift;
681 $type = 'opac' unless $type;
683 my $dbh = C4::Context->dbh;
684 my $timeout = _timeout_syspref();
686 _version_check($type,$query);
687 # state variables
688 my $loggedin = 0;
689 my %info;
690 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
691 my $logout = $query->param('logout.x');
693 my $anon_search_history;
695 # This parameter is the name of the CAS server we want to authenticate against,
696 # when using authentication against multiple CAS servers, as configured in Auth_cas_servers.yaml
697 my $casparam = $query->param('cas');
698 my $q_userid = $query->param('userid') // '';
700 # Basic authentication is incompatible with the use of Shibboleth,
701 # as Shibboleth may return REMOTE_USER as a Shibboleth attribute,
702 # and it may not be the attribute we want to use to match the koha login.
704 # Also, do not consider an empty REMOTE_USER.
706 # Finally, after those tests, we can assume (although if it would be better with
707 # a syspref) that if we get a REMOTE_USER, that's from basic authentication,
708 # and we can affect it to $userid.
709 if ( !$shib and defined($ENV{'REMOTE_USER'}) and $ENV{'REMOTE_USER'} ne '' and $userid = $ENV{'REMOTE_USER'} ) {
711 # Using Basic Authentication, no cookies required
712 $cookie = $query->cookie(
713 -name => 'CGISESSID',
714 -value => '',
715 -expires => '',
716 -HttpOnly => 1,
718 $loggedin = 1;
720 elsif ( $persona ){
721 # we dont want to set a session because we are being called by a persona callback
723 elsif ( $sessionID = $query->cookie("CGISESSID") )
724 { # assignment, not comparison
725 my $session = get_session($sessionID);
726 C4::Context->_new_userenv($sessionID);
727 my ($ip, $lasttime, $sessiontype);
728 my $s_userid = '';
729 if ($session){
730 $s_userid = $session->param('id') // '';
731 C4::Context::set_userenv(
732 $session->param('number'), $s_userid,
733 $session->param('cardnumber'), $session->param('firstname'),
734 $session->param('surname'), $session->param('branch'),
735 $session->param('branchname'), $session->param('flags'),
736 $session->param('emailaddress'), $session->param('branchprinter'),
737 $session->param('persona'), $session->param('shibboleth')
739 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
740 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
741 C4::Context::set_shelves_userenv('tot',$session->param('totshelves'));
742 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
743 $ip = $session->param('ip');
744 $lasttime = $session->param('lasttime');
745 $userid = $s_userid;
746 $sessiontype = $session->param('sessiontype') || '';
748 if ( ( $query->param('koha_login_context') && ($q_userid ne $s_userid) )
749 || ( $cas && $query->param('ticket') ) || ( $shib && $shib_login && !$logout ) ) {
750 #if a user enters an id ne to the id in the current session, we need to log them in...
751 #first we need to clear the anonymous session...
752 $debug and warn "query id = $q_userid but session id = $s_userid";
753 $anon_search_history = $session->param('search_history');
754 $session->delete();
755 $session->flush;
756 C4::Context->_unset_userenv($sessionID);
757 $sessionID = undef;
758 $userid = undef;
760 elsif ($logout) {
761 # voluntary logout the user
762 # check wether the user was using their shibboleth session or a local one
763 my $shibSuccess = C4::Context->userenv->{'shibboleth'};
764 $session->delete();
765 $session->flush;
766 C4::Context->_unset_userenv($sessionID);
767 #_session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,(strftime "%c",localtime));
768 $sessionID = undef;
769 $userid = undef;
771 if ($cas and $caslogout) {
772 logout_cas($query);
775 # If we are in a shibboleth session (shibboleth is enabled, a shibboleth match attribute is set and matches koha matchpoint)
776 if ( $shib and $shib_login and $shibSuccess and $type eq 'opac') {
777 # (Note: $type eq 'opac' condition should be removed when shibboleth authentication for intranet will be implemented)
778 logout_shib($query);
781 elsif ( !$lasttime || ($lasttime < time() - $timeout) ) {
782 # timed logout
783 $info{'timed_out'} = 1;
784 if ($session) {
785 $session->delete();
786 $session->flush;
788 C4::Context->_unset_userenv($sessionID);
789 #_session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,(strftime "%c",localtime));
790 $userid = undef;
791 $sessionID = undef;
793 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
794 # Different ip than originally logged in from
795 $info{'oldip'} = $ip;
796 $info{'newip'} = $ENV{'REMOTE_ADDR'};
797 $info{'different_ip'} = 1;
798 $session->delete();
799 $session->flush;
800 C4::Context->_unset_userenv($sessionID);
801 #_session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,(strftime "%c",localtime), $info{'newip'});
802 $sessionID = undef;
803 $userid = undef;
805 else {
806 $cookie = $query->cookie(
807 -name => 'CGISESSID',
808 -value => $session->id,
809 -HttpOnly => 1
811 $session->param( 'lasttime', time() );
812 unless ( $sessiontype && $sessiontype eq 'anon' ) { #if this is an anonymous session, we want to update the session, but not behave as if they are logged in...
813 $flags = haspermission($userid, $flagsrequired);
814 if ($flags) {
815 $loggedin = 1;
816 } else {
817 $info{'nopermission'} = 1;
822 unless ($userid || $sessionID) {
824 #we initiate a session prior to checking for a username to allow for anonymous sessions...
825 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
827 # Save anonymous search history in new session so it can be retrieved
828 # by get_template_and_user to store it in user's search history after
829 # a successful login.
830 if ($anon_search_history) {
831 $session->param('search_history', $anon_search_history);
834 my $sessionID = $session->id;
835 C4::Context->_new_userenv($sessionID);
836 $cookie = $query->cookie(
837 -name => 'CGISESSID',
838 -value => $session->id,
839 -HttpOnly => 1
841 $userid = $q_userid;
842 my $pki_field = C4::Context->preference('AllowPKIAuth');
843 if (! defined($pki_field) ) {
844 print STDERR "ERROR: Missing system preference AllowPKIAuth.\n";
845 $pki_field = 'None';
847 if ( ( $cas && $query->param('ticket') )
848 || $userid
849 || ( $shib && $shib_login )
850 || $pki_field ne 'None'
851 || $persona )
853 my $password = $query->param('password');
854 my $shibSuccess = 0;
856 my ( $return, $cardnumber );
857 # If shib is enabled and we have a shib login, does the login match a valid koha user
858 if ( $shib && $shib_login && $type eq 'opac' ) {
859 my $retuserid;
860 # Do not pass password here, else shib will not be checked in checkpw.
861 ( $return, $cardnumber, $retuserid ) = checkpw( $dbh, $userid, undef, $query );
862 $userid = $retuserid;
863 $shibSuccess = $return;
864 $info{'invalidShibLogin'} = 1 unless ($return);
866 # If shib login and match were successfull, skip further login methods
867 unless ( $shibSuccess ) {
868 if ( $cas && $query->param('ticket') ) {
869 my $retuserid;
870 ( $return, $cardnumber, $retuserid ) =
871 checkpw( $dbh, $userid, $password, $query );
872 $userid = $retuserid;
873 $info{'invalidCasLogin'} = 1 unless ($return);
876 elsif ($persona) {
877 my $value = $persona;
879 # If we're looking up the email, there's a chance that the person
880 # doesn't have a userid. So if there is none, we pass along the
881 # borrower number, and the bits of code that need to know the user
882 # ID will have to be smart enough to handle that.
883 require C4::Members;
884 my @users_info = C4::Members::GetBorrowersWithEmail($value);
885 if (@users_info) {
887 # First the userid, then the borrowernum
888 $value = $users_info[0][1] || $users_info[0][0];
890 else {
891 undef $value;
893 $return = $value ? 1 : 0;
894 $userid = $value;
897 elsif (
898 ( $pki_field eq 'Common Name' && $ENV{'SSL_CLIENT_S_DN_CN'} )
899 || ( $pki_field eq 'emailAddress'
900 && $ENV{'SSL_CLIENT_S_DN_Email'} )
903 my $value;
904 if ( $pki_field eq 'Common Name' ) {
905 $value = $ENV{'SSL_CLIENT_S_DN_CN'};
907 elsif ( $pki_field eq 'emailAddress' ) {
908 $value = $ENV{'SSL_CLIENT_S_DN_Email'};
910 # If we're looking up the email, there's a chance that the person
911 # doesn't have a userid. So if there is none, we pass along the
912 # borrower number, and the bits of code that need to know the user
913 # ID will have to be smart enough to handle that.
914 require C4::Members;
915 my @users_info = C4::Members::GetBorrowersWithEmail($value);
916 if (@users_info) {
918 # First the userid, then the borrowernum
919 $value = $users_info[0][1] || $users_info[0][0];
920 } else {
921 undef $value;
926 $return = $value ? 1 : 0;
927 $userid = $value;
930 else {
931 my $retuserid;
932 ( $return, $cardnumber, $retuserid ) =
933 checkpw( $dbh, $userid, $password, $query );
934 $userid = $retuserid if ( $retuserid );
935 $info{'invalid_username_or_password'} = 1 unless ($return);
937 if ($return) {
938 #_session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},(strftime '%c', localtime));
939 if ( $flags = haspermission( $userid, $flagsrequired ) ) {
940 $loggedin = 1;
942 else {
943 $info{'nopermission'} = 1;
944 C4::Context->_unset_userenv($sessionID);
946 my ($borrowernumber, $firstname, $surname, $userflags,
947 $branchcode, $branchname, $branchprinter, $emailaddress);
949 if ( $return == 1 ) {
950 my $select = "
951 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
952 branches.branchname as branchname,
953 branches.branchprinter as branchprinter,
954 email
955 FROM borrowers
956 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
958 my $sth = $dbh->prepare("$select where userid=?");
959 $sth->execute($userid);
960 unless ($sth->rows) {
961 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
962 $sth = $dbh->prepare("$select where cardnumber=?");
963 $sth->execute($cardnumber);
965 unless ($sth->rows) {
966 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
967 $sth->execute($userid);
968 unless ($sth->rows) {
969 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
973 if ($sth->rows) {
974 ($borrowernumber, $firstname, $surname, $userflags,
975 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
976 $debug and print STDERR "AUTH_3 results: " .
977 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
978 } else {
979 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
982 # launch a sequence to check if we have a ip for the branch, i
983 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
985 my $ip = $ENV{'REMOTE_ADDR'};
986 # if they specify at login, use that
987 if ($query->param('branch')) {
988 $branchcode = $query->param('branch');
989 $branchname = GetBranchName($branchcode);
991 my $branches = GetBranches();
992 if (C4::Context->boolean_preference('IndependentBranches') && C4::Context->boolean_preference('Autolocation')){
993 # we have to check they are coming from the right ip range
994 my $domain = $branches->{$branchcode}->{'branchip'};
995 if ($ip !~ /^$domain/){
996 $loggedin=0;
997 $info{'wrongip'} = 1;
1001 my @branchesloop;
1002 foreach my $br ( keys %$branches ) {
1003 # now we work with the treatment of ip
1004 my $domain = $branches->{$br}->{'branchip'};
1005 if ( $domain && $ip =~ /^$domain/ ) {
1006 $branchcode = $branches->{$br}->{'branchcode'};
1008 # new op dev : add the branchprinter and branchname in the cookie
1009 $branchprinter = $branches->{$br}->{'branchprinter'};
1010 $branchname = $branches->{$br}->{'branchname'};
1013 $session->param('number',$borrowernumber);
1014 $session->param('id',$userid);
1015 $session->param('cardnumber',$cardnumber);
1016 $session->param('firstname',$firstname);
1017 $session->param('surname',$surname);
1018 $session->param('branch',$branchcode);
1019 $session->param('branchname',$branchname);
1020 $session->param('flags',$userflags);
1021 $session->param('emailaddress',$emailaddress);
1022 $session->param('ip',$session->remote_addr());
1023 $session->param('lasttime',time());
1024 $session->param('shibboleth',$shibSuccess);
1025 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
1027 elsif ( $return == 2 ) {
1028 #We suppose the user is the superlibrarian
1029 $borrowernumber = 0;
1030 $session->param('number',0);
1031 $session->param('id',C4::Context->config('user'));
1032 $session->param('cardnumber',C4::Context->config('user'));
1033 $session->param('firstname',C4::Context->config('user'));
1034 $session->param('surname',C4::Context->config('user'));
1035 $session->param('branch','NO_LIBRARY_SET');
1036 $session->param('branchname','NO_LIBRARY_SET');
1037 $session->param('flags',1);
1038 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1039 $session->param('ip',$session->remote_addr());
1040 $session->param('lasttime',time());
1042 if ($persona){
1043 $session->param('persona',1);
1045 C4::Context::set_userenv(
1046 $session->param('number'), $session->param('id'),
1047 $session->param('cardnumber'), $session->param('firstname'),
1048 $session->param('surname'), $session->param('branch'),
1049 $session->param('branchname'), $session->param('flags'),
1050 $session->param('emailaddress'), $session->param('branchprinter'),
1051 $session->param('persona'), $session->param('shibboleth')
1055 else {
1056 if ($userid) {
1057 $info{'invalid_username_or_password'} = 1;
1058 C4::Context->_unset_userenv($sessionID);
1060 $session->param('lasttime',time());
1061 $session->param('ip',$session->remote_addr());
1063 } # END if ( $userid = $query->param('userid') )
1064 elsif ($type eq "opac") {
1065 # if we are here this is an anonymous session; add public lists to it and a few other items...
1066 # anonymous sessions are created only for the OPAC
1067 $debug and warn "Initiating an anonymous session...";
1069 # setting a couple of other session vars...
1070 $session->param('ip',$session->remote_addr());
1071 $session->param('lasttime',time());
1072 $session->param('sessiontype','anon');
1074 } # END unless ($userid)
1076 # finished authentification, now respond
1077 if ( $loggedin || $authnotrequired )
1079 # successful login
1080 unless ($cookie) {
1081 $cookie = $query->cookie(
1082 -name => 'CGISESSID',
1083 -value => '',
1084 -HttpOnly => 1
1087 return ( $userid, $cookie, $sessionID, $flags );
1092 # AUTH rejected, show the login/password template, after checking the DB.
1096 # get the inputs from the incoming query
1097 my @inputs = ();
1098 foreach my $name ( param $query) {
1099 (next) if ( $name eq 'userid' || $name eq 'password' || $name eq 'ticket' );
1100 my $value = $query->param($name);
1101 push @inputs, { name => $name, value => $value };
1104 my $LibraryNameTitle = C4::Context->preference("LibraryName");
1105 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
1106 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
1108 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tt' : 'auth.tt';
1109 my $template = C4::Templates::gettemplate($template_name, $type, $query );
1110 $template->param(
1111 branchloop => GetBranchesLoop(),
1112 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
1113 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
1114 login => 1,
1115 INPUTS => \@inputs,
1116 casAuthentication => C4::Context->preference("casAuthentication"),
1117 shibbolethAuthentication => $shib,
1118 suggestion => C4::Context->preference("suggestion"),
1119 virtualshelves => C4::Context->preference("virtualshelves"),
1120 LibraryName => "" . C4::Context->preference("LibraryName"),
1121 LibraryNameTitle => "" . $LibraryNameTitle,
1122 opacuserlogin => C4::Context->preference("opacuserlogin"),
1123 OpacNav => C4::Context->preference("OpacNav"),
1124 OpacNavRight => C4::Context->preference("OpacNavRight"),
1125 OpacNavBottom => C4::Context->preference("OpacNavBottom"),
1126 opaccredits => C4::Context->preference("opaccredits"),
1127 OpacFavicon => C4::Context->preference("OpacFavicon"),
1128 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
1129 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
1130 opacuserjs => C4::Context->preference("opacuserjs"),
1131 opacbookbag => "" . C4::Context->preference("opacbookbag"),
1132 OpacCloud => C4::Context->preference("OpacCloud"),
1133 OpacTopissue => C4::Context->preference("OpacTopissue"),
1134 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
1135 OpacBrowser => C4::Context->preference("OpacBrowser"),
1136 opacheader => C4::Context->preference("opacheader"),
1137 TagsEnabled => C4::Context->preference("TagsEnabled"),
1138 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
1139 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
1140 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
1141 intranetbookbag => C4::Context->preference("intranetbookbag"),
1142 IntranetNav => C4::Context->preference("IntranetNav"),
1143 IntranetFavicon => C4::Context->preference("IntranetFavicon"),
1144 intranetuserjs => C4::Context->preference("intranetuserjs"),
1145 IndependentBranches=> C4::Context->preference("IndependentBranches"),
1146 AutoLocation => C4::Context->preference("AutoLocation"),
1147 wrongip => $info{'wrongip'},
1148 PatronSelfRegistration => C4::Context->preference("PatronSelfRegistration"),
1149 PatronSelfRegistrationDefaultCategory => C4::Context->preference("PatronSelfRegistrationDefaultCategory"),
1150 persona => C4::Context->preference("Persona"),
1151 opac_css_override => $ENV{'OPAC_CSS_OVERRIDE'},
1154 $template->param( OpacPublic => C4::Context->preference("OpacPublic"));
1155 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
1157 if($type eq 'opac'){
1158 my ($total, $pubshelves) = C4::VirtualShelves::GetSomeShelfNames(undef, 'MASTHEAD');
1159 $template->param(
1160 pubshelves => $total->{pubtotal},
1161 pubshelvesloop => $pubshelves,
1165 if ($cas) {
1167 # Is authentication against multiple CAS servers enabled?
1168 if (C4::Auth_with_cas::multipleAuth && !$casparam) {
1169 my $casservers = C4::Auth_with_cas::getMultipleAuth();
1170 my @tmplservers;
1171 foreach my $key (keys %$casservers) {
1172 push @tmplservers, {name => $key, value => login_cas_url($query, $key) . "?cas=$key" };
1174 $template->param(
1175 casServersLoop => \@tmplservers
1177 } else {
1178 $template->param(
1179 casServerUrl => login_cas_url($query),
1183 $template->param(
1184 invalidCasLogin => $info{'invalidCasLogin'}
1188 if ($shib) {
1189 $template->param(
1190 shibbolethAuthentication => $shib,
1191 shibbolethLoginUrl => login_shib_url($query),
1195 my $self_url = $query->url( -absolute => 1 );
1196 $template->param(
1197 url => $self_url,
1198 LibraryName => C4::Context->preference("LibraryName"),
1200 $template->param( %info );
1201 # $cookie = $query->cookie(CGISESSID => $session->id
1202 # );
1203 print $query->header(
1204 -type => 'text/html',
1205 -charset => 'utf-8',
1206 -cookie => $cookie
1208 $template->output;
1209 safe_exit;
1212 =head2 check_api_auth
1214 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
1216 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
1217 cookie, determine if the user has the privileges specified by C<$userflags>.
1219 C<check_api_auth> is is meant for authenticating users of web services, and
1220 consequently will always return and will not attempt to redirect the user
1221 agent.
1223 If a valid session cookie is already present, check_api_auth will return a status
1224 of "ok", the cookie, and the Koha session ID.
1226 If no session cookie is present, check_api_auth will check the 'userid' and 'password
1227 parameters and create a session cookie and Koha session if the supplied credentials
1228 are OK.
1230 Possible return values in C<$status> are:
1232 =over
1234 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
1236 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
1238 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1240 =item "expired -- session cookie has expired; API user should resubmit userid and password
1242 =back
1244 =cut
1246 sub check_api_auth {
1247 my $query = shift;
1248 my $flagsrequired = shift;
1250 my $dbh = C4::Context->dbh;
1251 my $timeout = _timeout_syspref();
1253 unless (C4::Context->preference('Version')) {
1254 # database has not been installed yet
1255 return ("maintenance", undef, undef);
1257 my $kohaversion=C4::Context::KOHAVERSION;
1258 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1259 if (C4::Context->preference('Version') < $kohaversion) {
1260 # database in need of version update; assume that
1261 # no API should be called while databsae is in
1262 # this condition.
1263 return ("maintenance", undef, undef);
1266 # FIXME -- most of what follows is a copy-and-paste
1267 # of code from checkauth. There is an obvious need
1268 # for refactoring to separate the various parts of
1269 # the authentication code, but as of 2007-11-19 this
1270 # is deferred so as to not introduce bugs into the
1271 # regular authentication code for Koha 3.0.
1273 # see if we have a valid session cookie already
1274 # however, if a userid parameter is present (i.e., from
1275 # a form submission, assume that any current cookie
1276 # is to be ignored
1277 my $sessionID = undef;
1278 unless ($query->param('userid')) {
1279 $sessionID = $query->cookie("CGISESSID");
1281 if ($sessionID && not ($cas && $query->param('PT')) ) {
1282 my $session = get_session($sessionID);
1283 C4::Context->_new_userenv($sessionID);
1284 if ($session) {
1285 C4::Context::set_userenv(
1286 $session->param('number'), $session->param('id'),
1287 $session->param('cardnumber'), $session->param('firstname'),
1288 $session->param('surname'), $session->param('branch'),
1289 $session->param('branchname'), $session->param('flags'),
1290 $session->param('emailaddress'), $session->param('branchprinter')
1293 my $ip = $session->param('ip');
1294 my $lasttime = $session->param('lasttime');
1295 my $userid = $session->param('id');
1296 if ( $lasttime < time() - $timeout ) {
1297 # time out
1298 $session->delete();
1299 $session->flush;
1300 C4::Context->_unset_userenv($sessionID);
1301 $userid = undef;
1302 $sessionID = undef;
1303 return ("expired", undef, undef);
1304 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1305 # IP address changed
1306 $session->delete();
1307 $session->flush;
1308 C4::Context->_unset_userenv($sessionID);
1309 $userid = undef;
1310 $sessionID = undef;
1311 return ("expired", undef, undef);
1312 } else {
1313 my $cookie = $query->cookie(
1314 -name => 'CGISESSID',
1315 -value => $session->id,
1316 -HttpOnly => 1,
1318 $session->param('lasttime',time());
1319 my $flags = haspermission($userid, $flagsrequired);
1320 if ($flags) {
1321 return ("ok", $cookie, $sessionID);
1322 } else {
1323 $session->delete();
1324 $session->flush;
1325 C4::Context->_unset_userenv($sessionID);
1326 $userid = undef;
1327 $sessionID = undef;
1328 return ("failed", undef, undef);
1331 } else {
1332 return ("expired", undef, undef);
1334 } else {
1335 # new login
1336 my $userid = $query->param('userid');
1337 my $password = $query->param('password');
1338 my ($return, $cardnumber);
1340 # Proxy CAS auth
1341 if ($cas && $query->param('PT')) {
1342 my $retuserid;
1343 $debug and print STDERR "## check_api_auth - checking CAS\n";
1344 # In case of a CAS authentication, we use the ticket instead of the password
1345 my $PT = $query->param('PT');
1346 ($return,$cardnumber,$userid) = check_api_auth_cas($dbh, $PT, $query); # EXTERNAL AUTH
1347 } else {
1348 # User / password auth
1349 unless ($userid and $password) {
1350 # caller did something wrong, fail the authenticateion
1351 return ("failed", undef, undef);
1353 ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password, $query );
1356 if ($return and haspermission( $userid, $flagsrequired)) {
1357 my $session = get_session("");
1358 return ("failed", undef, undef) unless $session;
1360 my $sessionID = $session->id;
1361 C4::Context->_new_userenv($sessionID);
1362 my $cookie = $query->cookie(
1363 -name => 'CGISESSID',
1364 -value => $sessionID,
1365 -HttpOnly => 1,
1367 if ( $return == 1 ) {
1368 my (
1369 $borrowernumber, $firstname, $surname,
1370 $userflags, $branchcode, $branchname,
1371 $branchprinter, $emailaddress
1373 my $sth =
1374 $dbh->prepare(
1375 "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=?"
1377 $sth->execute($userid);
1379 $borrowernumber, $firstname, $surname,
1380 $userflags, $branchcode, $branchname,
1381 $branchprinter, $emailaddress
1382 ) = $sth->fetchrow if ( $sth->rows );
1384 unless ($sth->rows ) {
1385 my $sth = $dbh->prepare(
1386 "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=?"
1388 $sth->execute($cardnumber);
1390 $borrowernumber, $firstname, $surname,
1391 $userflags, $branchcode, $branchname,
1392 $branchprinter, $emailaddress
1393 ) = $sth->fetchrow if ( $sth->rows );
1395 unless ( $sth->rows ) {
1396 $sth->execute($userid);
1398 $borrowernumber, $firstname, $surname, $userflags,
1399 $branchcode, $branchname, $branchprinter, $emailaddress
1400 ) = $sth->fetchrow if ( $sth->rows );
1404 my $ip = $ENV{'REMOTE_ADDR'};
1405 # if they specify at login, use that
1406 if ($query->param('branch')) {
1407 $branchcode = $query->param('branch');
1408 $branchname = GetBranchName($branchcode);
1410 my $branches = GetBranches();
1411 my @branchesloop;
1412 foreach my $br ( keys %$branches ) {
1413 # now we work with the treatment of ip
1414 my $domain = $branches->{$br}->{'branchip'};
1415 if ( $domain && $ip =~ /^$domain/ ) {
1416 $branchcode = $branches->{$br}->{'branchcode'};
1418 # new op dev : add the branchprinter and branchname in the cookie
1419 $branchprinter = $branches->{$br}->{'branchprinter'};
1420 $branchname = $branches->{$br}->{'branchname'};
1423 $session->param('number',$borrowernumber);
1424 $session->param('id',$userid);
1425 $session->param('cardnumber',$cardnumber);
1426 $session->param('firstname',$firstname);
1427 $session->param('surname',$surname);
1428 $session->param('branch',$branchcode);
1429 $session->param('branchname',$branchname);
1430 $session->param('flags',$userflags);
1431 $session->param('emailaddress',$emailaddress);
1432 $session->param('ip',$session->remote_addr());
1433 $session->param('lasttime',time());
1434 } elsif ( $return == 2 ) {
1435 #We suppose the user is the superlibrarian
1436 $session->param('number',0);
1437 $session->param('id',C4::Context->config('user'));
1438 $session->param('cardnumber',C4::Context->config('user'));
1439 $session->param('firstname',C4::Context->config('user'));
1440 $session->param('surname',C4::Context->config('user'));
1441 $session->param('branch','NO_LIBRARY_SET');
1442 $session->param('branchname','NO_LIBRARY_SET');
1443 $session->param('flags',1);
1444 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1445 $session->param('ip',$session->remote_addr());
1446 $session->param('lasttime',time());
1448 C4::Context::set_userenv(
1449 $session->param('number'), $session->param('id'),
1450 $session->param('cardnumber'), $session->param('firstname'),
1451 $session->param('surname'), $session->param('branch'),
1452 $session->param('branchname'), $session->param('flags'),
1453 $session->param('emailaddress'), $session->param('branchprinter')
1455 return ("ok", $cookie, $sessionID);
1456 } else {
1457 return ("failed", undef, undef);
1462 =head2 check_cookie_auth
1464 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1466 Given a CGISESSID cookie set during a previous login to Koha, determine
1467 if the user has the privileges specified by C<$userflags>.
1469 C<check_cookie_auth> is meant for authenticating special services
1470 such as tools/upload-file.pl that are invoked by other pages that
1471 have been authenticated in the usual way.
1473 Possible return values in C<$status> are:
1475 =over
1477 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1479 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1481 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1483 =item "expired -- session cookie has expired; API user should resubmit userid and password
1485 =back
1487 =cut
1489 sub check_cookie_auth {
1490 my $cookie = shift;
1491 my $flagsrequired = shift;
1493 my $dbh = C4::Context->dbh;
1494 my $timeout = _timeout_syspref();
1496 unless (C4::Context->preference('Version')) {
1497 # database has not been installed yet
1498 return ("maintenance", undef);
1500 my $kohaversion=C4::Context::KOHAVERSION;
1501 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1502 if (C4::Context->preference('Version') < $kohaversion) {
1503 # database in need of version update; assume that
1504 # no API should be called while databsae is in
1505 # this condition.
1506 return ("maintenance", undef);
1509 # FIXME -- most of what follows is a copy-and-paste
1510 # of code from checkauth. There is an obvious need
1511 # for refactoring to separate the various parts of
1512 # the authentication code, but as of 2007-11-23 this
1513 # is deferred so as to not introduce bugs into the
1514 # regular authentication code for Koha 3.0.
1516 # see if we have a valid session cookie already
1517 # however, if a userid parameter is present (i.e., from
1518 # a form submission, assume that any current cookie
1519 # is to be ignored
1520 unless (defined $cookie and $cookie) {
1521 return ("failed", undef);
1523 my $sessionID = $cookie;
1524 my $session = get_session($sessionID);
1525 C4::Context->_new_userenv($sessionID);
1526 if ($session) {
1527 C4::Context::set_userenv(
1528 $session->param('number'), $session->param('id'),
1529 $session->param('cardnumber'), $session->param('firstname'),
1530 $session->param('surname'), $session->param('branch'),
1531 $session->param('branchname'), $session->param('flags'),
1532 $session->param('emailaddress'), $session->param('branchprinter')
1535 my $ip = $session->param('ip');
1536 my $lasttime = $session->param('lasttime');
1537 my $userid = $session->param('id');
1538 if ( $lasttime < time() - $timeout ) {
1539 # time out
1540 $session->delete();
1541 $session->flush;
1542 C4::Context->_unset_userenv($sessionID);
1543 $userid = undef;
1544 $sessionID = undef;
1545 return ("expired", undef);
1546 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1547 # IP address changed
1548 $session->delete();
1549 $session->flush;
1550 C4::Context->_unset_userenv($sessionID);
1551 $userid = undef;
1552 $sessionID = undef;
1553 return ("expired", undef);
1554 } else {
1555 $session->param('lasttime',time());
1556 my $flags = haspermission($userid, $flagsrequired);
1557 if ($flags) {
1558 return ("ok", $sessionID);
1559 } else {
1560 $session->delete();
1561 $session->flush;
1562 C4::Context->_unset_userenv($sessionID);
1563 $userid = undef;
1564 $sessionID = undef;
1565 return ("failed", undef);
1568 } else {
1569 return ("expired", undef);
1573 =head2 get_session
1575 use CGI::Session;
1576 my $session = get_session($sessionID);
1578 Given a session ID, retrieve the CGI::Session object used to store
1579 the session's state. The session object can be used to store
1580 data that needs to be accessed by different scripts during a
1581 user's session.
1583 If the C<$sessionID> parameter is an empty string, a new session
1584 will be created.
1586 =cut
1588 sub get_session {
1589 my $sessionID = shift;
1590 my $storage_method = C4::Context->preference('SessionStorage');
1591 my $dbh = C4::Context->dbh;
1592 my $session;
1593 if ($storage_method eq 'mysql'){
1594 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1596 elsif ($storage_method eq 'Pg') {
1597 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1599 elsif ($storage_method eq 'memcached' && C4::Context->ismemcached){
1600 $session = new CGI::Session("driver:memcached;serializer:yaml;id:md5", $sessionID, { Memcached => C4::Context->memcached } );
1602 else {
1603 # catch all defaults to tmp should work on all systems
1604 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1606 return $session;
1609 sub checkpw {
1610 my ( $dbh, $userid, $password, $query ) = @_;
1611 if ($ldap) {
1612 $debug and print STDERR "## checkpw - checking LDAP\n";
1613 my ($retval,$retcard,$retuserid) = checkpw_ldap(@_); # EXTERNAL AUTH
1614 return 0 if $retval == -1; # Incorrect password for LDAP login attempt
1615 ($retval) and return ($retval,$retcard,$retuserid);
1618 if ($cas && $query && $query->param('ticket')) {
1619 $debug and print STDERR "## checkpw - checking CAS\n";
1620 # In case of a CAS authentication, we use the ticket instead of the password
1621 my $ticket = $query->param('ticket');
1622 $query->delete('ticket'); # remove ticket to come back to original URL
1623 my ($retval,$retcard,$retuserid) = checkpw_cas($dbh, $ticket, $query); # EXTERNAL AUTH
1624 ($retval) and return ($retval,$retcard,$retuserid);
1625 return 0;
1628 # If we are in a shibboleth session (shibboleth is enabled, and a shibboleth match attribute is present)
1629 # Check for password to asertain whether we want to be testing against shibboleth or another method this
1630 # time around.
1631 if ($shib && $shib_login && !$password) {
1633 $debug and print STDERR "## checkpw - checking Shibboleth\n";
1634 # In case of a Shibboleth authentication, we expect a shibboleth user attribute
1635 # (defined under shibboleth mapping in koha-conf.xml) to contain the login of the
1636 # shibboleth-authenticated user
1638 # Then, we check if it matches a valid koha user
1639 if ($shib_login) {
1640 my ( $retval, $retcard, $retuserid ) = C4::Auth_with_shibboleth::checkpw_shib( $shib_login ); # EXTERNAL AUTH
1641 ($retval) and return ( $retval, $retcard, $retuserid );
1642 return 0;
1646 # INTERNAL AUTH
1647 return checkpw_internal(@_)
1650 sub checkpw_internal {
1651 my ( $dbh, $userid, $password ) = @_;
1653 if ( $userid && $userid eq C4::Context->config('user') ) {
1654 if ( $password && $password eq C4::Context->config('pass') ) {
1655 # Koha superuser account
1656 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1657 return 2;
1659 else {
1660 return 0;
1664 my $sth =
1665 $dbh->prepare(
1666 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1668 $sth->execute($userid);
1669 if ( $sth->rows ) {
1670 my ( $stored_hash, $cardnumber, $borrowernumber, $userid, $firstname,
1671 $surname, $branchcode, $flags )
1672 = $sth->fetchrow;
1674 if ( checkpw_hash($password, $stored_hash) ) {
1676 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1677 $firstname, $surname, $branchcode, $flags );
1678 return 1, $cardnumber, $userid;
1681 $sth =
1682 $dbh->prepare(
1683 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1685 $sth->execute($userid);
1686 if ( $sth->rows ) {
1687 my ( $stored_hash, $cardnumber, $borrowernumber, $userid, $firstname,
1688 $surname, $branchcode, $flags )
1689 = $sth->fetchrow;
1691 if ( checkpw_hash($password, $stored_hash) ) {
1693 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1694 $firstname, $surname, $branchcode, $flags );
1695 return 1, $cardnumber, $userid;
1698 if ( $userid && $userid eq 'demo'
1699 && "$password" eq 'demo'
1700 && C4::Context->config('demo') )
1703 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1704 # some features won't be effective : modify systempref, modify MARC structure,
1705 return 2;
1707 return 0;
1710 sub checkpw_hash {
1711 my ( $password, $stored_hash ) = @_;
1713 return if $stored_hash eq '!';
1715 # check what encryption algorithm was implemented: Bcrypt - if the hash starts with '$2' it is Bcrypt else md5
1716 my $hash;
1717 if ( substr($stored_hash,0,2) eq '$2') {
1718 $hash = hash_password($password, $stored_hash);
1719 } else {
1720 $hash = md5_base64($password);
1722 return $hash eq $stored_hash;
1725 =head2 getuserflags
1727 my $authflags = getuserflags($flags, $userid, [$dbh]);
1729 Translates integer flags into permissions strings hash.
1731 C<$flags> is the integer userflags value ( borrowers.userflags )
1732 C<$userid> is the members.userid, used for building subpermissions
1733 C<$authflags> is a hashref of permissions
1735 =cut
1737 sub getuserflags {
1738 my $flags = shift;
1739 my $userid = shift;
1740 my $dbh = @_ ? shift : C4::Context->dbh;
1741 my $userflags;
1743 # I don't want to do this, but if someone logs in as the database
1744 # user, it would be preferable not to spam them to death with
1745 # numeric warnings. So, we make $flags numeric.
1746 no warnings 'numeric';
1747 $flags += 0;
1749 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1750 $sth->execute;
1752 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1753 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1754 $userflags->{$flag} = 1;
1756 else {
1757 $userflags->{$flag} = 0;
1760 # get subpermissions and merge with top-level permissions
1761 my $user_subperms = get_user_subpermissions($userid);
1762 foreach my $module (keys %$user_subperms) {
1763 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1764 $userflags->{$module} = $user_subperms->{$module};
1767 return $userflags;
1770 =head2 get_user_subpermissions
1772 $user_perm_hashref = get_user_subpermissions($userid);
1774 Given the userid (note, not the borrowernumber) of a staff user,
1775 return a hashref of hashrefs of the specific subpermissions
1776 accorded to the user. An example return is
1779 tools => {
1780 export_catalog => 1,
1781 import_patrons => 1,
1785 The top-level hash-key is a module or function code from
1786 userflags.flag, while the second-level key is a code
1787 from permissions.
1789 The results of this function do not give a complete picture
1790 of the functions that a staff user can access; it is also
1791 necessary to check borrowers.flags.
1793 =cut
1795 sub get_user_subpermissions {
1796 my $userid = shift;
1798 my $dbh = C4::Context->dbh;
1799 my $sth = $dbh->prepare("SELECT flag, user_permissions.code
1800 FROM user_permissions
1801 JOIN permissions USING (module_bit, code)
1802 JOIN userflags ON (module_bit = bit)
1803 JOIN borrowers USING (borrowernumber)
1804 WHERE userid = ?");
1805 $sth->execute($userid);
1807 my $user_perms = {};
1808 while (my $perm = $sth->fetchrow_hashref) {
1809 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1811 return $user_perms;
1814 =head2 get_all_subpermissions
1816 my $perm_hashref = get_all_subpermissions();
1818 Returns a hashref of hashrefs defining all specific
1819 permissions currently defined. The return value
1820 has the same structure as that of C<get_user_subpermissions>,
1821 except that the innermost hash value is the description
1822 of the subpermission.
1824 =cut
1826 sub get_all_subpermissions {
1827 my $dbh = C4::Context->dbh;
1828 my $sth = $dbh->prepare("SELECT flag, code, description
1829 FROM permissions
1830 JOIN userflags ON (module_bit = bit)");
1831 $sth->execute();
1833 my $all_perms = {};
1834 while (my $perm = $sth->fetchrow_hashref) {
1835 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1837 return $all_perms;
1840 =head2 haspermission
1842 $flags = ($userid, $flagsrequired);
1844 C<$userid> the userid of the member
1845 C<$flags> is a hashref of required flags like C<$borrower-&lt;{authflags}>
1847 Returns member's flags or 0 if a permission is not met.
1849 =cut
1851 sub haspermission {
1852 my ($userid, $flagsrequired) = @_;
1853 my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1854 $sth->execute($userid);
1855 my $row = $sth->fetchrow();
1856 my $flags = getuserflags($row, $userid);
1857 if ( $userid eq C4::Context->config('user') ) {
1858 # Super User Account from /etc/koha.conf
1859 $flags->{'superlibrarian'} = 1;
1861 elsif ( $userid eq 'demo' && C4::Context->config('demo') ) {
1862 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1863 $flags->{'superlibrarian'} = 1;
1866 return $flags if $flags->{superlibrarian};
1868 foreach my $module ( keys %$flagsrequired ) {
1869 my $subperm = $flagsrequired->{$module};
1870 if ($subperm eq '*') {
1871 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1872 } else {
1873 return 0 unless ( $flags->{$module} == 1 or
1874 ( ref($flags->{$module}) and
1875 exists $flags->{$module}->{$subperm} and
1876 $flags->{$module}->{$subperm} == 1
1881 return $flags;
1882 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1886 sub getborrowernumber {
1887 my ($userid) = @_;
1888 my $userenv = C4::Context->userenv;
1889 if ( defined( $userenv ) && ref( $userenv ) eq 'HASH' && $userenv->{number} ) {
1890 return $userenv->{number};
1892 my $dbh = C4::Context->dbh;
1893 for my $field ( 'userid', 'cardnumber' ) {
1894 my $sth =
1895 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1896 $sth->execute($userid);
1897 if ( $sth->rows ) {
1898 my ($bnumber) = $sth->fetchrow;
1899 return $bnumber;
1902 return 0;
1905 END { } # module clean-up code here (global destructor)
1907 __END__
1909 =head1 SEE ALSO
1911 CGI(3)
1913 C4::Output(3)
1915 Crypt::Eksblowfish::Bcrypt(3)
1917 Digest::MD5(3)
1919 =cut