Merge branch 'new/bug_6479' into kcmaster
[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     Output.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   my $pref= ','.C4::Context->preference('language').',';
167   if(-d $path) {
168     if($pref =~ /,$opaclang,/ ) {
169       return $opaclang;
170     }
171   }
172   return 'en';
173 }
174
175 =item checkauth
176
177   ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
178
179 Verifies that the user is authorized to run this script.  If
180 the user is authorized, a (userid, cookie, session-id, flags)
181 quadruple is returned.  If the user is not authorized but does
182 not have the required privilege (see $flagsrequired below), it
183 displays an error page and exits.  Otherwise, it displays the
184 login page and exits.
185
186 Note that C<&checkauth> will return if and only if the user
187 is authorized, so it should be called early on, before any
188 unfinished operations (e.g., if you've opened a file, then
189 C<&checkauth> won't close it for you).
190
191 C<$query> is the CGI object for the script calling C<&checkauth>.
192
193 The C<$noauth> argument is optional. If it is set, then no
194 authorization is required for the script.
195
196 C<&checkauth> fetches user and session information from C<$query> and
197 ensures that the user is authorized to run scripts that require
198 authorization.
199
200 The C<$flagsrequired> argument specifies the required privileges
201 the user must have if the username and password are correct.
202 It should be specified as a reference-to-hash; keys in the hash
203 should be the "flags" for the user, as specified in the Members
204 intranet module. Any key specified must correspond to a "flag"
205 in the userflags table. E.g., { circulate => 1 } would specify
206 that the user must have the "circulate" privilege in order to
207 proceed. To make sure that access control is correct, the
208 C<$flagsrequired> parameter must be specified correctly.
209
210 The C<$type> argument specifies whether the template should be
211 retrieved from the opac or intranet directory tree.  "opac" is
212 assumed if it is not specified; however, if C<$type> is specified,
213 "intranet" is assumed if it is not "opac".
214
215 If C<$query> does not have a valid session ID associated with it
216 (i.e., the user has not logged in) or if the session has expired,
217 C<&checkauth> presents the user with a login page (from the point of
218 view of the original script, C<&checkauth> does not return). Once the
219 user has authenticated, C<&checkauth> restarts the original script
220 (this time, C<&checkauth> returns).
221
222 The login page is provided using a HTML::Template, which is set in the
223 systempreferences table or at the top of this file. The variable C<$type>
224 selects which template to use, either the opac or the intranet 
225 authentification template.
226
227 C<&checkauth> returns a user ID, a cookie, and a session ID. The
228 cookie should be sent back to the browser; it verifies that the user
229 has authenticated.
230
231 =cut
232
233 sub checkauth {
234     my $query = shift;
235
236 # $authnotrequired will be set for scripts which will run without authentication
237     my $authnotrequired = shift;
238     my $flagsrequired   = shift;
239     my $type            = shift;
240     $type = 'intranet' unless $type;
241
242     my $dbh = C4::Context->dbh();
243     my $template_name;
244     $template_name = "installer/auth.tmpl";
245
246     # state variables
247     my $loggedin = 0;
248     my %info;
249     my ( $userid, $cookie, $sessionID, $flags, $envcookie );
250     my $logout = $query->param('logout.x');
251     if ( $sessionID = $query->cookie("CGISESSID") ) {
252         C4::Context->_new_userenv($sessionID);
253         my $session =
254           new CGI::Session( "driver:File;serializer:yaml", $sessionID,
255             { Directory => '/tmp' } );
256         if ( $session->param('cardnumber') ) {
257             C4::Context::set_userenv(
258                 $session->param('number'),
259                 $session->param('id'),
260                 $session->param('cardnumber'),
261                 $session->param('firstname'),
262                 $session->param('surname'),
263                 $session->param('branch'),
264                 $session->param('branchname'),
265                 $session->param('flags'),
266                 $session->param('emailaddress'),
267                 $session->param('branchprinter')
268             );
269             $cookie   = $query->cookie( CGISESSID => $session->id );
270             $loggedin = 1;
271             $userid   = $session->param('cardnumber');
272         }
273         my ( $ip, $lasttime );
274
275         if ($logout) {
276
277             # voluntary logout the user
278             C4::Context->_unset_userenv($sessionID);
279             $sessionID = undef;
280             $userid    = undef;
281             open L, ">>/tmp/sessionlog";
282             my $time = localtime( time() );
283             printf L "%20s from %16s logged out at %30s (manually).\n", $userid,
284               $ip, $time;
285             close L;
286         }
287     }
288     unless ($userid) {
289         my $session =
290           new CGI::Session( "driver:File;serializer:yaml", undef, { Directory => '/tmp' } );
291         $sessionID = $session->id;
292         $userid    = $query->param('userid');
293         C4::Context->_new_userenv($sessionID);
294         my $password = $query->param('password');
295         C4::Context->_new_userenv($sessionID);
296         my ( $return, $cardnumber ) = checkpw( $userid, $password );
297         if ($return) {
298             $loggedin = 1;
299             open L, ">>/tmp/sessionlog";
300             my $time = localtime( time() );
301             printf L "%20s from %16s logged in  at %30s.\n", $userid,
302               $ENV{'REMOTE_ADDR'}, $time;
303             close L;
304             $cookie = $query->cookie( CGISESSID => $sessionID );
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                 -expires => ''
349             );
350         }
351         if ($envcookie) {
352             return ( $userid, [ $cookie, $envcookie ], $sessionID, $flags );
353         }
354         else {
355             return ( $userid, $cookie, $sessionID, $flags );
356         }
357     }
358
359     # else we have a problem...
360     # get the inputs from the incoming query
361     my @inputs = ();
362     foreach my $name ( param $query) {
363         (next) if ( $name eq 'userid' || $name eq 'password' );
364         my $value = $query->param($name);
365         push @inputs, { name => $name, value => $value };
366     }
367
368     my $path =
369       C4::Context->config('intrahtdocs') . "/prog/"
370       . ( $query->param('language') ? $query->param('language') : "en" );
371     my $filename = "$path/modules/$template_name";
372     $filename =~ s/\.tmpl$/.tt/;
373     my $interface = 'intranet';
374     my $template = C4::Templates->new( $interface, $filename);
375     $template->param(
376         INPUTS => \@inputs,
377
378     );
379     $template->param( login => 1 );
380     $template->param( loginprompt => 1 ) unless $info{'nopermission'};
381
382     my $self_url = $query->url( -absolute => 1 );
383     $template->param( url => $self_url, );
384     $template->param( \%info );
385     $cookie = $query->cookie(
386         -name    => 'CGISESSID',
387         -value   => $sessionID,
388         -expires => ''
389     );
390     print $query->header(
391         -type    => 'text/html; charset=utf-8',
392         -cookie  => $cookie
393       ),
394       $template->output;
395     exit;
396 }
397
398 sub checkpw {
399
400     my ( $userid, $password ) = @_;
401
402     if (   $userid
403         && $userid     eq C4::Context->config('user')
404         && "$password" eq C4::Context->config('pass') )
405     {
406
407         # Koha superuser account
408         C4::Context->set_userenv(
409             0, 0,
410             C4::Context->config('user'),
411             C4::Context->config('user'),
412             C4::Context->config('user'),
413             "", 1
414         );
415         return 2;
416     }
417     if (   $userid
418         && $userid     eq 'demo'
419         && "$password" eq 'demo'
420         && C4::Context->config('demo') )
421     {
422
423 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
424 # some features won't be effective : modify systempref, modify MARC structure,
425         return 2;
426     }
427     return 0;
428 }
429
430 END { }    # module clean-up code here (global destructor)
431 1;
432 __END__
433
434 =back
435
436 =head1 SEE ALSO
437
438 CGI(3)
439
440 C4::Output(3)
441
442 Digest::MD5(3)
443
444 =cut