6094 Fixing ModAuthority problems
[koha.git] / installer / InstallAuth.pm
1 # -*- tab-width: 8 -*-
2 # NOTE: This file uses 8-character tabs; do not change the tab size!
3
4 package InstallAuth;
5
6 # Copyright 2000-2002 Katipo Communications
7 #
8 # This file is part of Koha.
9 #
10 # Koha is free software; you can redistribute it and/or modify it under the
11 # terms of the GNU General Public License as published by the Free Software
12 # Foundation; either version 2 of the License, or (at your option) any later
13 # version.
14 #
15 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
16 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License along
20 # with Koha; if not, write to the Free Software Foundation, Inc.,
21 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
23 use strict;
24 #use warnings; FIXME - Bug 2505
25 use Digest::MD5 qw(md5_base64);
26
27 require Exporter;
28 use C4::Context;
29 use C4::Output;
30 use C4::Templates;
31 use C4::Koha;
32 use CGI::Session;
33
34 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
35
36 # set the version for version checking
37 $VERSION = 3.00;
38
39 =head1 NAME
40
41 InstallAuth - Authenticates Koha users for Install process
42
43 =head1 SYNOPSIS
44
45   use CGI;
46   use InstallAuth;
47   use C4::Output;
48
49   my $query = new CGI;
50
51   my ($template, $borrowernumber, $cookie) 
52     = get_template_and_user({template_name   => "opac-main.tmpl",
53                              query           => $query,
54                              type            => "opac",
55                              authnotrequired => 1,
56                              flagsrequired   => {borrow => 1},
57                           });
58
59   output_html_with_http_headers $query, $cookie, $template->output;
60
61 =head1 DESCRIPTION
62
63     The main function of this module is to provide
64     authentification. However the get_template_and_user function has
65     been provided so that a users login information is passed along
66     automatically. This gets loaded into the template.
67     This package is different from C4::Auth in so far as 
68     C4::Auth uses many preferences which are supposed NOT to be obtainable when installing the database.
69     
70     As in C4::Auth, Authentication is based on cookies.
71
72 =head1 FUNCTIONS
73
74 =over 2
75
76 =cut
77
78 @ISA    = qw(Exporter);
79 @EXPORT = qw(
80   &checkauth
81   &get_template_and_user
82 );
83
84 =item get_template_and_user
85
86   my ($template, $borrowernumber, $cookie)
87     = get_template_and_user({template_name   => "opac-main.tmpl",
88                              query           => $query,
89                              type            => "opac",
90                              authnotrequired => 1,
91                              flagsrequired   => {borrow => 1},
92                           });
93
94     This call passes the C<query>, C<flagsrequired> and C<authnotrequired>
95     to C<&checkauth> (in this module) to perform authentification.
96     See C<&checkauth> for an explanation of these parameters.
97
98     The C<template_name> is then used to find the correct template for
99     the page. The authenticated users details are loaded onto the
100     template in the HTML::Template LOOP variable C<USER_INFO>. Also the
101     C<sessionID> is passed to the template. This can be used in templates
102     if cookies are disabled. It needs to be put as and input to every
103     authenticated page.
104
105     More information on the C<gettemplate> sub can be found in the
106     Templates.pm module.
107
108 =cut
109
110 sub get_template_and_user {
111     my $in       = shift;
112     my $query    = $in->{'query'};
113     my $language =_get_template_language($query->cookie('KohaOpacLanguage'));
114     my $path     = C4::Context->config('intrahtdocs'). "/prog/". $language;
115
116     my $tmplbase = $in->{template_name};
117     $tmplbase=~ s/\.tmpl$/.tt/;
118     my $filename = "$path/modules/" . $tmplbase;
119     my $interface = 'intranet';
120     my $template = C4::Templates->new( $interface, $filename, $tmplbase);
121     
122     my ( $user, $cookie, $sessionID, $flags ) = checkauth(
123         $in->{'query'},
124         $in->{'authnotrequired'},
125         $in->{'flagsrequired'},
126         $in->{'type'}
127     );
128
129     #     use Data::Dumper;warn "utilisateur $user cookie : ".Dumper($cookie);
130
131     my $borrowernumber;
132     if ($user) {
133         $template->param( loggedinusername => $user );
134         $template->param( sessionID        => $sessionID );
135
136         # We are going to use the $flags returned by checkauth
137         # to create the template's parameters that will indicate
138         # which menus the user can access.
139         if ( ( $flags && $flags->{superlibrarian} == 1 ) ) {
140             $template->param( CAN_user_circulate        => 1 );
141             $template->param( CAN_user_catalogue        => 1 );
142             $template->param( CAN_user_parameters       => 1 );
143             $template->param( CAN_user_borrowers        => 1 );
144             $template->param( CAN_user_permission       => 1 );
145             $template->param( CAN_user_reserveforothers => 1 );
146             $template->param( CAN_user_borrow           => 1 );
147             $template->param( CAN_user_editcatalogue    => 1 );
148             $template->param( CAN_user_updatecharges    => 1 );
149             $template->param( CAN_user_acquisition      => 1 );
150             $template->param( CAN_user_management       => 1 );
151             $template->param( CAN_user_tools            => 1 );
152             $template->param( CAN_user_editauthorities  => 1 );
153             $template->param( CAN_user_serials          => 1 );
154             $template->param( CAN_user_reports          => 1 );
155         }
156     }
157     return ( $template, $borrowernumber, $cookie );
158 }
159
160 sub _get_template_language {
161   #verify if opac language exists in staff (bug 5660)
162   #conditions are 1) dir exists and 2) enabled in prefs
163   my ($opaclang)= @_;
164   return 'en' unless $opaclang;
165   my $path= C4::Context->config('intrahtdocs')."/prog/$opaclang";
166   -d $path ? $opaclang : 'en';
167 }
168
169 =item checkauth
170
171   ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
172
173 Verifies that the user is authorized to run this script.  If
174 the user is authorized, a (userid, cookie, session-id, flags)
175 quadruple is returned.  If the user is not authorized but does
176 not have the required privilege (see $flagsrequired below), it
177 displays an error page and exits.  Otherwise, it displays the
178 login page and exits.
179
180 Note that C<&checkauth> will return if and only if the user
181 is authorized, so it should be called early on, before any
182 unfinished operations (e.g., if you've opened a file, then
183 C<&checkauth> won't close it for you).
184
185 C<$query> is the CGI object for the script calling C<&checkauth>.
186
187 The C<$noauth> argument is optional. If it is set, then no
188 authorization is required for the script.
189
190 C<&checkauth> fetches user and session information from C<$query> and
191 ensures that the user is authorized to run scripts that require
192 authorization.
193
194 The C<$flagsrequired> argument specifies the required privileges
195 the user must have if the username and password are correct.
196 It should be specified as a reference-to-hash; keys in the hash
197 should be the "flags" for the user, as specified in the Members
198 intranet module. Any key specified must correspond to a "flag"
199 in the userflags table. E.g., { circulate => 1 } would specify
200 that the user must have the "circulate" privilege in order to
201 proceed. To make sure that access control is correct, the
202 C<$flagsrequired> parameter must be specified correctly.
203
204 The C<$type> argument specifies whether the template should be
205 retrieved from the opac or intranet directory tree.  "opac" is
206 assumed if it is not specified; however, if C<$type> is specified,
207 "intranet" is assumed if it is not "opac".
208
209 If C<$query> does not have a valid session ID associated with it
210 (i.e., the user has not logged in) or if the session has expired,
211 C<&checkauth> presents the user with a login page (from the point of
212 view of the original script, C<&checkauth> does not return). Once the
213 user has authenticated, C<&checkauth> restarts the original script
214 (this time, C<&checkauth> returns).
215
216 The login page is provided using a HTML::Template, which is set in the
217 systempreferences table or at the top of this file. The variable C<$type>
218 selects which template to use, either the opac or the intranet 
219 authentification template.
220
221 C<&checkauth> returns a user ID, a cookie, and a session ID. The
222 cookie should be sent back to the browser; it verifies that the user
223 has authenticated.
224
225 =cut
226
227 sub checkauth {
228     my $query = shift;
229
230 # $authnotrequired will be set for scripts which will run without authentication
231     my $authnotrequired = shift;
232     my $flagsrequired   = shift;
233     my $type            = shift;
234     $type = 'intranet' unless $type;
235
236     my $dbh = C4::Context->dbh();
237     my $template_name;
238     $template_name = "installer/auth.tmpl";
239
240     # state variables
241     my $loggedin = 0;
242     my %info;
243     my ( $userid, $cookie, $sessionID, $flags, $envcookie );
244     my $logout = $query->param('logout.x');
245     if ( $sessionID = $query->cookie("CGISESSID") ) {
246         C4::Context->_new_userenv($sessionID);
247         my $session =
248           new CGI::Session( "driver:File;serializer:yaml", $sessionID,
249             { Directory => '/tmp' } );
250         if ( $session->param('cardnumber') ) {
251             C4::Context::set_userenv(
252                 $session->param('number'),
253                 $session->param('id'),
254                 $session->param('cardnumber'),
255                 $session->param('firstname'),
256                 $session->param('surname'),
257                 $session->param('branch'),
258                 $session->param('branchname'),
259                 $session->param('flags'),
260                 $session->param('emailaddress'),
261                 $session->param('branchprinter')
262             );
263             $cookie   = $query->cookie( CGISESSID => $session->id );
264             $loggedin = 1;
265             $userid   = $session->param('cardnumber');
266         }
267         my ( $ip, $lasttime );
268
269         if ($logout) {
270
271             # voluntary logout the user
272             C4::Context->_unset_userenv($sessionID);
273             $sessionID = undef;
274             $userid    = undef;
275             open L, ">>/tmp/sessionlog";
276             my $time = localtime( time() );
277             printf L "%20s from %16s logged out at %30s (manually).\n", $userid,
278               $ip, $time;
279             close L;
280         }
281     }
282     unless ($userid) {
283         my $session =
284           new CGI::Session( "driver:File;serializer:yaml", undef, { Directory => '/tmp' } );
285         $sessionID = $session->id;
286         $userid    = $query->param('userid');
287         C4::Context->_new_userenv($sessionID);
288         my $password = $query->param('password');
289         C4::Context->_new_userenv($sessionID);
290         my ( $return, $cardnumber ) = checkpw( $userid, $password );
291         if ($return) {
292             $loggedin = 1;
293             open L, ">>/tmp/sessionlog";
294             my $time = localtime( time() );
295             printf L "%20s from %16s logged in  at %30s.\n", $userid,
296               $ENV{'REMOTE_ADDR'}, $time;
297             close L;
298             $cookie = $query->cookie( CGISESSID => $sessionID );
299             if ( $return == 2 ) {
300
301            #Only superlibrarian should have access to this page.
302            #Since if it is a user, it is supposed that there is a borrower table
303            #And thus that data structure is loaded.
304                 my $hash = C4::Context::set_userenv(
305                     0,                           0,
306                     C4::Context->config('user'), C4::Context->config('user'),
307                     C4::Context->config('user'), "",
308                     "NO_LIBRARY_SET",            1,
309                     ""
310                 );
311                 $session->param( 'number',     0 );
312                 $session->param( 'id',         C4::Context->config('user') );
313                 $session->param( 'cardnumber', C4::Context->config('user') );
314                 $session->param( 'firstname',  C4::Context->config('user') );
315                 $session->param( 'surname',    C4::Context->config('user'), );
316                 $session->param( 'branch',     'NO_LIBRARY_SET' );
317                 $session->param( 'branchname', 'NO_LIBRARY_SET' );
318                 $session->param( 'flags',      1 );
319                 $session->param( 'emailaddress',
320                     C4::Context->preference('KohaAdminEmailAddress') );
321                 $session->param( 'ip',       $session->remote_addr() );
322                 $session->param( 'lasttime', time() );
323                 $userid = C4::Context->config('user');
324             }
325         }
326         else {
327             if ($userid) {
328                 $info{'invalid_username_or_password'} = 1;
329                 C4::Context->_unset_userenv($sessionID);
330             }
331         }
332     }
333
334     # finished authentification, now respond
335     if ($loggedin) {
336
337         # successful login
338         unless ($cookie) {
339             $cookie = $query->cookie(
340                 -name    => 'CGISESSID',
341                 -value   => '',
342                 -expires => ''
343             );
344         }
345         if ($envcookie) {
346             return ( $userid, [ $cookie, $envcookie ], $sessionID, $flags );
347         }
348         else {
349             return ( $userid, $cookie, $sessionID, $flags );
350         }
351     }
352
353     # else we have a problem...
354     # get the inputs from the incoming query
355     my @inputs = ();
356     foreach my $name ( param $query) {
357         (next) if ( $name eq 'userid' || $name eq 'password' );
358         my $value = $query->param($name);
359         push @inputs, { name => $name, value => $value };
360     }
361
362     my $path =
363       C4::Context->config('intrahtdocs') . "/prog/"
364       . ( $query->param('language') ? $query->param('language') : "en" );
365     my $filename = "$path/modules/$template_name";
366     $filename =~ s/\.tmpl$/.tt/;
367     my $interface = 'intranet';
368     my $template = C4::Templates->new( $interface, $filename);
369     $template->param(
370         INPUTS => \@inputs,
371
372     );
373     $template->param( login => 1 );
374     $template->param( loginprompt => 1 ) unless $info{'nopermission'};
375
376     my $self_url = $query->url( -absolute => 1 );
377     $template->param( url => $self_url, );
378     $template->param( \%info );
379     $cookie = $query->cookie(
380         -name    => 'CGISESSID',
381         -value   => $sessionID,
382         -expires => ''
383     );
384     print $query->header(
385         -type    => 'text/html; charset=utf-8',
386         -cookie  => $cookie
387       ),
388       $template->output;
389     exit;
390 }
391
392 sub checkpw {
393
394     my ( $userid, $password ) = @_;
395
396     if (   $userid
397         && $userid     eq C4::Context->config('user')
398         && "$password" eq C4::Context->config('pass') )
399     {
400
401         # Koha superuser account
402         C4::Context->set_userenv(
403             0, 0,
404             C4::Context->config('user'),
405             C4::Context->config('user'),
406             C4::Context->config('user'),
407             "", 1
408         );
409         return 2;
410     }
411     if (   $userid
412         && $userid     eq 'demo'
413         && "$password" eq 'demo'
414         && C4::Context->config('demo') )
415     {
416
417 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
418 # some features won't be effective : modify systempref, modify MARC structure,
419         return 2;
420     }
421     return 0;
422 }
423
424 END { }    # module clean-up code here (global destructor)
425 1;
426 __END__
427
428 =back
429
430 =head1 SEE ALSO
431
432 CGI(3)
433
434 C4::Output(3)
435
436 Digest::MD5(3)
437
438 =cut