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);
159 my ( $borr ) = GetMemberDetails( $borrowernumber );
162 $template->param( "USER_INFO" => \@bordat );
164 my $all_perms = get_all_subpermissions();
166 my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow
167 editcatalogue updatecharges management tools editauthorities serials reports);
168 # We are going to use the $flags returned by checkauth
169 # to create the template's parameters that will indicate
170 # which menus the user can access.
171 if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) {
172 $template->param( CAN_user_circulate => 1 );
173 $template->param( CAN_user_catalogue => 1 );
174 $template->param( CAN_user_parameters => 1 );
175 $template->param( CAN_user_borrowers => 1 );
176 $template->param( CAN_user_permissions => 1 );
177 $template->param( CAN_user_reserveforothers => 1 );
178 $template->param( CAN_user_borrow => 1 );
179 $template->param( CAN_user_editcatalogue => 1 );
180 $template->param( CAN_user_updatecharges => 1 );
181 $template->param( CAN_user_acquisition => 1 );
182 $template->param( CAN_user_management => 1 );
183 $template->param( CAN_user_tools => 1 );
184 $template->param( CAN_user_editauthorities => 1 );
185 $template->param( CAN_user_serials => 1 );
186 $template->param( CAN_user_reports => 1 );
187 $template->param( CAN_user_staffaccess => 1 );
188 foreach my $module (keys %$all_perms) {
189 foreach my $subperm (keys %{ $all_perms->{$module} }) {
190 $template->param( "CAN_user_${module}_${subperm}" => 1 );
195 if (C4::Context->preference('GranularPermissions')) {
197 foreach my $module (keys %$all_perms) {
198 if ( $flags->{$module} == 1) {
199 foreach my $subperm (keys %{ $all_perms->{$module} }) {
200 $template->param( "CAN_user_${module}_${subperm}" => 1 );
202 } elsif ( ref($flags->{$module}) ) {
203 foreach my $subperm (keys %{ $flags->{$module} } ) {
204 $template->param( "CAN_user_${module}_${subperm}" => 1 );
210 foreach my $module (keys %$all_perms) {
211 foreach my $subperm (keys %{ $all_perms->{$module} }) {
212 $template->param( "CAN_user_${module}_${subperm}" => 1 );
218 foreach my $module (keys %$flags) {
219 if ( $flags->{$module} == 1 or ref($flags->{$module}) ) {
220 $template->param( "CAN_user_$module" => 1 );
221 if ($module eq "parameters") {
222 $template->param( CAN_user_management => 1 );
228 else { # if this is an anonymous session, setup to display public lists...
230 # load the template variables for stylesheets and JavaScript
231 $template->param( css_libs => $in->{'css_libs'} );
232 $template->param( css_module => $in->{'css_module'} );
233 $template->param( css_page => $in->{'css_page'} );
234 $template->param( css_widgets => $in->{'css_widgets'} );
236 $template->param( js_libs => $in->{'js_libs'} );
237 $template->param( js_module => $in->{'js_module'} );
238 $template->param( js_page => $in->{'js_page'} );
239 $template->param( js_widgets => $in->{'js_widgets'} );
241 $template->param( sessionID => $sessionID );
243 my ($total, $pubshelves) = C4::Context->get_shelves_userenv(); # an anonymous user has no 'barshelves'...
244 if (defined(($pubshelves))) {
245 $template->param( pubshelves => scalar (@$pubshelves),
246 pubshelvesloop => $pubshelves,
248 $template->param( pubtotal => $total->{'pubtotal'}, ) if ($total->{'pubtotal'} > scalar (@$pubshelves));
253 # these template parameters are set the same regardless of $in->{'type'}
255 "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
256 EnhancedMessagingPreferences => C4::Context->preference('EnhancedMessagingPreferences'),
257 GoogleJackets => C4::Context->preference("GoogleJackets"),
258 KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"),
259 LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
260 LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
261 LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
262 TagsEnabled => C4::Context->preference("TagsEnabled"),
263 hide_marc => C4::Context->preference("hide_marc"),
264 dateformat => C4::Context->preference("dateformat"),
265 'item-level_itypes' => C4::Context->preference('item-level_itypes'),
266 patronimages => C4::Context->preference("patronimages"),
267 singleBranchMode => C4::Context->preference("singleBranchMode"),
270 if ( $in->{'type'} eq "intranet" ) {
272 AmazonContent => C4::Context->preference("AmazonContent"),
273 AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
274 AutoLocation => C4::Context->preference("AutoLocation"),
275 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
276 CircAutocompl => C4::Context->preference("CircAutocompl"),
277 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
278 IndependantBranches => C4::Context->preference("IndependantBranches"),
279 IntranetNav => C4::Context->preference("IntranetNav"),
280 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
281 LibraryName => C4::Context->preference("LibraryName"),
282 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
283 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
284 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
285 canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
286 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
287 intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
288 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
289 intranetuserjs => C4::Context->preference("intranetuserjs"),
290 noItemTypeImages => C4::Context->preference("noItemTypeImages"),
291 suggestion => C4::Context->preference("suggestion"),
292 virtualshelves => C4::Context->preference("virtualshelves"),
296 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
297 #TODO : replace LibraryName syspref with 'system name', and remove this html processing
298 my $LibraryNameTitle = C4::Context->preference("LibraryName");
299 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
300 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
301 # variables passed from CGI: opac_css_override and opac_search_limits.
302 my $opac_search_limit = $ENV{'OPAC_SEARCH_LIMIT'};
303 my $opac_limit_override = $ENV{'OPAC_LIMIT_OVERRIDE'};
304 my $mylibraryfirst = C4::Context->preference("SearchMyLibraryFirst");
306 if($opac_limit_override && ($opac_search_limit =~ /branch:(\w+)/) ){
307 $opac_name = C4::Branch::GetBranchName($1) # opac_search_limit is a branch, so we use it.
308 } elsif($mylibraryfirst){
309 $opac_name = C4::Branch::GetBranchName($mylibraryfirst);
312 OPACAmazonContent => "" . C4::Context->preference("OPACAmazonContent"),
313 OPACAmazonCoverImages => "" . C4::Context->preference("OPACAmazonCoverImages"),
314 OPACAmazonSimilarItems => "" . C4::Context->preference("OPACAmazonSimilarItems"),
315 OPACAmazonEnabled => C4::Context->preference("OPACAmazonEnabled"),
316 OPACAmazonReviews => C4::Context->preference("OPACAmazonReviews"),
317 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
318 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
319 LibraryName => "" . C4::Context->preference("LibraryName"),
320 LibraryNameTitle => "" . $LibraryNameTitle,
321 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
322 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
323 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
324 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
325 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
326 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
327 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
328 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
329 OPACBaseURL => ($in->{'query'}->https() ? "https://" : "http://") . $ENV{'SERVER_NAME'} .
330 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
331 opac_name => $opac_name,
332 opac_css_override => $ENV{'OPAC_CSS_OVERRIDE'},
333 opac_search_limit => $opac_search_limit,
334 opac_limit_override => $opac_limit_override,
335 OpacBrowser => C4::Context->preference("OpacBrowser"),
336 OpacCloud => C4::Context->preference("OpacCloud"),
337 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
338 OpacNav => "" . C4::Context->preference("OpacNav"),
339 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
340 OpacTopissue => C4::Context->preference("OpacTopissue"),
341 OpacRecentAcquisitions => C4::Context->preference("OpacRecentAcquisitions"),
342 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
343 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
344 'Version' => C4::Context->preference('Version'),
345 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
346 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
347 hidelostitems => C4::Context->preference("hidelostitems"),
348 mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '',
349 opaclayoutstylesheet => "" . C4::Context->preference("opaclayoutstylesheet"),
350 opaccolorstylesheet => "" . C4::Context->preference("opaccolorstylesheet"),
351 opacstylesheet => "" . C4::Context->preference("opacstylesheet"),
352 opacbookbag => "" . C4::Context->preference("opacbookbag"),
353 opaccredits => "" . C4::Context->preference("opaccredits"),
354 opacheader => "" . C4::Context->preference("opacheader"),
355 opaclanguagesdisplay => "" . C4::Context->preference("opaclanguagesdisplay"),
356 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
357 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
358 opacuserjs => C4::Context->preference("opacuserjs"),
359 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
360 reviewson => C4::Context->preference("reviewson"),
361 suggestion => "" . C4::Context->preference("suggestion"),
362 virtualshelves => "" . C4::Context->preference("virtualshelves"),
365 return ( $template, $borrowernumber, $cookie, $flags);
370 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
372 Verifies that the user is authorized to run this script. If
373 the user is authorized, a (userid, cookie, session-id, flags)
374 quadruple is returned. If the user is not authorized due to
375 insufficent privileges (see $flagsrequired below), it
376 displays an error page and exits. Otherwise, it displays the
377 login page and exits.
379 Note that C<&checkauth> will return if and only if the user
380 is authorized, so it should be called early on, before any
381 unfinished operations (e.g., if you've opened a file, then
382 C<&checkauth> won't close it for you).
384 C<$query> is the CGI object for the script calling C<&checkauth>.
386 The C<$noauth> argument is optional. If it is set, then no
387 authorization is required for the script.
389 C<&checkauth> fetches user and session information from C<$query> and
390 ensures that the user is authorized to run scripts that require
393 The C<$flagsrequired> argument specifies the required privileges
394 the user must have if the username and password are correct.
395 It should be specified as a reference-to-hash; keys in the hash
396 should be the "flags" for the user, as specified in the Members
397 intranet module. Any key specified must correspond to a "flag"
398 in the userflags table. E.g., { circulate => 1 } would specify
399 that the user must have the "circulate" privilege in order to
400 proceed. To make sure that access control is correct, the
401 C<$flagsrequired> parameter must be specified correctly.
403 If the GranularPermissions system preference is ON, the
404 value of each key in the C<flagsrequired> hash takes on an additional
409 The user must have access to all subfunctions of the module
410 specified by the hash key.
414 The user must have access to at least one subfunction of the module
415 specified by the hash key.
417 =item specific permission, e.g., 'export_catalog'
419 The user must have access to the specific subfunction list, which
420 must correspond to a row in the permissions table.
422 The C<$type> argument specifies whether the template should be
423 retrieved from the opac or intranet directory tree. "opac" is
424 assumed if it is not specified; however, if C<$type> is specified,
425 "intranet" is assumed if it is not "opac".
427 If C<$query> does not have a valid session ID associated with it
428 (i.e., the user has not logged in) or if the session has expired,
429 C<&checkauth> presents the user with a login page (from the point of
430 view of the original script, C<&checkauth> does not return). Once the
431 user has authenticated, C<&checkauth> restarts the original script
432 (this time, C<&checkauth> returns).
434 The login page is provided using a HTML::Template, which is set in the
435 systempreferences table or at the top of this file. The variable C<$type>
436 selects which template to use, either the opac or the intranet
437 authentification template.
439 C<&checkauth> returns a user ID, a cookie, and a session ID. The
440 cookie should be sent back to the browser; it verifies that the user
445 sub _version_check ($$) {
449 # If Version syspref is unavailable, it means Koha is beeing installed,
450 # and so we must redirect to OPAC maintenance page or to the WebInstaller
451 # also, if OpacMaintenance is ON, OPAC should redirect to maintenance
452 if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') {
453 warn "OPAC Install required, redirecting to maintenance";
454 print $query->redirect("/cgi-bin/koha/maintenance.pl");
456 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
457 if ($type ne 'opac') {
458 warn "Install required, redirecting to Installer";
459 print $query->redirect("/cgi-bin/koha/installer/install.pl");
462 warn "OPAC Install required, redirecting to maintenance";
463 print $query->redirect("/cgi-bin/koha/maintenance.pl");
468 # check that database and koha version are the same
469 # there is no DB version, it's a fresh install,
470 # go to web installer
471 # there is a DB version, compare it to the code version
472 my $kohaversion=C4::Context::KOHAVERSION;
473 # remove the 3 last . to have a Perl number
474 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
475 $debug and print STDERR "kohaversion : $kohaversion\n";
476 if ($version < $kohaversion){
477 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
478 if ($type ne 'opac'){
479 warn sprintf($warning, 'Installer');
480 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
482 warn sprintf("OPAC: " . $warning, 'maintenance');
483 print $query->redirect("/cgi-bin/koha/maintenance.pl");
491 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
492 printf L join("\n",@_);
498 $debug and warn "Checking Auth";
499 # $authnotrequired will be set for scripts which will run without authentication
500 my $authnotrequired = shift;
501 my $flagsrequired = shift;
503 $type = 'opac' unless $type;
505 my $dbh = C4::Context->dbh;
506 my $timeout = C4::Context->preference('timeout');
508 if ($timeout =~ /(\d+)[dD]/) {
509 $timeout = $1 * 86400;
511 $timeout = 600 unless $timeout;
513 _version_check($type,$query);
517 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
518 my $logout = $query->param('logout.x');
520 if ( $userid = $ENV{'REMOTE_USER'} ) {
521 # Using Basic Authentication, no cookies required
522 $cookie = $query->cookie(
523 -name => 'CGISESSID',
529 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
530 my $session = get_session($sessionID);
531 C4::Context->_new_userenv($sessionID);
532 my ($ip, $lasttime, $sessiontype);
534 C4::Context::set_userenv(
535 $session->param('number'), $session->param('id'),
536 $session->param('cardnumber'), $session->param('firstname'),
537 $session->param('surname'), $session->param('branch'),
538 $session->param('branchname'), $session->param('flags'),
539 $session->param('emailaddress'), $session->param('branchprinter')
541 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
542 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
543 C4::Context::set_shelves_userenv('tot',$session->param('totshelves'));
544 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
545 $ip = $session->param('ip');
546 $lasttime = $session->param('lasttime');
547 $userid = $session->param('id');
548 $sessiontype = $session->param('sessiontype');
551 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
552 #if a user enters an id ne to the id in the current session, we need to log them in...
553 #first we need to clear the anonymous session...
554 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
557 C4::Context->_unset_userenv($sessionID);
562 # voluntary logout the user
565 C4::Context->_unset_userenv($sessionID);
566 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,localtime);
570 elsif ( $lasttime < time() - $timeout ) {
572 $info{'timed_out'} = 1;
574 C4::Context->_unset_userenv($sessionID);
575 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,localtime);
579 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
580 # Different ip than originally logged in from
581 $info{'oldip'} = $ip;
582 $info{'newip'} = $ENV{'REMOTE_ADDR'};
583 $info{'different_ip'} = 1;
585 C4::Context->_unset_userenv($sessionID);
586 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,localtime, $info{'newip'});
591 $cookie = $query->cookie( CGISESSID => $session->id );
592 $session->param('lasttime',time());
593 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...
594 $flags = haspermission($userid, $flagsrequired);
598 $info{'nopermission'} = 1;
603 unless ($userid || $sessionID) {
604 #we initiate a session prior to checking for a username to allow for anonymous sessions...
605 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
606 my $sessionID = $session->id;
607 C4::Context->_new_userenv($sessionID);
608 $cookie = $query->cookie(CGISESSID => $sessionID);
609 if ( $userid = $query->param('userid') ) {
610 my $password = $query->param('password');
611 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
613 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},localtime);
614 if ( $flags = haspermission($userid, $flagsrequired) ) {
618 $info{'nopermission'} = 1;
619 C4::Context->_unset_userenv($sessionID);
622 my ($borrowernumber, $firstname, $surname, $userflags,
623 $branchcode, $branchname, $branchprinter, $emailaddress);
625 if ( $return == 1 ) {
627 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
628 branches.branchname as branchname,
629 branches.branchprinter as branchprinter,
632 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
634 my $sth = $dbh->prepare("$select where userid=?");
635 $sth->execute($userid);
636 unless ($sth->rows) {
637 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
638 $sth = $dbh->prepare("$select where cardnumber=?");
639 $sth->execute($cardnumber);
640 unless ($sth->rows) {
641 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
642 $sth->execute($userid);
643 unless ($sth->rows) {
644 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
649 ($borrowernumber, $firstname, $surname, $userflags,
650 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
651 $debug and print STDERR "AUTH_3 results: " .
652 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
654 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
656 # launch a sequence to check if we have a ip for the branch, i
657 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
659 my $ip = $ENV{'REMOTE_ADDR'};
660 # if they specify at login, use that
661 if ($query->param('branch')) {
662 $branchcode = $query->param('branch');
663 $branchname = GetBranchName($branchcode);
665 my $branches = GetBranches();
666 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
667 # we have to check they are coming from the right ip range
668 my $domain = $branches->{$branchcode}->{'branchip'};
669 if ($ip !~ /^$domain/){
671 $info{'wrongip'} = 1;
676 foreach my $br ( keys %$branches ) {
677 # now we work with the treatment of ip
678 my $domain = $branches->{$br}->{'branchip'};
679 if ( $domain && $ip =~ /^$domain/ ) {
680 $branchcode = $branches->{$br}->{'branchcode'};
682 # new op dev : add the branchprinter and branchname in the cookie
683 $branchprinter = $branches->{$br}->{'branchprinter'};
684 $branchname = $branches->{$br}->{'branchname'};
687 $session->param('number',$borrowernumber);
688 $session->param('id',$userid);
689 $session->param('cardnumber',$cardnumber);
690 $session->param('firstname',$firstname);
691 $session->param('surname',$surname);
692 $session->param('branch',$branchcode);
693 $session->param('branchname',$branchname);
694 $session->param('flags',$userflags);
695 $session->param('emailaddress',$emailaddress);
696 $session->param('ip',$session->remote_addr());
697 $session->param('lasttime',time());
698 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
700 elsif ( $return == 2 ) {
701 #We suppose the user is the superlibrarian
703 $session->param('number',0);
704 $session->param('id',C4::Context->config('user'));
705 $session->param('cardnumber',C4::Context->config('user'));
706 $session->param('firstname',C4::Context->config('user'));
707 $session->param('surname',C4::Context->config('user'));
708 $session->param('branch','NO_LIBRARY_SET');
709 $session->param('branchname','NO_LIBRARY_SET');
710 $session->param('flags',1);
711 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
712 $session->param('ip',$session->remote_addr());
713 $session->param('lasttime',time());
715 C4::Context::set_userenv(
716 $session->param('number'), $session->param('id'),
717 $session->param('cardnumber'), $session->param('firstname'),
718 $session->param('surname'), $session->param('branch'),
719 $session->param('branchname'), $session->param('flags'),
720 $session->param('emailaddress'), $session->param('branchprinter')
723 # Grab borrower's shelves and public shelves and add them to the session
724 # $row_count determines how many records are returned from the db query
725 # and the number of lists to be displayed of each type in the 'Lists' button drop down
726 my $row_count = 10; # FIXME:This probably should be a syspref
727 my ($total, $totshelves, $barshelves, $pubshelves);
728 ($barshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(1, $row_count, $borrowernumber);
729 $total->{'bartotal'} = $totshelves;
730 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
731 $total->{'pubtotal'} = $totshelves;
732 $session->param('barshelves', $barshelves->[0]);
733 $session->param('pubshelves', $pubshelves->[0]);
734 $session->param('totshelves', $total);
736 C4::Context::set_shelves_userenv('bar',$barshelves->[0]);
737 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
738 C4::Context::set_shelves_userenv('tot',$total);
742 $info{'invalid_username_or_password'} = 1;
743 C4::Context->_unset_userenv($sessionID);
746 } # END if ( $userid = $query->param('userid') )
747 elsif ($type eq "opac") {
748 # if we are here this is an anonymous session; add public lists to it and a few other items...
749 # anonymous sessions are created only for the OPAC
750 $debug and warn "Initiating an anonymous session...";
752 # Grab the public shelves and add to the session...
753 my $row_count = 20; # FIXME:This probably should be a syspref
754 my ($total, $totshelves, $pubshelves);
755 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
756 $total->{'pubtotal'} = $totshelves;
757 $session->param('pubshelves', $pubshelves->[0]);
758 $session->param('totshelves', $total);
759 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
760 C4::Context::set_shelves_userenv('tot',$total);
762 # setting a couple of other session vars...
763 $session->param('ip',$session->remote_addr());
764 $session->param('lasttime',time());
765 $session->param('sessiontype','anon');
767 } # END unless ($userid)
768 my $insecure = C4::Context->boolean_preference('insecure');
770 # finished authentification, now respond
771 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
775 $cookie = $query->cookie( CGISESSID => '' );
777 return ( $userid, $cookie, $sessionID, $flags );
782 # AUTH rejected, show the login/password template, after checking the DB.
786 # get the inputs from the incoming query
788 foreach my $name ( param $query) {
789 (next) if ( $name eq 'userid' || $name eq 'password' );
790 my $value = $query->param($name);
791 push @inputs, { name => $name, value => $value };
793 # get the branchloop, which we need for authentication
794 my $branches = GetBranches();
796 for my $branch_hash (sort keys %$branches) {
797 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
800 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
801 my $template = gettemplate( $template_name, $type, $query );
802 $template->param(branchloop => \@branch_loop,);
806 suggestion => C4::Context->preference("suggestion"),
807 virtualshelves => C4::Context->preference("virtualshelves"),
808 LibraryName => C4::Context->preference("LibraryName"),
809 opacuserlogin => C4::Context->preference("opacuserlogin"),
810 OpacNav => C4::Context->preference("OpacNav"),
811 opaccredits => C4::Context->preference("opaccredits"),
812 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
813 opacsmallimage => C4::Context->preference("opacsmallimage"),
814 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
815 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
816 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
817 opacuserjs => C4::Context->preference("opacuserjs"),
818 opacbookbag => "" . C4::Context->preference("opacbookbag"),
819 OpacCloud => C4::Context->preference("OpacCloud"),
820 OpacTopissue => C4::Context->preference("OpacTopissue"),
821 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
822 OpacBrowser => C4::Context->preference("OpacBrowser"),
823 opacheader => C4::Context->preference("opacheader"),
824 TagsEnabled => C4::Context->preference("TagsEnabled"),
825 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
826 intranetcolorstylesheet =>
827 C4::Context->preference("intranetcolorstylesheet"),
828 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
829 IntranetNav => C4::Context->preference("IntranetNav"),
830 intranetuserjs => C4::Context->preference("intranetuserjs"),
831 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
832 IndependantBranches=> C4::Context->preference("IndependantBranches"),
833 AutoLocation => C4::Context->preference("AutoLocation"),
834 wrongip => $info{'wrongip'}
837 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
839 my $self_url = $query->url( -absolute => 1 );
842 LibraryName => C4::Context->preference("LibraryName"),
844 $template->param( \%info );
845 # $cookie = $query->cookie(CGISESSID => $session->id
847 print $query->header(
848 -type => 'text/html',
858 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
860 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
861 cookie, determine if the user has the privileges specified by C<$userflags>.
863 C<check_api_auth> is is meant for authenticating users of web services, and
864 consequently will always return and will not attempt to redirect the user
867 If a valid session cookie is already present, check_api_auth will return a status
868 of "ok", the cookie, and the Koha session ID.
870 If no session cookie is present, check_api_auth will check the 'userid' and 'password
871 parameters and create a session cookie and Koha session if the supplied credentials
874 Possible return values in C<$status> are:
878 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
880 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
882 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
884 =item "expired -- session cookie has expired; API user should resubmit userid and password
892 my $flagsrequired = shift;
894 my $dbh = C4::Context->dbh;
895 my $timeout = C4::Context->preference('timeout');
896 $timeout = 600 unless $timeout;
898 unless (C4::Context->preference('Version')) {
899 # database has not been installed yet
900 return ("maintenance", undef, undef);
902 my $kohaversion=C4::Context::KOHAVERSION;
903 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
904 if (C4::Context->preference('Version') < $kohaversion) {
905 # database in need of version update; assume that
906 # no API should be called while databsae is in
908 return ("maintenance", undef, undef);
911 # FIXME -- most of what follows is a copy-and-paste
912 # of code from checkauth. There is an obvious need
913 # for refactoring to separate the various parts of
914 # the authentication code, but as of 2007-11-19 this
915 # is deferred so as to not introduce bugs into the
916 # regular authentication code for Koha 3.0.
918 # see if we have a valid session cookie already
919 # however, if a userid parameter is present (i.e., from
920 # a form submission, assume that any current cookie
922 my $sessionID = undef;
923 unless ($query->param('userid')) {
924 $sessionID = $query->cookie("CGISESSID");
927 my $session = get_session($sessionID);
928 C4::Context->_new_userenv($sessionID);
930 C4::Context::set_userenv(
931 $session->param('number'), $session->param('id'),
932 $session->param('cardnumber'), $session->param('firstname'),
933 $session->param('surname'), $session->param('branch'),
934 $session->param('branchname'), $session->param('flags'),
935 $session->param('emailaddress'), $session->param('branchprinter')
938 my $ip = $session->param('ip');
939 my $lasttime = $session->param('lasttime');
940 my $userid = $session->param('id');
941 if ( $lasttime < time() - $timeout ) {
944 C4::Context->_unset_userenv($sessionID);
947 return ("expired", undef, undef);
948 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
951 C4::Context->_unset_userenv($sessionID);
954 return ("expired", undef, undef);
956 my $cookie = $query->cookie( CGISESSID => $session->id );
957 $session->param('lasttime',time());
958 my $flags = haspermission($userid, $flagsrequired);
960 return ("ok", $cookie, $sessionID);
963 C4::Context->_unset_userenv($sessionID);
966 return ("failed", undef, undef);
970 return ("expired", undef, undef);
974 my $userid = $query->param('userid');
975 my $password = $query->param('password');
976 unless ($userid and $password) {
977 # caller did something wrong, fail the authenticateion
978 return ("failed", undef, undef);
980 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
981 if ($return and haspermission($userid, $flagsrequired)) {
982 my $session = get_session("");
983 return ("failed", undef, undef) unless $session;
985 my $sessionID = $session->id;
986 C4::Context->_new_userenv($sessionID);
987 my $cookie = $query->cookie(CGISESSID => $sessionID);
988 if ( $return == 1 ) {
990 $borrowernumber, $firstname, $surname,
991 $userflags, $branchcode, $branchname,
992 $branchprinter, $emailaddress
996 "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=?"
998 $sth->execute($userid);
1000 $borrowernumber, $firstname, $surname,
1001 $userflags, $branchcode, $branchname,
1002 $branchprinter, $emailaddress
1003 ) = $sth->fetchrow if ( $sth->rows );
1005 unless ($sth->rows ) {
1006 my $sth = $dbh->prepare(
1007 "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=?"
1009 $sth->execute($cardnumber);
1011 $borrowernumber, $firstname, $surname,
1012 $userflags, $branchcode, $branchname,
1013 $branchprinter, $emailaddress
1014 ) = $sth->fetchrow if ( $sth->rows );
1016 unless ( $sth->rows ) {
1017 $sth->execute($userid);
1019 $borrowernumber, $firstname, $surname, $userflags,
1020 $branchcode, $branchname, $branchprinter, $emailaddress
1021 ) = $sth->fetchrow if ( $sth->rows );
1025 my $ip = $ENV{'REMOTE_ADDR'};
1026 # if they specify at login, use that
1027 if ($query->param('branch')) {
1028 $branchcode = $query->param('branch');
1029 $branchname = GetBranchName($branchcode);
1031 my $branches = GetBranches();
1033 foreach my $br ( keys %$branches ) {
1034 # now we work with the treatment of ip
1035 my $domain = $branches->{$br}->{'branchip'};
1036 if ( $domain && $ip =~ /^$domain/ ) {
1037 $branchcode = $branches->{$br}->{'branchcode'};
1039 # new op dev : add the branchprinter and branchname in the cookie
1040 $branchprinter = $branches->{$br}->{'branchprinter'};
1041 $branchname = $branches->{$br}->{'branchname'};
1044 $session->param('number',$borrowernumber);
1045 $session->param('id',$userid);
1046 $session->param('cardnumber',$cardnumber);
1047 $session->param('firstname',$firstname);
1048 $session->param('surname',$surname);
1049 $session->param('branch',$branchcode);
1050 $session->param('branchname',$branchname);
1051 $session->param('flags',$userflags);
1052 $session->param('emailaddress',$emailaddress);
1053 $session->param('ip',$session->remote_addr());
1054 $session->param('lasttime',time());
1055 } elsif ( $return == 2 ) {
1056 #We suppose the user is the superlibrarian
1057 $session->param('number',0);
1058 $session->param('id',C4::Context->config('user'));
1059 $session->param('cardnumber',C4::Context->config('user'));
1060 $session->param('firstname',C4::Context->config('user'));
1061 $session->param('surname',C4::Context->config('user'));
1062 $session->param('branch','NO_LIBRARY_SET');
1063 $session->param('branchname','NO_LIBRARY_SET');
1064 $session->param('flags',1);
1065 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1066 $session->param('ip',$session->remote_addr());
1067 $session->param('lasttime',time());
1069 C4::Context::set_userenv(
1070 $session->param('number'), $session->param('id'),
1071 $session->param('cardnumber'), $session->param('firstname'),
1072 $session->param('surname'), $session->param('branch'),
1073 $session->param('branchname'), $session->param('flags'),
1074 $session->param('emailaddress'), $session->param('branchprinter')
1076 return ("ok", $cookie, $sessionID);
1078 return ("failed", undef, undef);
1083 =item check_cookie_auth
1085 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1087 Given a CGISESSID cookie set during a previous login to Koha, determine
1088 if the user has the privileges specified by C<$userflags>.
1090 C<check_cookie_auth> is meant for authenticating special services
1091 such as tools/upload-file.pl that are invoked by other pages that
1092 have been authenticated in the usual way.
1094 Possible return values in C<$status> are:
1098 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1100 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1102 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1104 =item "expired -- session cookie has expired; API user should resubmit userid and password
1110 sub check_cookie_auth {
1112 my $flagsrequired = shift;
1114 my $dbh = C4::Context->dbh;
1115 my $timeout = C4::Context->preference('timeout');
1116 $timeout = 600 unless $timeout;
1118 unless (C4::Context->preference('Version')) {
1119 # database has not been installed yet
1120 return ("maintenance", undef);
1122 my $kohaversion=C4::Context::KOHAVERSION;
1123 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1124 if (C4::Context->preference('Version') < $kohaversion) {
1125 # database in need of version update; assume that
1126 # no API should be called while databsae is in
1128 return ("maintenance", undef);
1131 # FIXME -- most of what follows is a copy-and-paste
1132 # of code from checkauth. There is an obvious need
1133 # for refactoring to separate the various parts of
1134 # the authentication code, but as of 2007-11-23 this
1135 # is deferred so as to not introduce bugs into the
1136 # regular authentication code for Koha 3.0.
1138 # see if we have a valid session cookie already
1139 # however, if a userid parameter is present (i.e., from
1140 # a form submission, assume that any current cookie
1142 unless (defined $cookie and $cookie) {
1143 return ("failed", undef);
1145 my $sessionID = $cookie;
1146 my $session = get_session($sessionID);
1147 C4::Context->_new_userenv($sessionID);
1149 C4::Context::set_userenv(
1150 $session->param('number'), $session->param('id'),
1151 $session->param('cardnumber'), $session->param('firstname'),
1152 $session->param('surname'), $session->param('branch'),
1153 $session->param('branchname'), $session->param('flags'),
1154 $session->param('emailaddress'), $session->param('branchprinter')
1157 my $ip = $session->param('ip');
1158 my $lasttime = $session->param('lasttime');
1159 my $userid = $session->param('id');
1160 if ( $lasttime < time() - $timeout ) {
1163 C4::Context->_unset_userenv($sessionID);
1166 return ("expired", undef);
1167 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1168 # IP address changed
1170 C4::Context->_unset_userenv($sessionID);
1173 return ("expired", undef);
1175 $session->param('lasttime',time());
1176 my $flags = haspermission($userid, $flagsrequired);
1178 return ("ok", $sessionID);
1181 C4::Context->_unset_userenv($sessionID);
1184 return ("failed", undef);
1188 return ("expired", undef);
1195 my $session = get_session($sessionID);
1197 Given a session ID, retrieve the CGI::Session object used to store
1198 the session's state. The session object can be used to store
1199 data that needs to be accessed by different scripts during a
1202 If the C<$sessionID> parameter is an empty string, a new session
1208 my $sessionID = shift;
1209 my $storage_method = C4::Context->preference('SessionStorage');
1210 my $dbh = C4::Context->dbh;
1212 if ($storage_method eq 'mysql'){
1213 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1215 elsif ($storage_method eq 'Pg') {
1216 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1219 # catch all defaults to tmp should work on all systems
1220 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1227 my ( $dbh, $userid, $password ) = @_;
1229 $debug and print STDERR "## checkpw - checking LDAP\n";
1230 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1231 ($retval) and return ($retval,$retcard);
1237 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1239 $sth->execute($userid);
1241 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1242 $surname, $branchcode, $flags )
1244 if ( ( md5_base64($password) eq $md5password ) and ($md5password ne "!") ) {
1246 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1247 $firstname, $surname, $branchcode, $flags );
1248 return 1, $cardnumber;
1253 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1255 $sth->execute($userid);
1257 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1258 $surname, $branchcode, $flags )
1260 if ( md5_base64($password) eq $md5password ) {
1262 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1263 $firstname, $surname, $branchcode, $flags );
1267 if ( $userid && $userid eq C4::Context->config('user')
1268 && "$password" eq C4::Context->config('pass') )
1271 # Koha superuser account
1272 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1275 if ( $userid && $userid eq 'demo'
1276 && "$password" eq 'demo'
1277 && C4::Context->config('demo') )
1280 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1281 # some features won't be effective : modify systempref, modify MARC structure,
1289 my $authflags = getuserflags($flags, $userid, [$dbh]);
1291 Translates integer flags into permissions strings hash.
1293 C<$flags> is the integer userflags value ( borrowers.userflags )
1294 C<$userid> is the members.userid, used for building subpermissions
1295 C<$authflags> is a hashref of permissions
1302 my $dbh = @_ ? shift : C4::Context->dbh;
1304 $flags = 0 unless $flags;
1305 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1308 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1309 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1310 $userflags->{$flag} = 1;
1313 $userflags->{$flag} = 0;
1317 # get subpermissions and merge with top-level permissions
1318 my $user_subperms = get_user_subpermissions($userid);
1319 foreach my $module (keys %$user_subperms) {
1320 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1321 $userflags->{$module} = $user_subperms->{$module};
1327 =item get_user_subpermissions
1331 my $user_perm_hashref = get_user_subpermissions($userid);
1335 Given the userid (note, not the borrowernumber) of a staff user,
1336 return a hashref of hashrefs of the specific subpermissions
1337 accorded to the user. An example return is
1341 export_catalog => 1,
1342 import_patrons => 1,
1346 The top-level hash-key is a module or function code from
1347 userflags.flag, while the second-level key is a code
1350 The results of this function do not give a complete picture
1351 of the functions that a staff user can access; it is also
1352 necessary to check borrowers.flags.
1356 sub get_user_subpermissions {
1359 my $dbh = C4::Context->dbh;
1360 my $sth = $dbh->prepare("SELECT flag, user_permissions.code as code
1361 FROM user_permissions
1362 JOIN permissions USING (module_bit, code)
1363 JOIN userflags ON (permissions.module_bit = userflags.bit)
1364 JOIN borrowers ON (user_permissions.borrowernumber=borrowers.borrowernumber)
1366 $sth->execute($userid);
1368 my $user_perms = {};
1369 while (my $perm = $sth->fetchrow_hashref) {
1370 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1375 =item get_all_subpermissions
1379 my $perm_hashref = get_all_subpermissions();
1383 Returns a hashref of hashrefs defining all specific
1384 permissions currently defined. The return value
1385 has the same structure as that of C<get_user_subpermissions>,
1386 except that the innermost hash value is the description
1387 of the subpermission.
1391 sub get_all_subpermissions {
1392 my $dbh = C4::Context->dbh;
1393 my $sth = $dbh->prepare("SELECT flag, code, description
1395 JOIN userflags ON (module_bit = bit)");
1399 while (my $perm = $sth->fetchrow_hashref) {
1400 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1407 $flags = ($userid, $flagsrequired);
1409 C<$userid> the userid of the member
1410 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1412 Returns member's flags or 0 if a permission is not met.
1417 my ($userid, $flagsrequired) = @_;
1418 my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1419 $sth->execute($userid);
1420 my $flags = getuserflags( $sth->fetchrow(), $userid );
1421 if ( $userid eq C4::Context->config('user') ) {
1422 # Super User Account from /etc/koha.conf
1423 $flags->{'superlibrarian'} = 1;
1425 elsif ( $userid eq 'demo' && C4::Context->config('demo') ) {
1426 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1427 $flags->{'superlibrarian'} = 1;
1429 return $flags if $flags->{superlibrarian};
1430 foreach my $module ( keys %$flagsrequired ) {
1431 if (C4::Context->preference('GranularPermissions')) {
1432 my $subperm = $flagsrequired->{$module};
1433 if ($subperm eq '*') {
1434 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1436 return 0 unless ( $flags->{$module} == 1 or
1437 ( ref($flags->{$module}) and
1438 exists $flags->{$module}->{$subperm} and
1439 $flags->{$module}->{$subperm} == 1
1444 return 0 unless ( $flags->{$module} );
1448 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1452 sub getborrowernumber {
1454 my $userenv = C4::Context->userenv;
1455 if ( defined( $userenv ) && ref( $userenv ) eq 'HASH' && $userenv->{number} ) {
1456 return $userenv->{number};
1458 my $dbh = C4::Context->dbh;
1459 for my $field ( 'userid', 'cardnumber' ) {
1461 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1462 $sth->execute($userid);
1464 my ($bnumber) = $sth->fetchrow;
1471 END { } # module clean-up code here (global destructor)