3 # Copyright 2000-2002 Katipo Communications
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA 02111-1307 USA
21 use Digest::MD5 qw(md5_base64);
26 use C4::Output; # to get the template
29 use C4::Branch; # GetBranches
30 use C4::VirtualShelves;
33 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap);
36 $VERSION = 3.02; # set version for version checking
37 $debug = $ENV{DEBUG} || 0 ;
39 @EXPORT = qw(&checkauth &get_template_and_user);
40 @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &get_all_subpermissions &get_user_subpermissions);
41 %EXPORT_TAGS = (EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)]);
42 $ldap = C4::Context->config('useldapserver') || 0;
44 require C4::Auth_with_ldap; # no import
45 import C4::Auth_with_ldap qw(checkpw_ldap);
51 C4::Auth - Authenticates Koha users
61 my ($template, $borrowernumber, $cookie)
62 = get_template_and_user(
64 template_name => "opac-main.tmpl",
68 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
72 output_html_with_http_headers $query, $cookie, $template->output;
76 The main function of this module is to provide
77 authentification. However the get_template_and_user function has
78 been provided so that a users login information is passed along
79 automatically. This gets loaded into the template.
85 =item get_template_and_user
87 my ($template, $borrowernumber, $cookie)
88 = get_template_and_user(
90 template_name => "opac-main.tmpl",
94 flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' },
98 This call passes the C<query>, C<flagsrequired> and C<authnotrequired>
99 to C<&checkauth> (in this module) to perform authentification.
100 See C<&checkauth> for an explanation of these parameters.
102 The C<template_name> is then used to find the correct template for
103 the page. The authenticated users details are loaded onto the
104 template in the HTML::Template LOOP variable C<USER_INFO>. Also the
105 C<sessionID> is passed to the template. This can be used in templates
106 if cookies are disabled. It needs to be put as and input to every
109 More information on the C<gettemplate> sub can be found in the
114 sub get_template_and_user {
117 gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'} );
118 my ( $user, $cookie, $sessionID, $flags ) = checkauth(
120 $in->{'authnotrequired'},
121 $in->{'flagsrequired'},
123 ) unless ($in->{'template_name'}=~/maintenance/);
126 my $insecure = C4::Context->preference('insecure');
127 if ($user or $insecure) {
129 # load the template variables for stylesheets and JavaScript
130 $template->param( css_libs => $in->{'css_libs'} );
131 $template->param( css_module => $in->{'css_module'} );
132 $template->param( css_page => $in->{'css_page'} );
133 $template->param( css_widgets => $in->{'css_widgets'} );
135 $template->param( js_libs => $in->{'js_libs'} );
136 $template->param( js_module => $in->{'js_module'} );
137 $template->param( js_page => $in->{'js_page'} );
138 $template->param( js_widgets => $in->{'js_widgets'} );
141 $template->param( loggedinusername => $user );
142 $template->param( sessionID => $sessionID );
144 my ($total, $pubshelves, $barshelves) = C4::Context->get_shelves_userenv();
145 if (defined($pubshelves)) {
146 $template->param( pubshelves => scalar (@$pubshelves),
147 pubshelvesloop => $pubshelves,
149 $template->param( pubtotal => $total->{'pubtotal'}, ) if ($total->{'pubtotal'} > scalar (@$pubshelves));
151 if (defined($barshelves)) {
152 $template->param( barshelves => scalar (@$barshelves),
153 barshelvesloop => $barshelves,
155 $template->param( bartotal => $total->{'bartotal'}, ) if ($total->{'bartotal'} > scalar (@$barshelves));
158 $borrowernumber = getborrowernumber($user) if defined($user);
160 my ( $borr ) = GetMemberDetails( $borrowernumber );
163 $template->param( "USER_INFO" => \@bordat );
165 my $all_perms = get_all_subpermissions();
167 my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow
168 editcatalogue updatecharges management tools editauthorities serials reports);
169 # We are going to use the $flags returned by checkauth
170 # to create the template's parameters that will indicate
171 # which menus the user can access.
172 if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) {
173 $template->param( CAN_user_circulate => 1 );
174 $template->param( CAN_user_catalogue => 1 );
175 $template->param( CAN_user_parameters => 1 );
176 $template->param( CAN_user_borrowers => 1 );
177 $template->param( CAN_user_permissions => 1 );
178 $template->param( CAN_user_reserveforothers => 1 );
179 $template->param( CAN_user_borrow => 1 );
180 $template->param( CAN_user_editcatalogue => 1 );
181 $template->param( CAN_user_updatecharges => 1 );
182 $template->param( CAN_user_acquisition => 1 );
183 $template->param( CAN_user_management => 1 );
184 $template->param( CAN_user_tools => 1 );
185 $template->param( CAN_user_editauthorities => 1 );
186 $template->param( CAN_user_serials => 1 );
187 $template->param( CAN_user_reports => 1 );
188 $template->param( CAN_user_staffaccess => 1 );
189 foreach my $module (keys %$all_perms) {
190 foreach my $subperm (keys %{ $all_perms->{$module} }) {
191 $template->param( "CAN_user_${module}_${subperm}" => 1 );
196 if (C4::Context->preference('GranularPermissions')) {
198 foreach my $module (keys %$all_perms) {
199 if ( $flags->{$module} == 1) {
200 foreach my $subperm (keys %{ $all_perms->{$module} }) {
201 $template->param( "CAN_user_${module}_${subperm}" => 1 );
203 } elsif ( ref($flags->{$module}) ) {
204 foreach my $subperm (keys %{ $flags->{$module} } ) {
205 $template->param( "CAN_user_${module}_${subperm}" => 1 );
211 foreach my $module (keys %$all_perms) {
212 foreach my $subperm (keys %{ $all_perms->{$module} }) {
213 $template->param( "CAN_user_${module}_${subperm}" => 1 );
219 foreach my $module (keys %$flags) {
220 if ( $flags->{$module} == 1 or ref($flags->{$module}) ) {
221 $template->param( "CAN_user_$module" => 1 );
222 if ($module eq "parameters") {
223 $template->param( CAN_user_management => 1 );
229 else { # if this is an anonymous session, setup to display public lists...
231 # load the template variables for stylesheets and JavaScript
232 $template->param( css_libs => $in->{'css_libs'} );
233 $template->param( css_module => $in->{'css_module'} );
234 $template->param( css_page => $in->{'css_page'} );
235 $template->param( css_widgets => $in->{'css_widgets'} );
237 $template->param( js_libs => $in->{'js_libs'} );
238 $template->param( js_module => $in->{'js_module'} );
239 $template->param( js_page => $in->{'js_page'} );
240 $template->param( js_widgets => $in->{'js_widgets'} );
242 $template->param( sessionID => $sessionID );
244 my ($total, $pubshelves) = C4::Context->get_shelves_userenv(); # an anonymous user has no 'barshelves'...
245 if (defined(($pubshelves))) {
246 $template->param( pubshelves => scalar (@$pubshelves),
247 pubshelvesloop => $pubshelves,
249 $template->param( pubtotal => $total->{'pubtotal'}, ) if ($total->{'pubtotal'} > scalar (@$pubshelves));
254 # these template parameters are set the same regardless of $in->{'type'}
256 "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
257 EnhancedMessagingPreferences => C4::Context->preference('EnhancedMessagingPreferences'),
258 GoogleJackets => C4::Context->preference("GoogleJackets"),
259 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
260 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
261 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
262 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
263 TagsEnabled => C4::Context->preference("TagsEnabled"),
264 hide_marc => C4::Context->preference("hide_marc"),
265 dateformat => C4::Context->preference("dateformat"),
266 'item-level_itypes' => C4::Context->preference('item-level_itypes'),
267 patronimages => C4::Context->preference("patronimages"),
268 singleBranchMode => C4::Context->preference("singleBranchMode"),
271 if ( $in->{'type'} eq "intranet" ) {
273 AmazonContent => C4::Context->preference("AmazonContent"),
274 AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
275 AutoLocation => C4::Context->preference("AutoLocation"),
276 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
277 CircAutocompl => C4::Context->preference("CircAutocompl"),
278 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
279 IndependantBranches => C4::Context->preference("IndependantBranches"),
280 IntranetNav => C4::Context->preference("IntranetNav"),
281 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
282 LibraryName => C4::Context->preference("LibraryName"),
283 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
284 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
285 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
286 canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
287 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
288 intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
289 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
290 intranetuserjs => C4::Context->preference("intranetuserjs"),
291 noItemTypeImages => C4::Context->preference("noItemTypeImages"),
292 suggestion => C4::Context->preference("suggestion"),
293 virtualshelves => C4::Context->preference("virtualshelves"),
297 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
298 #TODO : replace LibraryName syspref with 'system name', and remove this html processing
299 my $LibraryNameTitle = C4::Context->preference("LibraryName");
300 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
301 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
302 # variables passed from CGI: opac_css_override and opac_search_limits.
303 my $opac_search_limit = $ENV{'OPAC_SEARCH_LIMIT'};
304 my $opac_limit_override = $ENV{'OPAC_LIMIT_OVERRIDE'};
305 my $mylibraryfirst = C4::Context->preference("SearchMyLibraryFirst");
307 if($opac_limit_override && ($opac_search_limit =~ /branch:(\w+)/) ){
308 $opac_name = C4::Branch::GetBranchName($1) # opac_search_limit is a branch, so we use it.
309 } elsif($mylibraryfirst){
310 $opac_name = C4::Branch::GetBranchName($mylibraryfirst);
313 OPACAmazonContent => "" . C4::Context->preference("OPACAmazonContent"),
314 OPACAmazonCoverImages => "" . C4::Context->preference("OPACAmazonCoverImages"),
315 OPACAmazonSimilarItems => "" . C4::Context->preference("OPACAmazonSimilarItems"),
316 OPACAmazonEnabled => C4::Context->preference("OPACAmazonEnabled"),
317 OPACAmazonReviews => C4::Context->preference("OPACAmazonReviews"),
318 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
319 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
320 LibraryName => "" . C4::Context->preference("LibraryName"),
321 LibraryNameTitle => "" . $LibraryNameTitle,
322 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
323 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
324 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
325 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
326 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
327 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
328 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
329 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
330 OPACBaseURL => ($in->{'query'}->https() ? "https://" : "http://") . $ENV{'SERVER_NAME'} .
331 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
332 opac_name => $opac_name,
333 opac_css_override => $ENV{'OPAC_CSS_OVERRIDE'},
334 opac_search_limit => $opac_search_limit,
335 opac_limit_override => $opac_limit_override,
336 OpacBrowser => C4::Context->preference("OpacBrowser"),
337 OpacCloud => C4::Context->preference("OpacCloud"),
338 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
339 OpacNav => "" . C4::Context->preference("OpacNav"),
340 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
341 OpacTopissue => C4::Context->preference("OpacTopissue"),
342 OpacRecentAcquisitions => C4::Context->preference("OpacRecentAcquisitions"),
343 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
344 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
345 'Version' => C4::Context->preference('Version'),
346 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
347 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
348 hidelostitems => C4::Context->preference("hidelostitems"),
349 mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '',
350 opaclayoutstylesheet => "" . C4::Context->preference("opaclayoutstylesheet"),
351 opaccolorstylesheet => "" . C4::Context->preference("opaccolorstylesheet"),
352 opacstylesheet => "" . C4::Context->preference("opacstylesheet"),
353 opacbookbag => "" . C4::Context->preference("opacbookbag"),
354 opaccredits => "" . C4::Context->preference("opaccredits"),
355 opacheader => "" . C4::Context->preference("opacheader"),
356 opaclanguagesdisplay => "" . C4::Context->preference("opaclanguagesdisplay"),
357 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
358 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
359 opacuserjs => C4::Context->preference("opacuserjs"),
360 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
361 reviewson => C4::Context->preference("reviewson"),
362 suggestion => "" . C4::Context->preference("suggestion"),
363 virtualshelves => "" . C4::Context->preference("virtualshelves"),
366 return ( $template, $borrowernumber, $cookie, $flags);
371 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
373 Verifies that the user is authorized to run this script. If
374 the user is authorized, a (userid, cookie, session-id, flags)
375 quadruple is returned. If the user is not authorized due to
376 insufficent privileges (see $flagsrequired below), it
377 displays an error page and exits. Otherwise, it displays the
378 login page and exits.
380 Note that C<&checkauth> will return if and only if the user
381 is authorized, so it should be called early on, before any
382 unfinished operations (e.g., if you've opened a file, then
383 C<&checkauth> won't close it for you).
385 C<$query> is the CGI object for the script calling C<&checkauth>.
387 The C<$noauth> argument is optional. If it is set, then no
388 authorization is required for the script.
390 C<&checkauth> fetches user and session information from C<$query> and
391 ensures that the user is authorized to run scripts that require
394 The C<$flagsrequired> argument specifies the required privileges
395 the user must have if the username and password are correct.
396 It should be specified as a reference-to-hash; keys in the hash
397 should be the "flags" for the user, as specified in the Members
398 intranet module. Any key specified must correspond to a "flag"
399 in the userflags table. E.g., { circulate => 1 } would specify
400 that the user must have the "circulate" privilege in order to
401 proceed. To make sure that access control is correct, the
402 C<$flagsrequired> parameter must be specified correctly.
404 If the GranularPermissions system preference is ON, the
405 value of each key in the C<flagsrequired> hash takes on an additional
410 The user must have access to all subfunctions of the module
411 specified by the hash key.
415 The user must have access to at least one subfunction of the module
416 specified by the hash key.
418 =item specific permission, e.g., 'export_catalog'
420 The user must have access to the specific subfunction list, which
421 must correspond to a row in the permissions table.
423 The C<$type> argument specifies whether the template should be
424 retrieved from the opac or intranet directory tree. "opac" is
425 assumed if it is not specified; however, if C<$type> is specified,
426 "intranet" is assumed if it is not "opac".
428 If C<$query> does not have a valid session ID associated with it
429 (i.e., the user has not logged in) or if the session has expired,
430 C<&checkauth> presents the user with a login page (from the point of
431 view of the original script, C<&checkauth> does not return). Once the
432 user has authenticated, C<&checkauth> restarts the original script
433 (this time, C<&checkauth> returns).
435 The login page is provided using a HTML::Template, which is set in the
436 systempreferences table or at the top of this file. The variable C<$type>
437 selects which template to use, either the opac or the intranet
438 authentification template.
440 C<&checkauth> returns a user ID, a cookie, and a session ID. The
441 cookie should be sent back to the browser; it verifies that the user
446 sub _version_check ($$) {
450 # If Version syspref is unavailable, it means Koha is beeing installed,
451 # and so we must redirect to OPAC maintenance page or to the WebInstaller
452 # also, if OpacMaintenance is ON, OPAC should redirect to maintenance
453 if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') {
454 warn "OPAC Install required, redirecting to maintenance";
455 print $query->redirect("/cgi-bin/koha/maintenance.pl");
457 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
458 if ($type ne 'opac') {
459 warn "Install required, redirecting to Installer";
460 print $query->redirect("/cgi-bin/koha/installer/install.pl");
463 warn "OPAC Install required, redirecting to maintenance";
464 print $query->redirect("/cgi-bin/koha/maintenance.pl");
469 # check that database and koha version are the same
470 # there is no DB version, it's a fresh install,
471 # go to web installer
472 # there is a DB version, compare it to the code version
473 my $kohaversion=C4::Context::KOHAVERSION;
474 # remove the 3 last . to have a Perl number
475 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
476 $debug and print STDERR "kohaversion : $kohaversion\n";
477 if ($version < $kohaversion){
478 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
479 if ($type ne 'opac'){
480 warn sprintf($warning, 'Installer');
481 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
483 warn sprintf("OPAC: " . $warning, 'maintenance');
484 print $query->redirect("/cgi-bin/koha/maintenance.pl");
492 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
493 printf L join("\n",@_);
499 $debug and warn "Checking Auth";
500 # $authnotrequired will be set for scripts which will run without authentication
501 my $authnotrequired = shift;
502 my $flagsrequired = shift;
504 $type = 'opac' unless $type;
506 my $dbh = C4::Context->dbh;
507 my $timeout = C4::Context->preference('timeout');
509 if ($timeout =~ /(\d+)[dD]/) {
510 $timeout = $1 * 86400;
512 $timeout = 600 unless $timeout;
514 _version_check($type,$query);
518 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
519 my $logout = $query->param('logout.x');
521 if ( $userid = $ENV{'REMOTE_USER'} ) {
522 # Using Basic Authentication, no cookies required
523 $cookie = $query->cookie(
524 -name => 'CGISESSID',
530 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
531 my $session = get_session($sessionID);
532 C4::Context->_new_userenv($sessionID);
533 my ($ip, $lasttime, $sessiontype);
535 C4::Context::set_userenv(
536 $session->param('number'), $session->param('id'),
537 $session->param('cardnumber'), $session->param('firstname'),
538 $session->param('surname'), $session->param('branch'),
539 $session->param('branchname'), $session->param('flags'),
540 $session->param('emailaddress'), $session->param('branchprinter')
542 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
543 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
544 C4::Context::set_shelves_userenv('tot',$session->param('totshelves'));
545 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
546 $ip = $session->param('ip');
547 $lasttime = $session->param('lasttime');
548 $userid = $session->param('id');
549 $sessiontype = $session->param('sessiontype');
552 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
553 #if a user enters an id ne to the id in the current session, we need to log them in...
554 #first we need to clear the anonymous session...
555 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
558 C4::Context->_unset_userenv($sessionID);
563 # voluntary logout the user
566 C4::Context->_unset_userenv($sessionID);
567 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,localtime);
571 elsif ( $lasttime < time() - $timeout ) {
573 $info{'timed_out'} = 1;
575 C4::Context->_unset_userenv($sessionID);
576 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,localtime);
580 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
581 # Different ip than originally logged in from
582 $info{'oldip'} = $ip;
583 $info{'newip'} = $ENV{'REMOTE_ADDR'};
584 $info{'different_ip'} = 1;
586 C4::Context->_unset_userenv($sessionID);
587 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,localtime, $info{'newip'});
592 $cookie = $query->cookie( CGISESSID => $session->id );
593 $session->param('lasttime',time());
594 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...
595 $flags = haspermission($userid, $flagsrequired);
599 $info{'nopermission'} = 1;
604 unless ($userid || $sessionID) {
605 #we initiate a session prior to checking for a username to allow for anonymous sessions...
606 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
607 my $sessionID = $session->id;
608 C4::Context->_new_userenv($sessionID);
609 $cookie = $query->cookie(CGISESSID => $sessionID);
610 if ( $userid = $query->param('userid') ) {
611 my $password = $query->param('password');
612 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
614 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},localtime);
615 if ( $flags = haspermission($userid, $flagsrequired) ) {
619 $info{'nopermission'} = 1;
620 C4::Context->_unset_userenv($sessionID);
623 my ($borrowernumber, $firstname, $surname, $userflags,
624 $branchcode, $branchname, $branchprinter, $emailaddress);
626 if ( $return == 1 ) {
628 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
629 branches.branchname as branchname,
630 branches.branchprinter as branchprinter,
633 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
635 my $sth = $dbh->prepare("$select where userid=?");
636 $sth->execute($userid);
637 unless ($sth->rows) {
638 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
639 $sth = $dbh->prepare("$select where cardnumber=?");
640 $sth->execute($cardnumber);
641 unless ($sth->rows) {
642 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
643 $sth->execute($userid);
644 unless ($sth->rows) {
645 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
650 ($borrowernumber, $firstname, $surname, $userflags,
651 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
652 $debug and print STDERR "AUTH_3 results: " .
653 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
655 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
657 # launch a sequence to check if we have a ip for the branch, i
658 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
660 my $ip = $ENV{'REMOTE_ADDR'};
661 # if they specify at login, use that
662 if ($query->param('branch')) {
663 $branchcode = $query->param('branch');
664 $branchname = GetBranchName($branchcode);
666 my $branches = GetBranches();
667 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
668 # we have to check they are coming from the right ip range
669 my $domain = $branches->{$branchcode}->{'branchip'};
670 if ($ip !~ /^$domain/){
672 $info{'wrongip'} = 1;
677 foreach my $br ( keys %$branches ) {
678 # now we work with the treatment of ip
679 my $domain = $branches->{$br}->{'branchip'};
680 if ( $domain && $ip =~ /^$domain/ ) {
681 $branchcode = $branches->{$br}->{'branchcode'};
683 # new op dev : add the branchprinter and branchname in the cookie
684 $branchprinter = $branches->{$br}->{'branchprinter'};
685 $branchname = $branches->{$br}->{'branchname'};
688 $session->param('number',$borrowernumber);
689 $session->param('id',$userid);
690 $session->param('cardnumber',$cardnumber);
691 $session->param('firstname',$firstname);
692 $session->param('surname',$surname);
693 $session->param('branch',$branchcode);
694 $session->param('branchname',$branchname);
695 $session->param('flags',$userflags);
696 $session->param('emailaddress',$emailaddress);
697 $session->param('ip',$session->remote_addr());
698 $session->param('lasttime',time());
699 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
701 elsif ( $return == 2 ) {
702 #We suppose the user is the superlibrarian
704 $session->param('number',0);
705 $session->param('id',C4::Context->config('user'));
706 $session->param('cardnumber',C4::Context->config('user'));
707 $session->param('firstname',C4::Context->config('user'));
708 $session->param('surname',C4::Context->config('user'));
709 $session->param('branch','NO_LIBRARY_SET');
710 $session->param('branchname','NO_LIBRARY_SET');
711 $session->param('flags',1);
712 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
713 $session->param('ip',$session->remote_addr());
714 $session->param('lasttime',time());
716 C4::Context::set_userenv(
717 $session->param('number'), $session->param('id'),
718 $session->param('cardnumber'), $session->param('firstname'),
719 $session->param('surname'), $session->param('branch'),
720 $session->param('branchname'), $session->param('flags'),
721 $session->param('emailaddress'), $session->param('branchprinter')
724 # Grab borrower's shelves and public shelves and add them to the session
725 # $row_count determines how many records are returned from the db query
726 # and the number of lists to be displayed of each type in the 'Lists' button drop down
727 my $row_count = 10; # FIXME:This probably should be a syspref
728 my ($total, $totshelves, $barshelves, $pubshelves);
729 ($barshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(1, $row_count, $borrowernumber);
730 $total->{'bartotal'} = $totshelves;
731 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
732 $total->{'pubtotal'} = $totshelves;
733 $session->param('barshelves', $barshelves->[0]);
734 $session->param('pubshelves', $pubshelves->[0]);
735 $session->param('totshelves', $total);
737 C4::Context::set_shelves_userenv('bar',$barshelves->[0]);
738 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
739 C4::Context::set_shelves_userenv('tot',$total);
743 $info{'invalid_username_or_password'} = 1;
744 C4::Context->_unset_userenv($sessionID);
747 } # END if ( $userid = $query->param('userid') )
748 elsif ($type eq "opac") {
749 # if we are here this is an anonymous session; add public lists to it and a few other items...
750 # anonymous sessions are created only for the OPAC
751 $debug and warn "Initiating an anonymous session...";
753 # Grab the public shelves and add to the session...
754 my $row_count = 20; # FIXME:This probably should be a syspref
755 my ($total, $totshelves, $pubshelves);
756 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
757 $total->{'pubtotal'} = $totshelves;
758 $session->param('pubshelves', $pubshelves->[0]);
759 $session->param('totshelves', $total);
760 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
761 C4::Context::set_shelves_userenv('tot',$total);
763 # setting a couple of other session vars...
764 $session->param('ip',$session->remote_addr());
765 $session->param('lasttime',time());
766 $session->param('sessiontype','anon');
768 } # END unless ($userid)
769 my $insecure = C4::Context->boolean_preference('insecure');
771 # finished authentification, now respond
772 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
776 $cookie = $query->cookie( CGISESSID => '' );
778 return ( $userid, $cookie, $sessionID, $flags );
783 # AUTH rejected, show the login/password template, after checking the DB.
787 # get the inputs from the incoming query
789 foreach my $name ( param $query) {
790 (next) if ( $name eq 'userid' || $name eq 'password' );
791 my $value = $query->param($name);
792 push @inputs, { name => $name, value => $value };
794 # get the branchloop, which we need for authentication
795 my $branches = GetBranches();
797 for my $branch_hash (sort keys %$branches) {
798 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
801 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
802 my $template = gettemplate( $template_name, $type, $query );
803 $template->param(branchloop => \@branch_loop,);
807 suggestion => C4::Context->preference("suggestion"),
808 virtualshelves => C4::Context->preference("virtualshelves"),
809 LibraryName => C4::Context->preference("LibraryName"),
810 opacuserlogin => C4::Context->preference("opacuserlogin"),
811 OpacNav => C4::Context->preference("OpacNav"),
812 opaccredits => C4::Context->preference("opaccredits"),
813 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
814 opacsmallimage => C4::Context->preference("opacsmallimage"),
815 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
816 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
817 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
818 opacuserjs => C4::Context->preference("opacuserjs"),
819 opacbookbag => "" . C4::Context->preference("opacbookbag"),
820 OpacCloud => C4::Context->preference("OpacCloud"),
821 OpacTopissue => C4::Context->preference("OpacTopissue"),
822 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
823 OpacBrowser => C4::Context->preference("OpacBrowser"),
824 opacheader => C4::Context->preference("opacheader"),
825 TagsEnabled => C4::Context->preference("TagsEnabled"),
826 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
827 intranetcolorstylesheet =>
828 C4::Context->preference("intranetcolorstylesheet"),
829 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
830 IntranetNav => C4::Context->preference("IntranetNav"),
831 intranetuserjs => C4::Context->preference("intranetuserjs"),
832 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
833 IndependantBranches=> C4::Context->preference("IndependantBranches"),
834 AutoLocation => C4::Context->preference("AutoLocation"),
835 wrongip => $info{'wrongip'}
838 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
840 my $self_url = $query->url( -absolute => 1 );
843 LibraryName => C4::Context->preference("LibraryName"),
845 $template->param( \%info );
846 # $cookie = $query->cookie(CGISESSID => $session->id
848 print $query->header(
849 -type => 'text/html',
859 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
861 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
862 cookie, determine if the user has the privileges specified by C<$userflags>.
864 C<check_api_auth> is is meant for authenticating users of web services, and
865 consequently will always return and will not attempt to redirect the user
868 If a valid session cookie is already present, check_api_auth will return a status
869 of "ok", the cookie, and the Koha session ID.
871 If no session cookie is present, check_api_auth will check the 'userid' and 'password
872 parameters and create a session cookie and Koha session if the supplied credentials
875 Possible return values in C<$status> are:
879 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
881 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
883 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
885 =item "expired -- session cookie has expired; API user should resubmit userid and password
893 my $flagsrequired = shift;
895 my $dbh = C4::Context->dbh;
896 my $timeout = C4::Context->preference('timeout');
897 $timeout = 600 unless $timeout;
899 unless (C4::Context->preference('Version')) {
900 # database has not been installed yet
901 return ("maintenance", undef, undef);
903 my $kohaversion=C4::Context::KOHAVERSION;
904 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
905 if (C4::Context->preference('Version') < $kohaversion) {
906 # database in need of version update; assume that
907 # no API should be called while databsae is in
909 return ("maintenance", undef, undef);
912 # FIXME -- most of what follows is a copy-and-paste
913 # of code from checkauth. There is an obvious need
914 # for refactoring to separate the various parts of
915 # the authentication code, but as of 2007-11-19 this
916 # is deferred so as to not introduce bugs into the
917 # regular authentication code for Koha 3.0.
919 # see if we have a valid session cookie already
920 # however, if a userid parameter is present (i.e., from
921 # a form submission, assume that any current cookie
923 my $sessionID = undef;
924 unless ($query->param('userid')) {
925 $sessionID = $query->cookie("CGISESSID");
928 my $session = get_session($sessionID);
929 C4::Context->_new_userenv($sessionID);
931 C4::Context::set_userenv(
932 $session->param('number'), $session->param('id'),
933 $session->param('cardnumber'), $session->param('firstname'),
934 $session->param('surname'), $session->param('branch'),
935 $session->param('branchname'), $session->param('flags'),
936 $session->param('emailaddress'), $session->param('branchprinter')
939 my $ip = $session->param('ip');
940 my $lasttime = $session->param('lasttime');
941 my $userid = $session->param('id');
942 if ( $lasttime < time() - $timeout ) {
945 C4::Context->_unset_userenv($sessionID);
948 return ("expired", undef, undef);
949 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
952 C4::Context->_unset_userenv($sessionID);
955 return ("expired", undef, undef);
957 my $cookie = $query->cookie( CGISESSID => $session->id );
958 $session->param('lasttime',time());
959 my $flags = haspermission($userid, $flagsrequired);
961 return ("ok", $cookie, $sessionID);
964 C4::Context->_unset_userenv($sessionID);
967 return ("failed", undef, undef);
971 return ("expired", undef, undef);
975 my $userid = $query->param('userid');
976 my $password = $query->param('password');
977 unless ($userid and $password) {
978 # caller did something wrong, fail the authenticateion
979 return ("failed", undef, undef);
981 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
982 if ($return and haspermission($userid, $flagsrequired)) {
983 my $session = get_session("");
984 return ("failed", undef, undef) unless $session;
986 my $sessionID = $session->id;
987 C4::Context->_new_userenv($sessionID);
988 my $cookie = $query->cookie(CGISESSID => $sessionID);
989 if ( $return == 1 ) {
991 $borrowernumber, $firstname, $surname,
992 $userflags, $branchcode, $branchname,
993 $branchprinter, $emailaddress
997 "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=?"
999 $sth->execute($userid);
1001 $borrowernumber, $firstname, $surname,
1002 $userflags, $branchcode, $branchname,
1003 $branchprinter, $emailaddress
1004 ) = $sth->fetchrow if ( $sth->rows );
1006 unless ($sth->rows ) {
1007 my $sth = $dbh->prepare(
1008 "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=?"
1010 $sth->execute($cardnumber);
1012 $borrowernumber, $firstname, $surname,
1013 $userflags, $branchcode, $branchname,
1014 $branchprinter, $emailaddress
1015 ) = $sth->fetchrow if ( $sth->rows );
1017 unless ( $sth->rows ) {
1018 $sth->execute($userid);
1020 $borrowernumber, $firstname, $surname, $userflags,
1021 $branchcode, $branchname, $branchprinter, $emailaddress
1022 ) = $sth->fetchrow if ( $sth->rows );
1026 my $ip = $ENV{'REMOTE_ADDR'};
1027 # if they specify at login, use that
1028 if ($query->param('branch')) {
1029 $branchcode = $query->param('branch');
1030 $branchname = GetBranchName($branchcode);
1032 my $branches = GetBranches();
1034 foreach my $br ( keys %$branches ) {
1035 # now we work with the treatment of ip
1036 my $domain = $branches->{$br}->{'branchip'};
1037 if ( $domain && $ip =~ /^$domain/ ) {
1038 $branchcode = $branches->{$br}->{'branchcode'};
1040 # new op dev : add the branchprinter and branchname in the cookie
1041 $branchprinter = $branches->{$br}->{'branchprinter'};
1042 $branchname = $branches->{$br}->{'branchname'};
1045 $session->param('number',$borrowernumber);
1046 $session->param('id',$userid);
1047 $session->param('cardnumber',$cardnumber);
1048 $session->param('firstname',$firstname);
1049 $session->param('surname',$surname);
1050 $session->param('branch',$branchcode);
1051 $session->param('branchname',$branchname);
1052 $session->param('flags',$userflags);
1053 $session->param('emailaddress',$emailaddress);
1054 $session->param('ip',$session->remote_addr());
1055 $session->param('lasttime',time());
1056 } elsif ( $return == 2 ) {
1057 #We suppose the user is the superlibrarian
1058 $session->param('number',0);
1059 $session->param('id',C4::Context->config('user'));
1060 $session->param('cardnumber',C4::Context->config('user'));
1061 $session->param('firstname',C4::Context->config('user'));
1062 $session->param('surname',C4::Context->config('user'));
1063 $session->param('branch','NO_LIBRARY_SET');
1064 $session->param('branchname','NO_LIBRARY_SET');
1065 $session->param('flags',1);
1066 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1067 $session->param('ip',$session->remote_addr());
1068 $session->param('lasttime',time());
1070 C4::Context::set_userenv(
1071 $session->param('number'), $session->param('id'),
1072 $session->param('cardnumber'), $session->param('firstname'),
1073 $session->param('surname'), $session->param('branch'),
1074 $session->param('branchname'), $session->param('flags'),
1075 $session->param('emailaddress'), $session->param('branchprinter')
1077 return ("ok", $cookie, $sessionID);
1079 return ("failed", undef, undef);
1084 =item check_cookie_auth
1086 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1088 Given a CGISESSID cookie set during a previous login to Koha, determine
1089 if the user has the privileges specified by C<$userflags>.
1091 C<check_cookie_auth> is meant for authenticating special services
1092 such as tools/upload-file.pl that are invoked by other pages that
1093 have been authenticated in the usual way.
1095 Possible return values in C<$status> are:
1099 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1101 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1103 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1105 =item "expired -- session cookie has expired; API user should resubmit userid and password
1111 sub check_cookie_auth {
1113 my $flagsrequired = shift;
1115 my $dbh = C4::Context->dbh;
1116 my $timeout = C4::Context->preference('timeout');
1117 $timeout = 600 unless $timeout;
1119 unless (C4::Context->preference('Version')) {
1120 # database has not been installed yet
1121 return ("maintenance", undef);
1123 my $kohaversion=C4::Context::KOHAVERSION;
1124 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1125 if (C4::Context->preference('Version') < $kohaversion) {
1126 # database in need of version update; assume that
1127 # no API should be called while databsae is in
1129 return ("maintenance", undef);
1132 # FIXME -- most of what follows is a copy-and-paste
1133 # of code from checkauth. There is an obvious need
1134 # for refactoring to separate the various parts of
1135 # the authentication code, but as of 2007-11-23 this
1136 # is deferred so as to not introduce bugs into the
1137 # regular authentication code for Koha 3.0.
1139 # see if we have a valid session cookie already
1140 # however, if a userid parameter is present (i.e., from
1141 # a form submission, assume that any current cookie
1143 unless (defined $cookie and $cookie) {
1144 return ("failed", undef);
1146 my $sessionID = $cookie;
1147 my $session = get_session($sessionID);
1148 C4::Context->_new_userenv($sessionID);
1150 C4::Context::set_userenv(
1151 $session->param('number'), $session->param('id'),
1152 $session->param('cardnumber'), $session->param('firstname'),
1153 $session->param('surname'), $session->param('branch'),
1154 $session->param('branchname'), $session->param('flags'),
1155 $session->param('emailaddress'), $session->param('branchprinter')
1158 my $ip = $session->param('ip');
1159 my $lasttime = $session->param('lasttime');
1160 my $userid = $session->param('id');
1161 if ( $lasttime < time() - $timeout ) {
1164 C4::Context->_unset_userenv($sessionID);
1167 return ("expired", undef);
1168 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1169 # IP address changed
1171 C4::Context->_unset_userenv($sessionID);
1174 return ("expired", undef);
1176 $session->param('lasttime',time());
1177 my $flags = haspermission($userid, $flagsrequired);
1179 return ("ok", $sessionID);
1182 C4::Context->_unset_userenv($sessionID);
1185 return ("failed", undef);
1189 return ("expired", undef);
1196 my $session = get_session($sessionID);
1198 Given a session ID, retrieve the CGI::Session object used to store
1199 the session's state. The session object can be used to store
1200 data that needs to be accessed by different scripts during a
1203 If the C<$sessionID> parameter is an empty string, a new session
1209 my $sessionID = shift;
1210 my $storage_method = C4::Context->preference('SessionStorage');
1211 my $dbh = C4::Context->dbh;
1213 if ($storage_method eq 'mysql'){
1214 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1216 elsif ($storage_method eq 'Pg') {
1217 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1220 # catch all defaults to tmp should work on all systems
1221 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1228 my ( $dbh, $userid, $password ) = @_;
1230 $debug and print STDERR "## checkpw - checking LDAP\n";
1231 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1232 ($retval) and return ($retval,$retcard);
1238 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1240 $sth->execute($userid);
1242 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1243 $surname, $branchcode, $flags )
1245 if ( ( md5_base64($password) eq $md5password ) and ($md5password ne "!") ) {
1247 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1248 $firstname, $surname, $branchcode, $flags );
1249 return 1, $cardnumber;
1254 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1256 $sth->execute($userid);
1258 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1259 $surname, $branchcode, $flags )
1261 if ( ( md5_base64($password) eq $md5password ) and ($md5password ne "!") ) {
1263 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1264 $firstname, $surname, $branchcode, $flags );
1265 return 1, $cardnumber;
1268 if ( $userid && $userid eq C4::Context->config('user')
1269 && "$password" eq C4::Context->config('pass') )
1272 # Koha superuser account
1273 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1276 if ( $userid && $userid eq 'demo'
1277 && "$password" eq 'demo'
1278 && C4::Context->config('demo') )
1281 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1282 # some features won't be effective : modify systempref, modify MARC structure,
1290 my $authflags = getuserflags($flags, $userid, [$dbh]);
1292 Translates integer flags into permissions strings hash.
1294 C<$flags> is the integer userflags value ( borrowers.userflags )
1295 C<$userid> is the members.userid, used for building subpermissions
1296 C<$authflags> is a hashref of permissions
1303 my $dbh = @_ ? shift : C4::Context->dbh;
1305 $flags = 0 unless $flags;
1306 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1309 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1310 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1311 $userflags->{$flag} = 1;
1314 $userflags->{$flag} = 0;
1318 # get subpermissions and merge with top-level permissions
1319 my $user_subperms = get_user_subpermissions($userid);
1320 foreach my $module (keys %$user_subperms) {
1321 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1322 $userflags->{$module} = $user_subperms->{$module};
1328 =item get_user_subpermissions
1332 my $user_perm_hashref = get_user_subpermissions($userid);
1336 Given the userid (note, not the borrowernumber) of a staff user,
1337 return a hashref of hashrefs of the specific subpermissions
1338 accorded to the user. An example return is
1342 export_catalog => 1,
1343 import_patrons => 1,
1347 The top-level hash-key is a module or function code from
1348 userflags.flag, while the second-level key is a code
1351 The results of this function do not give a complete picture
1352 of the functions that a staff user can access; it is also
1353 necessary to check borrowers.flags.
1357 sub get_user_subpermissions {
1360 my $dbh = C4::Context->dbh;
1361 my $sth = $dbh->prepare("SELECT flag, user_permissions.code as code
1362 FROM user_permissions
1363 JOIN permissions USING (module_bit, code)
1364 JOIN userflags ON (permissions.module_bit = userflags.bit)
1365 JOIN borrowers ON (user_permissions.borrowernumber=borrowers.borrowernumber)
1367 $sth->execute($userid);
1369 my $user_perms = {};
1370 while (my $perm = $sth->fetchrow_hashref) {
1371 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1376 =item get_all_subpermissions
1380 my $perm_hashref = get_all_subpermissions();
1384 Returns a hashref of hashrefs defining all specific
1385 permissions currently defined. The return value
1386 has the same structure as that of C<get_user_subpermissions>,
1387 except that the innermost hash value is the description
1388 of the subpermission.
1392 sub get_all_subpermissions {
1393 my $dbh = C4::Context->dbh;
1394 my $sth = $dbh->prepare("SELECT flag, code, description
1396 JOIN userflags ON (module_bit = bit)");
1400 while (my $perm = $sth->fetchrow_hashref) {
1401 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1408 $flags = ($userid, $flagsrequired);
1410 C<$userid> the userid of the member
1411 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1413 Returns member's flags or 0 if a permission is not met.
1418 my ($userid, $flagsrequired) = @_;
1419 my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=? or cardnumber=?");
1420 $sth->execute($userid,$userid);
1421 my $flags = getuserflags( $sth->fetchrow(), $userid );
1422 if ( $userid eq C4::Context->config('user') ) {
1423 # Super User Account from /etc/koha.conf
1424 $flags->{'superlibrarian'} = 1;
1426 elsif ( $userid eq 'demo' && C4::Context->config('demo') ) {
1427 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1428 $flags->{'superlibrarian'} = 1;
1430 return $flags if $flags->{superlibrarian};
1431 foreach my $module ( keys %$flagsrequired ) {
1432 if (C4::Context->preference('GranularPermissions')) {
1433 my $subperm = $flagsrequired->{$module};
1434 if ($subperm eq '*') {
1435 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1437 return 0 unless ( $flags->{$module} == 1 or
1438 ( ref($flags->{$module}) and
1439 exists $flags->{$module}->{$subperm} and
1440 $flags->{$module}->{$subperm} == 1
1445 return 0 unless ( $flags->{$module} );
1449 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1453 sub getborrowernumber {
1455 my $userenv = C4::Context->userenv;
1456 if ( defined( $userenv ) && ref( $userenv ) eq 'HASH' && $userenv->{number} ) {
1457 return $userenv->{number};
1459 my $dbh = C4::Context->dbh;
1460 for my $field ( 'userid', 'cardnumber' ) {
1462 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1463 $sth->execute($userid);
1465 my ($bnumber) = $sth->fetchrow;
1472 END { } # module clean-up code here (global destructor)