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