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"),
268 SyndeticsClientCode => C4::Context->preference("SyndeticsClientCode"),
269 SyndeticsEnabled => C4::Context->preference("SyndeticsEnabled"),
270 SyndeticsCoverImages => C4::Context->preference("SyndeticsCoverImages"),
271 SyndeticsTOC => C4::Context->preference("SyndeticsTOC"),
272 SyndeticsSummary => C4::Context->preference("SyndeticsSummary"),
273 SyndeticsEditions => C4::Context->preference("SyndeticsEditions"),
274 SyndeticsExcerpt => C4::Context->preference("SyndeticsExcerpt"),
275 SyndeticsReviews => C4::Context->preference("SyndeticsReviews"),
276 SyndeticsAuthorNotes => C4::Context->preference("SyndeticsAuthorNotes"),
277 SyndeticsAwards => C4::Context->preference("SyndeticsAwards"),
278 SyndeticsSeries => C4::Context->preference("SyndeticsSeries"),
279 SyndeticsCoverImageSize => C4::Context->preference("SyndeticsCoverImageSize"),
282 if ( $in->{'type'} eq "intranet" ) {
284 AmazonEnabled => C4::Context->preference("AmazonEnabled"),
285 AmazonCoverImages => C4::Context->preference("AmazonCoverImages"),
286 AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
287 AutoLocation => C4::Context->preference("AutoLocation"),
288 "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
289 CircAutocompl => C4::Context->preference("CircAutocompl"),
290 FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
291 IndependantBranches => C4::Context->preference("IndependantBranches"),
292 IntranetNav => C4::Context->preference("IntranetNav"),
293 IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"),
294 LibraryName => C4::Context->preference("LibraryName"),
295 LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
296 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
297 advancedMARCEditor => C4::Context->preference("advancedMARCEditor"),
298 canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
299 intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
300 intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
301 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
302 intranetuserjs => C4::Context->preference("intranetuserjs"),
303 noItemTypeImages => C4::Context->preference("noItemTypeImages"),
304 suggestion => C4::Context->preference("suggestion"),
305 virtualshelves => C4::Context->preference("virtualshelves"),
306 StaffSerialIssueDisplayCount => C4::Context->preference("StaffSerialIssueDisplayCount"),
310 warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
311 #TODO : replace LibraryName syspref with 'system name', and remove this html processing
312 my $LibraryNameTitle = C4::Context->preference("LibraryName");
313 $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
314 $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
315 # variables passed from CGI: opac_css_override and opac_search_limits.
316 my $opac_search_limit = $ENV{'OPAC_SEARCH_LIMIT'};
317 my $opac_limit_override = $ENV{'OPAC_LIMIT_OVERRIDE'};
318 my $mylibraryfirst = C4::Context->preference("SearchMyLibraryFirst");
320 if($opac_limit_override && ($opac_search_limit =~ /branch:(\w+)/) ){
321 $opac_name = C4::Branch::GetBranchName($1) # opac_search_limit is a branch, so we use it.
322 } elsif($mylibraryfirst){
323 $opac_name = C4::Branch::GetBranchName($mylibraryfirst);
326 AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"),
327 AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"),
328 LibraryName => "" . C4::Context->preference("LibraryName"),
329 LibraryNameTitle => "" . $LibraryNameTitle,
330 LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
331 OPACAmazonEnabled => C4::Context->preference("OPACAmazonEnabled"),
332 OPACAmazonCoverImages => C4::Context->preference("OPACAmazonCoverImages"),
333 OPACAmazonSimilarItems => "" . C4::Context->preference("OPACAmazonSimilarItems"),
334 OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
335 OPACItemHolds => C4::Context->preference("OPACItemHolds"),
336 OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"),
337 OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
338 OPACUserCSS => "". C4::Context->preference("OPACUserCSS"),
339 OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"),
340 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
341 OPACBaseURL => ($in->{'query'}->https() ? "https://" : "http://") . $ENV{'SERVER_NAME'} .
342 ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"),
343 opac_name => $opac_name,
344 opac_css_override => $ENV{'OPAC_CSS_OVERRIDE'},
345 opac_search_limit => $opac_search_limit,
346 opac_limit_override => $opac_limit_override,
347 OpacBrowser => C4::Context->preference("OpacBrowser"),
348 OpacCloud => C4::Context->preference("OpacCloud"),
349 OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"),
350 OpacNav => "" . C4::Context->preference("OpacNav"),
351 OpacPasswordChange => C4::Context->preference("OpacPasswordChange"),
352 OpacTopissue => C4::Context->preference("OpacTopissue"),
353 RequestOnOpac => C4::Context->preference("RequestOnOpac"),
354 TemplateEncoding => "". C4::Context->preference("TemplateEncoding"),
355 'Version' => C4::Context->preference('Version'),
356 XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"),
357 XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"),
358 hidelostitems => C4::Context->preference("hidelostitems"),
359 mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst")) ? C4::Context->userenv->{'branch'} : '',
360 opacbookbag => "" . C4::Context->preference("opacbookbag"),
361 opaccolorstylesheet => "". C4::Context->preference("opaccolorstylesheet"),
362 opaccredits => "" . C4::Context->preference("opaccredits"),
363 opacheader => "" . C4::Context->preference("opacheader"),
364 opaclanguagesdisplay => "". C4::Context->preference("opaclanguagesdisplay"),
365 opaclayoutstylesheet => "". C4::Context->preference("opaclayoutstylesheet"),
366 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
367 opacsmallimage => "" . C4::Context->preference("opacsmallimage"),
368 opacuserjs => C4::Context->preference("opacuserjs"),
369 opacuserlogin => "" . C4::Context->preference("opacuserlogin"),
370 reviewson => C4::Context->preference("reviewson"),
371 suggestion => "" . C4::Context->preference("suggestion"),
372 virtualshelves => "" . C4::Context->preference("virtualshelves"),
373 OPACSerialIssueDisplayCount => C4::Context->preference("OPACSerialIssueDisplayCount"),
376 return ( $template, $borrowernumber, $cookie, $flags);
381 ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
383 Verifies that the user is authorized to run this script. If
384 the user is authorized, a (userid, cookie, session-id, flags)
385 quadruple is returned. If the user is not authorized due to
386 insufficent privileges (see $flagsrequired below), it
387 displays an error page and exits. Otherwise, it displays the
388 login page and exits.
390 Note that C<&checkauth> will return if and only if the user
391 is authorized, so it should be called early on, before any
392 unfinished operations (e.g., if you've opened a file, then
393 C<&checkauth> won't close it for you).
395 C<$query> is the CGI object for the script calling C<&checkauth>.
397 The C<$noauth> argument is optional. If it is set, then no
398 authorization is required for the script.
400 C<&checkauth> fetches user and session information from C<$query> and
401 ensures that the user is authorized to run scripts that require
404 The C<$flagsrequired> argument specifies the required privileges
405 the user must have if the username and password are correct.
406 It should be specified as a reference-to-hash; keys in the hash
407 should be the "flags" for the user, as specified in the Members
408 intranet module. Any key specified must correspond to a "flag"
409 in the userflags table. E.g., { circulate => 1 } would specify
410 that the user must have the "circulate" privilege in order to
411 proceed. To make sure that access control is correct, the
412 C<$flagsrequired> parameter must be specified correctly.
414 If the GranularPermissions system preference is ON, the
415 value of each key in the C<flagsrequired> hash takes on an additional
420 The user must have access to all subfunctions of the module
421 specified by the hash key.
425 The user must have access to at least one subfunction of the module
426 specified by the hash key.
428 =item specific permission, e.g., 'export_catalog'
430 The user must have access to the specific subfunction list, which
431 must correspond to a row in the permissions table.
433 The C<$type> argument specifies whether the template should be
434 retrieved from the opac or intranet directory tree. "opac" is
435 assumed if it is not specified; however, if C<$type> is specified,
436 "intranet" is assumed if it is not "opac".
438 If C<$query> does not have a valid session ID associated with it
439 (i.e., the user has not logged in) or if the session has expired,
440 C<&checkauth> presents the user with a login page (from the point of
441 view of the original script, C<&checkauth> does not return). Once the
442 user has authenticated, C<&checkauth> restarts the original script
443 (this time, C<&checkauth> returns).
445 The login page is provided using a HTML::Template, which is set in the
446 systempreferences table or at the top of this file. The variable C<$type>
447 selects which template to use, either the opac or the intranet
448 authentification template.
450 C<&checkauth> returns a user ID, a cookie, and a session ID. The
451 cookie should be sent back to the browser; it verifies that the user
456 sub _version_check ($$) {
460 # If Version syspref is unavailable, it means Koha is beeing installed,
461 # and so we must redirect to OPAC maintenance page or to the WebInstaller
462 # also, if OpacMaintenance is ON, OPAC should redirect to maintenance
463 if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') {
464 warn "OPAC Install required, redirecting to maintenance";
465 print $query->redirect("/cgi-bin/koha/maintenance.pl");
467 unless ($version = C4::Context->preference('Version')) { # assignment, not comparison
468 if ($type ne 'opac') {
469 warn "Install required, redirecting to Installer";
470 print $query->redirect("/cgi-bin/koha/installer/install.pl");
473 warn "OPAC Install required, redirecting to maintenance";
474 print $query->redirect("/cgi-bin/koha/maintenance.pl");
479 # check that database and koha version are the same
480 # there is no DB version, it's a fresh install,
481 # go to web installer
482 # there is a DB version, compare it to the code version
483 my $kohaversion=C4::Context::KOHAVERSION;
484 # remove the 3 last . to have a Perl number
485 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
486 $debug and print STDERR "kohaversion : $kohaversion\n";
487 if ($version < $kohaversion){
488 my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion";
489 if ($type ne 'opac'){
490 warn sprintf($warning, 'Installer');
491 print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
493 warn sprintf("OPAC: " . $warning, 'maintenance');
494 print $query->redirect("/cgi-bin/koha/maintenance.pl");
502 open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
503 printf L join("\n",@_);
509 $debug and warn "Checking Auth";
510 # $authnotrequired will be set for scripts which will run without authentication
511 my $authnotrequired = shift;
512 my $flagsrequired = shift;
514 $type = 'opac' unless $type;
516 my $dbh = C4::Context->dbh;
517 my $timeout = C4::Context->preference('timeout');
519 if ($timeout =~ /(\d+)[dD]/) {
520 $timeout = $1 * 86400;
522 $timeout = 600 unless $timeout;
524 _version_check($type,$query);
528 my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves );
529 my $logout = $query->param('logout.x');
531 if ( $userid = $ENV{'REMOTE_USER'} ) {
532 # Using Basic Authentication, no cookies required
533 $cookie = $query->cookie(
534 -name => 'CGISESSID',
540 elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison
541 my $session = get_session($sessionID);
542 C4::Context->_new_userenv($sessionID);
543 my ($ip, $lasttime, $sessiontype);
545 C4::Context::set_userenv(
546 $session->param('number'), $session->param('id'),
547 $session->param('cardnumber'), $session->param('firstname'),
548 $session->param('surname'), $session->param('branch'),
549 $session->param('branchname'), $session->param('flags'),
550 $session->param('emailaddress'), $session->param('branchprinter')
552 C4::Context::set_shelves_userenv('bar',$session->param('barshelves'));
553 C4::Context::set_shelves_userenv('pub',$session->param('pubshelves'));
554 C4::Context::set_shelves_userenv('tot',$session->param('totshelves'));
555 $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
556 $ip = $session->param('ip');
557 $lasttime = $session->param('lasttime');
558 $userid = $session->param('id');
559 $sessiontype = $session->param('sessiontype');
562 if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) {
563 #if a user enters an id ne to the id in the current session, we need to log them in...
564 #first we need to clear the anonymous session...
565 $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id');
568 C4::Context->_unset_userenv($sessionID);
573 # voluntary logout the user
576 C4::Context->_unset_userenv($sessionID);
577 _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,localtime);
581 elsif ( $lasttime < time() - $timeout ) {
583 $info{'timed_out'} = 1;
585 C4::Context->_unset_userenv($sessionID);
586 _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,localtime);
590 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
591 # Different ip than originally logged in from
592 $info{'oldip'} = $ip;
593 $info{'newip'} = $ENV{'REMOTE_ADDR'};
594 $info{'different_ip'} = 1;
596 C4::Context->_unset_userenv($sessionID);
597 _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,localtime, $info{'newip'});
602 $cookie = $query->cookie( CGISESSID => $session->id );
603 $session->param('lasttime',time());
604 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...
605 $flags = haspermission($userid, $flagsrequired);
609 $info{'nopermission'} = 1;
614 unless ($userid || $sessionID) {
615 #we initiate a session prior to checking for a username to allow for anonymous sessions...
616 my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
617 my $sessionID = $session->id;
618 C4::Context->_new_userenv($sessionID);
619 $cookie = $query->cookie(CGISESSID => $sessionID);
620 if ( $userid = $query->param('userid') ) {
621 my $password = $query->param('password');
622 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
624 _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},localtime);
625 if ( $flags = haspermission($userid, $flagsrequired) ) {
629 $info{'nopermission'} = 1;
630 C4::Context->_unset_userenv($sessionID);
633 my ($borrowernumber, $firstname, $surname, $userflags,
634 $branchcode, $branchname, $branchprinter, $emailaddress);
636 if ( $return == 1 ) {
638 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode,
639 branches.branchname as branchname,
640 branches.branchprinter as branchprinter,
643 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
645 my $sth = $dbh->prepare("$select where userid=?");
646 $sth->execute($userid);
647 unless ($sth->rows) {
648 $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
649 $sth = $dbh->prepare("$select where cardnumber=?");
650 $sth->execute($cardnumber);
651 unless ($sth->rows) {
652 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
653 $sth->execute($userid);
654 unless ($sth->rows) {
655 $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
660 ($borrowernumber, $firstname, $surname, $userflags,
661 $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
662 $debug and print STDERR "AUTH_3 results: " .
663 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
665 print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
668 # launch a sequence to check if we have a ip for the branch, i
669 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
671 my $ip = $ENV{'REMOTE_ADDR'};
672 # if they specify at login, use that
673 if ($query->param('branch')) {
674 $branchcode = $query->param('branch');
675 $branchname = GetBranchName($branchcode);
677 my $branches = GetBranches();
678 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
679 # we have to check they are coming from the right ip range
680 my $domain = $branches->{$branchcode}->{'branchip'};
681 if ($ip !~ /^$domain/){
683 $info{'wrongip'} = 1;
688 foreach my $br ( keys %$branches ) {
689 # now we work with the treatment of ip
690 my $domain = $branches->{$br}->{'branchip'};
691 if ( $domain && $ip =~ /^$domain/ ) {
692 $branchcode = $branches->{$br}->{'branchcode'};
694 # new op dev : add the branchprinter and branchname in the cookie
695 $branchprinter = $branches->{$br}->{'branchprinter'};
696 $branchname = $branches->{$br}->{'branchname'};
699 $session->param('number',$borrowernumber);
700 $session->param('id',$userid);
701 $session->param('cardnumber',$cardnumber);
702 $session->param('firstname',$firstname);
703 $session->param('surname',$surname);
704 $session->param('branch',$branchcode);
705 $session->param('branchname',$branchname);
706 $session->param('flags',$userflags);
707 $session->param('emailaddress',$emailaddress);
708 $session->param('ip',$session->remote_addr());
709 $session->param('lasttime',time());
710 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
712 elsif ( $return == 2 ) {
713 #We suppose the user is the superlibrarian
715 $session->param('number',0);
716 $session->param('id',C4::Context->config('user'));
717 $session->param('cardnumber',C4::Context->config('user'));
718 $session->param('firstname',C4::Context->config('user'));
719 $session->param('surname',C4::Context->config('user'));
720 $session->param('branch','NO_LIBRARY_SET');
721 $session->param('branchname','NO_LIBRARY_SET');
722 $session->param('flags',1);
723 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
724 $session->param('ip',$session->remote_addr());
725 $session->param('lasttime',time());
727 C4::Context::set_userenv(
728 $session->param('number'), $session->param('id'),
729 $session->param('cardnumber'), $session->param('firstname'),
730 $session->param('surname'), $session->param('branch'),
731 $session->param('branchname'), $session->param('flags'),
732 $session->param('emailaddress'), $session->param('branchprinter')
735 # Grab borrower's shelves and public shelves and add them to the session
736 # $row_count determines how many records are returned from the db query
737 # and the number of lists to be displayed of each type in the 'Lists' button drop down
738 my $row_count = 10; # FIXME:This probably should be a syspref
739 my ($total, $totshelves, $barshelves, $pubshelves);
740 ($barshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(1, $row_count, $borrowernumber);
741 $total->{'bartotal'} = $totshelves;
742 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
743 $total->{'pubtotal'} = $totshelves;
744 $session->param('barshelves', $barshelves->[0]);
745 $session->param('pubshelves', $pubshelves->[0]);
746 $session->param('totshelves', $total);
748 C4::Context::set_shelves_userenv('bar',$barshelves->[0]);
749 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
750 C4::Context::set_shelves_userenv('tot',$total);
754 $info{'invalid_username_or_password'} = 1;
755 C4::Context->_unset_userenv($sessionID);
758 } # END if ( $userid = $query->param('userid') )
759 elsif ($type eq "opac") {
760 # if we are here this is an anonymous session; add public lists to it and a few other items...
761 # anonymous sessions are created only for the OPAC
762 $debug and warn "Initiating an anonymous session...";
764 # Grab the public shelves and add to the session...
765 my $row_count = 20; # FIXME:This probably should be a syspref
766 my ($total, $totshelves, $pubshelves);
767 ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef);
768 $total->{'pubtotal'} = $totshelves;
769 $session->param('pubshelves', $pubshelves->[0]);
770 $session->param('totshelves', $total);
771 C4::Context::set_shelves_userenv('pub',$pubshelves->[0]);
772 C4::Context::set_shelves_userenv('tot',$total);
774 # setting a couple of other session vars...
775 $session->param('ip',$session->remote_addr());
776 $session->param('lasttime',time());
777 $session->param('sessiontype','anon');
779 } # END unless ($userid)
780 my $insecure = C4::Context->boolean_preference('insecure');
782 # finished authentification, now respond
783 if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
787 $cookie = $query->cookie( CGISESSID => '' );
789 return ( $userid, $cookie, $sessionID, $flags );
794 # AUTH rejected, show the login/password template, after checking the DB.
798 # get the inputs from the incoming query
800 foreach my $name ( param $query) {
801 (next) if ( $name eq 'userid' || $name eq 'password' );
802 my $value = $query->param($name);
803 push @inputs, { name => $name, value => $value };
805 # get the branchloop, which we need for authentication
806 my $branches = GetBranches();
808 for my $branch_hash (sort keys %$branches) {
809 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
812 my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
813 my $template = gettemplate( $template_name, $type, $query );
814 $template->param(branchloop => \@branch_loop,);
818 suggestion => C4::Context->preference("suggestion"),
819 virtualshelves => C4::Context->preference("virtualshelves"),
820 LibraryName => C4::Context->preference("LibraryName"),
821 opacuserlogin => C4::Context->preference("opacuserlogin"),
822 OpacNav => C4::Context->preference("OpacNav"),
823 opaccredits => C4::Context->preference("opaccredits"),
824 opacreadinghistory => C4::Context->preference("opacreadinghistory"),
825 opacsmallimage => C4::Context->preference("opacsmallimage"),
826 opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
827 opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"),
828 opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
829 opacuserjs => C4::Context->preference("opacuserjs"),
830 opacbookbag => "" . C4::Context->preference("opacbookbag"),
831 OpacCloud => C4::Context->preference("OpacCloud"),
832 OpacTopissue => C4::Context->preference("OpacTopissue"),
833 OpacAuthorities => C4::Context->preference("OpacAuthorities"),
834 OpacBrowser => C4::Context->preference("OpacBrowser"),
835 opacheader => C4::Context->preference("opacheader"),
836 TagsEnabled => C4::Context->preference("TagsEnabled"),
837 OPACUserCSS => C4::Context->preference("OPACUserCSS"),
838 intranetcolorstylesheet =>
839 C4::Context->preference("intranetcolorstylesheet"),
840 intranetstylesheet => C4::Context->preference("intranetstylesheet"),
841 IntranetNav => C4::Context->preference("IntranetNav"),
842 intranetuserjs => C4::Context->preference("intranetuserjs"),
843 TemplateEncoding => C4::Context->preference("TemplateEncoding"),
844 IndependantBranches=> C4::Context->preference("IndependantBranches"),
845 AutoLocation => C4::Context->preference("AutoLocation"),
846 wrongip => $info{'wrongip'}
849 $template->param( loginprompt => 1 ) unless $info{'nopermission'};
851 my $self_url = $query->url( -absolute => 1 );
854 LibraryName => C4::Context->preference("LibraryName"),
856 $template->param( \%info );
857 # $cookie = $query->cookie(CGISESSID => $session->id
859 print $query->header(
860 -type => 'text/html',
870 ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
872 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
873 cookie, determine if the user has the privileges specified by C<$userflags>.
875 C<check_api_auth> is is meant for authenticating users of web services, and
876 consequently will always return and will not attempt to redirect the user
879 If a valid session cookie is already present, check_api_auth will return a status
880 of "ok", the cookie, and the Koha session ID.
882 If no session cookie is present, check_api_auth will check the 'userid' and 'password
883 parameters and create a session cookie and Koha session if the supplied credentials
886 Possible return values in C<$status> are:
890 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
892 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
894 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
896 =item "expired -- session cookie has expired; API user should resubmit userid and password
904 my $flagsrequired = shift;
906 my $dbh = C4::Context->dbh;
907 my $timeout = C4::Context->preference('timeout');
908 $timeout = 600 unless $timeout;
910 unless (C4::Context->preference('Version')) {
911 # database has not been installed yet
912 return ("maintenance", undef, undef);
914 my $kohaversion=C4::Context::KOHAVERSION;
915 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
916 if (C4::Context->preference('Version') < $kohaversion) {
917 # database in need of version update; assume that
918 # no API should be called while databsae is in
920 return ("maintenance", undef, undef);
923 # FIXME -- most of what follows is a copy-and-paste
924 # of code from checkauth. There is an obvious need
925 # for refactoring to separate the various parts of
926 # the authentication code, but as of 2007-11-19 this
927 # is deferred so as to not introduce bugs into the
928 # regular authentication code for Koha 3.0.
930 # see if we have a valid session cookie already
931 # however, if a userid parameter is present (i.e., from
932 # a form submission, assume that any current cookie
934 my $sessionID = undef;
935 unless ($query->param('userid')) {
936 $sessionID = $query->cookie("CGISESSID");
939 my $session = get_session($sessionID);
940 C4::Context->_new_userenv($sessionID);
942 C4::Context::set_userenv(
943 $session->param('number'), $session->param('id'),
944 $session->param('cardnumber'), $session->param('firstname'),
945 $session->param('surname'), $session->param('branch'),
946 $session->param('branchname'), $session->param('flags'),
947 $session->param('emailaddress'), $session->param('branchprinter')
950 my $ip = $session->param('ip');
951 my $lasttime = $session->param('lasttime');
952 my $userid = $session->param('id');
953 if ( $lasttime < time() - $timeout ) {
956 C4::Context->_unset_userenv($sessionID);
959 return ("expired", undef, undef);
960 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
963 C4::Context->_unset_userenv($sessionID);
966 return ("expired", undef, undef);
968 my $cookie = $query->cookie( CGISESSID => $session->id );
969 $session->param('lasttime',time());
970 my $flags = haspermission($userid, $flagsrequired);
972 return ("ok", $cookie, $sessionID);
975 C4::Context->_unset_userenv($sessionID);
978 return ("failed", undef, undef);
982 return ("expired", undef, undef);
986 my $userid = $query->param('userid');
987 my $password = $query->param('password');
988 unless ($userid and $password) {
989 # caller did something wrong, fail the authenticateion
990 return ("failed", undef, undef);
992 my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
993 if ($return and haspermission($userid, $flagsrequired)) {
994 my $session = get_session("");
995 return ("failed", undef, undef) unless $session;
997 my $sessionID = $session->id;
998 C4::Context->_new_userenv($sessionID);
999 my $cookie = $query->cookie(CGISESSID => $sessionID);
1000 if ( $return == 1 ) {
1002 $borrowernumber, $firstname, $surname,
1003 $userflags, $branchcode, $branchname,
1004 $branchprinter, $emailaddress
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 userid=?"
1010 $sth->execute($userid);
1012 $borrowernumber, $firstname, $surname,
1013 $userflags, $branchcode, $branchname,
1014 $branchprinter, $emailaddress
1015 ) = $sth->fetchrow if ( $sth->rows );
1017 unless ($sth->rows ) {
1018 my $sth = $dbh->prepare(
1019 "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=?"
1021 $sth->execute($cardnumber);
1023 $borrowernumber, $firstname, $surname,
1024 $userflags, $branchcode, $branchname,
1025 $branchprinter, $emailaddress
1026 ) = $sth->fetchrow if ( $sth->rows );
1028 unless ( $sth->rows ) {
1029 $sth->execute($userid);
1031 $borrowernumber, $firstname, $surname, $userflags,
1032 $branchcode, $branchname, $branchprinter, $emailaddress
1033 ) = $sth->fetchrow if ( $sth->rows );
1037 my $ip = $ENV{'REMOTE_ADDR'};
1038 # if they specify at login, use that
1039 if ($query->param('branch')) {
1040 $branchcode = $query->param('branch');
1041 $branchname = GetBranchName($branchcode);
1043 my $branches = GetBranches();
1045 foreach my $br ( keys %$branches ) {
1046 # now we work with the treatment of ip
1047 my $domain = $branches->{$br}->{'branchip'};
1048 if ( $domain && $ip =~ /^$domain/ ) {
1049 $branchcode = $branches->{$br}->{'branchcode'};
1051 # new op dev : add the branchprinter and branchname in the cookie
1052 $branchprinter = $branches->{$br}->{'branchprinter'};
1053 $branchname = $branches->{$br}->{'branchname'};
1056 $session->param('number',$borrowernumber);
1057 $session->param('id',$userid);
1058 $session->param('cardnumber',$cardnumber);
1059 $session->param('firstname',$firstname);
1060 $session->param('surname',$surname);
1061 $session->param('branch',$branchcode);
1062 $session->param('branchname',$branchname);
1063 $session->param('flags',$userflags);
1064 $session->param('emailaddress',$emailaddress);
1065 $session->param('ip',$session->remote_addr());
1066 $session->param('lasttime',time());
1067 } elsif ( $return == 2 ) {
1068 #We suppose the user is the superlibrarian
1069 $session->param('number',0);
1070 $session->param('id',C4::Context->config('user'));
1071 $session->param('cardnumber',C4::Context->config('user'));
1072 $session->param('firstname',C4::Context->config('user'));
1073 $session->param('surname',C4::Context->config('user'));
1074 $session->param('branch','NO_LIBRARY_SET');
1075 $session->param('branchname','NO_LIBRARY_SET');
1076 $session->param('flags',1);
1077 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
1078 $session->param('ip',$session->remote_addr());
1079 $session->param('lasttime',time());
1081 C4::Context::set_userenv(
1082 $session->param('number'), $session->param('id'),
1083 $session->param('cardnumber'), $session->param('firstname'),
1084 $session->param('surname'), $session->param('branch'),
1085 $session->param('branchname'), $session->param('flags'),
1086 $session->param('emailaddress'), $session->param('branchprinter')
1088 return ("ok", $cookie, $sessionID);
1090 return ("failed", undef, undef);
1095 =item check_cookie_auth
1097 ($status, $sessionId) = check_api_auth($cookie, $userflags);
1099 Given a CGISESSID cookie set during a previous login to Koha, determine
1100 if the user has the privileges specified by C<$userflags>.
1102 C<check_cookie_auth> is meant for authenticating special services
1103 such as tools/upload-file.pl that are invoked by other pages that
1104 have been authenticated in the usual way.
1106 Possible return values in C<$status> are:
1110 =item "ok" -- user authenticated; C<$sessionID> have valid values.
1112 =item "failed" -- credentials are not correct; C<$sessionid> are undef
1114 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
1116 =item "expired -- session cookie has expired; API user should resubmit userid and password
1122 sub check_cookie_auth {
1124 my $flagsrequired = shift;
1126 my $dbh = C4::Context->dbh;
1127 my $timeout = C4::Context->preference('timeout');
1128 $timeout = 600 unless $timeout;
1130 unless (C4::Context->preference('Version')) {
1131 # database has not been installed yet
1132 return ("maintenance", undef);
1134 my $kohaversion=C4::Context::KOHAVERSION;
1135 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1136 if (C4::Context->preference('Version') < $kohaversion) {
1137 # database in need of version update; assume that
1138 # no API should be called while databsae is in
1140 return ("maintenance", undef);
1143 # FIXME -- most of what follows is a copy-and-paste
1144 # of code from checkauth. There is an obvious need
1145 # for refactoring to separate the various parts of
1146 # the authentication code, but as of 2007-11-23 this
1147 # is deferred so as to not introduce bugs into the
1148 # regular authentication code for Koha 3.0.
1150 # see if we have a valid session cookie already
1151 # however, if a userid parameter is present (i.e., from
1152 # a form submission, assume that any current cookie
1154 unless (defined $cookie and $cookie) {
1155 return ("failed", undef);
1157 my $sessionID = $cookie;
1158 my $session = get_session($sessionID);
1159 C4::Context->_new_userenv($sessionID);
1161 C4::Context::set_userenv(
1162 $session->param('number'), $session->param('id'),
1163 $session->param('cardnumber'), $session->param('firstname'),
1164 $session->param('surname'), $session->param('branch'),
1165 $session->param('branchname'), $session->param('flags'),
1166 $session->param('emailaddress'), $session->param('branchprinter')
1169 my $ip = $session->param('ip');
1170 my $lasttime = $session->param('lasttime');
1171 my $userid = $session->param('id');
1172 if ( $lasttime < time() - $timeout ) {
1175 C4::Context->_unset_userenv($sessionID);
1178 return ("expired", undef);
1179 } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1180 # IP address changed
1182 C4::Context->_unset_userenv($sessionID);
1185 return ("expired", undef);
1187 $session->param('lasttime',time());
1188 my $flags = haspermission($userid, $flagsrequired);
1190 return ("ok", $sessionID);
1193 C4::Context->_unset_userenv($sessionID);
1196 return ("failed", undef);
1200 return ("expired", undef);
1207 my $session = get_session($sessionID);
1209 Given a session ID, retrieve the CGI::Session object used to store
1210 the session's state. The session object can be used to store
1211 data that needs to be accessed by different scripts during a
1214 If the C<$sessionID> parameter is an empty string, a new session
1220 my $sessionID = shift;
1221 my $storage_method = C4::Context->preference('SessionStorage');
1222 my $dbh = C4::Context->dbh;
1224 if ($storage_method eq 'mysql'){
1225 $session = new CGI::Session("driver:MySQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1227 elsif ($storage_method eq 'Pg') {
1228 $session = new CGI::Session("driver:PostgreSQL;serializer:yaml;id:md5", $sessionID, {Handle=>$dbh});
1231 # catch all defaults to tmp should work on all systems
1232 $session = new CGI::Session("driver:File;serializer:yaml;id:md5", $sessionID, {Directory=>'/tmp'});
1239 my ( $dbh, $userid, $password ) = @_;
1241 $debug and print STDERR "## checkpw - checking LDAP\n";
1242 my ($retval,$retcard) = checkpw_ldap(@_); # EXTERNAL AUTH
1243 ($retval) and return ($retval,$retcard);
1249 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1251 $sth->execute($userid);
1253 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1254 $surname, $branchcode, $flags )
1256 if ( md5_base64($password) eq $md5password ) {
1258 C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1259 $firstname, $surname, $branchcode, $flags );
1260 return 1, $cardnumber;
1265 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1267 $sth->execute($userid);
1269 my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1270 $surname, $branchcode, $flags )
1272 if ( md5_base64($password) eq $md5password ) {
1274 C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1275 $firstname, $surname, $branchcode, $flags );
1279 if ( $userid && $userid eq C4::Context->config('user')
1280 && "$password" eq C4::Context->config('pass') )
1283 # Koha superuser account
1284 # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1287 if ( $userid && $userid eq 'demo'
1288 && "$password" eq 'demo'
1289 && C4::Context->config('demo') )
1292 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1293 # some features won't be effective : modify systempref, modify MARC structure,
1301 my $authflags = getuserflags($flags, $userid, [$dbh]);
1303 Translates integer flags into permissions strings hash.
1305 C<$flags> is the integer userflags value ( borrowers.userflags )
1306 C<$userid> is the members.userid, used for building subpermissions
1307 C<$authflags> is a hashref of permissions
1314 my $dbh = @_ ? shift : C4::Context->dbh;
1316 $flags = 0 unless $flags;
1317 my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1320 while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1321 if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1322 $userflags->{$flag} = 1;
1325 $userflags->{$flag} = 0;
1329 # get subpermissions and merge with top-level permissions
1330 my $user_subperms = get_user_subpermissions($userid);
1331 foreach my $module (keys %$user_subperms) {
1332 next if $userflags->{$module} == 1; # user already has permission for everything in this module
1333 $userflags->{$module} = $user_subperms->{$module};
1339 =item get_user_subpermissions
1343 my $user_perm_hashref = get_user_subpermissions($userid);
1347 Given the userid (note, not the borrowernumber) of a staff user,
1348 return a hashref of hashrefs of the specific subpermissions
1349 accorded to the user. An example return is
1353 export_catalog => 1,
1354 import_patrons => 1,
1358 The top-level hash-key is a module or function code from
1359 userflags.flag, while the second-level key is a code
1362 The results of this function do not give a complete picture
1363 of the functions that a staff user can access; it is also
1364 necessary to check borrowers.flags.
1368 sub get_user_subpermissions {
1371 my $dbh = C4::Context->dbh;
1372 my $sth = $dbh->prepare("SELECT flag, user_permissions.code
1373 FROM user_permissions
1374 JOIN permissions USING (module_bit, code)
1375 JOIN userflags ON (module_bit = bit)
1376 JOIN borrowers USING (borrowernumber)
1378 $sth->execute($userid);
1380 my $user_perms = {};
1381 while (my $perm = $sth->fetchrow_hashref) {
1382 $user_perms->{$perm->{'flag'}}->{$perm->{'code'}} = 1;
1387 =item get_all_subpermissions
1391 my $perm_hashref = get_all_subpermissions();
1395 Returns a hashref of hashrefs defining all specific
1396 permissions currently defined. The return value
1397 has the same structure as that of C<get_user_subpermissions>,
1398 except that the innermost hash value is the description
1399 of the subpermission.
1403 sub get_all_subpermissions {
1404 my $dbh = C4::Context->dbh;
1405 my $sth = $dbh->prepare("SELECT flag, code, description
1407 JOIN userflags ON (module_bit = bit)");
1411 while (my $perm = $sth->fetchrow_hashref) {
1412 $all_perms->{$perm->{'flag'}}->{$perm->{'code'}} = $perm->{'description'};
1419 $flags = ($userid, $flagsrequired);
1421 C<$userid> the userid of the member
1422 C<$flags> is a hashref of required flags like C<$borrower-<{authflags}>
1424 Returns member's flags or 0 if a permission is not met.
1429 my ($userid, $flagsrequired) = @_;
1430 my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1431 $sth->execute($userid);
1432 my $flags = getuserflags( $sth->fetchrow(), $userid );
1433 if ( $userid eq C4::Context->config('user') ) {
1434 # Super User Account from /etc/koha.conf
1435 $flags->{'superlibrarian'} = 1;
1437 elsif ( $userid eq 'demo' && C4::Context->config('demo') ) {
1438 # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1439 $flags->{'superlibrarian'} = 1;
1441 return $flags if $flags->{superlibrarian};
1442 foreach my $module ( keys %$flagsrequired ) {
1443 if (C4::Context->preference('GranularPermissions')) {
1444 my $subperm = $flagsrequired->{$module};
1445 if ($subperm eq '*') {
1446 return 0 unless ( $flags->{$module} == 1 or ref($flags->{$module}) );
1448 return 0 unless ( $flags->{$module} == 1 or
1449 ( ref($flags->{$module}) and
1450 exists $flags->{$module}->{$subperm} and
1451 $flags->{$module}->{$subperm} == 1
1456 return 0 unless ( $flags->{$module} );
1460 #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1464 sub getborrowernumber {
1466 my $dbh = C4::Context->dbh;
1467 for my $field ( 'userid', 'cardnumber' ) {
1469 $dbh->prepare("select borrowernumber from borrowers where $field=?");
1470 $sth->execute($userid);
1472 my ($bnumber) = $sth->fetchrow;
1479 END { } # module clean-up code here (global destructor)