Merge branch 'master' of /home/jmf/repos/koha-rm-root.git/
[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 with
20 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
21 # Suite 330, Boston, MA  02111-1307 USA
22
23 use strict;
24 use Digest::MD5 qw(md5_base64);
25
26 require Exporter;
27 use C4::Context;
28 use C4::Output;
29 use C4::Koha;
30 use CGI::Session;
31
32 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
33
34 # set the version for version checking
35 $VERSION = do { my @v = '$Revision$' =~ /\d+/g;
36     shift(@v) . "." . join( "_", map { sprintf "%03d", $_ } @v );
37 };
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
48   my $query = new CGI;
49
50   my ($template, $borrowernumber, $cookie) 
51     = get_template_and_user({template_name   => "opac-main.tmpl",
52                              query           => $query,
53                              type            => "opac",
54                              authnotrequired => 1,
55                              flagsrequired   => {borrow => 1},
56                           });
57
58   print $query->header(
59     -type => 'utf-8',
60     -cookie => $cookie
61   ), $template->output;
62
63
64 =head1 DESCRIPTION
65
66     The main function of this module is to provide
67     authentification. However the get_template_and_user function has
68     been provided so that a users login information is passed along
69     automatically. This gets loaded into the template.
70     This package is different from C4::Auth in so far as 
71     C4::Auth uses many preferences which are supposed NOT to be obtainable when installing the database.
72     
73     As in C4::Auth, Authentication is based on cookies.
74
75 =head1 FUNCTIONS
76
77 =over 2
78
79 =cut
80
81 @ISA    = qw(Exporter);
82 @EXPORT = qw(
83   &checkauth
84   &get_template_and_user
85   &setlanguagecookie
86 );
87
88 =item get_template_and_user
89
90   my ($template, $borrowernumber, $cookie)
91     = get_template_and_user({template_name   => "opac-main.tmpl",
92                              query           => $query,
93                              type            => "opac",
94                              authnotrequired => 1,
95                              flagsrequired   => {borrow => 1},
96                           });
97
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.
101
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
107     authenticated page.
108
109     More information on the C<gettemplate> sub can be found in the
110     Output.pm module.
111
112 =cut
113
114 sub get_template_and_user {
115     my $in       = shift;
116     my $query=$in->{'query'};
117     my $language=$query->cookie('KohaOpacLanguage');
118     my $path=C4::Context->config('intrahtdocs')."/prog/".($language?$language:"en");
119     my $template       = HTML::Template::Pro->new(
120         filename          => "$path/".$in->{template_name},
121         die_on_bad_params => 1,
122         global_vars       => 1,
123         case_sensitive    => 1,
124         path              => ["$path/includes"]
125     );
126     
127     my ( $user, $cookie, $sessionID, $flags ) = checkauth(
128         $in->{'query'},
129         $in->{'authnotrequired'},
130         $in->{'flagsrequired'},
131         $in->{'type'}
132     );
133 #     use Data::Dumper;warn "utilisateur $user cookie : ".Dumper($cookie);
134
135     my $borrowernumber;
136     if ($user) {
137         $template->param( loggedinusername => $user );
138         $template->param( sessionID        => $sessionID );
139
140
141         # We are going to use the $flags returned by checkauth
142         # to create the template's parameters that will indicate
143         # which menus the user can access.
144         if (( $flags && $flags->{superlibrarian}==1)) {
145             $template->param( CAN_user_circulate        => 1 );
146             $template->param( CAN_user_catalogue        => 1 );
147             $template->param( CAN_user_parameters       => 1 );
148             $template->param( CAN_user_borrowers        => 1 );
149             $template->param( CAN_user_permission       => 1 );
150             $template->param( CAN_user_reserveforothers => 1 );
151             $template->param( CAN_user_borrow           => 1 );
152             $template->param( CAN_user_editcatalogue    => 1 );
153             $template->param( CAN_user_updatecharge     => 1 );
154             $template->param( CAN_user_acquisition      => 1 );
155             $template->param( CAN_user_management       => 1 );
156             $template->param( CAN_user_tools            => 1 ); 
157             $template->param( CAN_user_editauthorities  => 1 );
158             $template->param( CAN_user_serials          => 1 );
159             $template->param( CAN_user_reports          => 1 );
160         }
161     }
162     return ( $template, $borrowernumber, $cookie );
163 }
164
165 =item 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 # $authnotrequired will be set for scripts which will run without authentication
226     my $authnotrequired = shift;
227     my $flagsrequired   = shift;
228     my $type            = shift;
229     $type = 'intranet' unless $type;
230
231     my $dbh = C4::Context->dbh();
232     my $template_name;
233     $template_name = "installer/auth.tmpl";
234
235     # state variables
236     my $loggedin = 0;
237     my %info;
238     my ( $userid, $cookie, $sessionID, $flags, $envcookie );
239     my $logout = $query->param('logout.x');
240     if (  $sessionID = $query->cookie("CGISESSID") ) {
241         C4::Context->_new_userenv($sessionID);
242                 my $session = new CGI::Session("driver:File", $sessionID, {Directory=>'/tmp'});
243         if ( $session ) {
244             C4::Context::set_userenv(                           
245                  $session->param('number'),       $session->param('id'),
246                  $session->param('cardnumber'),   $session->param('firstname'),
247                  $session->param('surname'),      $session->param('branch'),
248                  $session->param('branchname'),   $session->param('flags'),
249                  $session->param('emailaddress'), $session->param('branchprinter')
250              );
251                         $cookie = $query->cookie(CGISESSID => $session->id);
252             $loggedin=1;
253             $userid = $session->param('cardnumber');
254         }
255         my ( $ip, $lasttime );
256
257         if ($logout) {
258
259             # voluntary logout the user
260             C4::Context->_unset_userenv($sessionID);
261             $sessionID = undef;
262             $userid    = undef;
263             open L, ">>/tmp/sessionlog";
264             my $time = localtime( time() );
265             printf L "%20s from %16s logged out at %30s (manually).\n", $userid,
266               $ip, $time;
267             close L;
268         }
269     }
270     unless ($userid) {
271                 my $session = new CGI::Session("driver:File", undef, {Directory=>'/tmp'});
272                 $sessionID = $session->id;
273         $userid    = $query->param('userid');
274         C4::Context->_new_userenv($sessionID);
275         my $password = $query->param('password');
276         C4::Context->_new_userenv($sessionID);
277         my ( $return, $cardnumber ) = checkpw( $userid, $password );
278         if ($return) {
279             $loggedin=1;
280             open L, ">>/tmp/sessionlog";
281             my $time = localtime( time() );
282             printf L "%20s from %16s logged in  at %30s.\n", $userid,
283               $ENV{'REMOTE_ADDR'}, $time;
284             close L;
285                         $cookie = $query->cookie(CGISESSID => $sessionID);
286             if ( $return == 2 ) {
287                 #Only superlibrarian should have access to this page.
288                 #Since if it is a user, it is supposed that there is a borrower table
289                 #And thus that data structure is loaded.
290                 my $hash = C4::Context::set_userenv(
291                     0,
292                     0,
293                     C4::Context->config('user'),
294                     C4::Context->config('user'),
295                     C4::Context->config('user'),
296                     "",
297                     "NO_LIBRARY_SET",
298                     1,""
299                 );
300                                 $session->param('number',0);
301                                 $session->param('id',C4::Context->config('user'));
302                                 $session->param('cardnumber',C4::Context->config('user'));
303                                 $session->param('firstname',C4::Context->config('user'));
304                                 $session->param('surname',C4::Context->config('user'),);
305                                 $session->param('branch','NO_LIBRARY_SET');
306                                 $session->param('branchname','NO_LIBRARY_SET');
307                                 $session->param('flags',1);
308                                 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
309                                 $session->param('ip',$session->remote_addr());
310                                 $session->param('lasttime',time());                
311                 $userid=C4::Context->config('user');
312             }
313         }
314         else {
315             if ($userid) {
316                 $info{'invalid_username_or_password'} = 1;
317                 C4::Context->_unset_userenv($sessionID);
318             }
319         }
320     }
321
322     # finished authentification, now respond
323     if ( $loggedin )
324     {
325
326         # successful login
327         unless ($cookie) {
328             $cookie = $query->cookie(
329                 -name    => 'CGISESSID',
330                 -value   => '',
331                 -expires => ''
332             );
333         }
334         if ($envcookie) {
335             return ( $userid, [ $cookie, $envcookie ], $sessionID, $flags );
336         }
337         else {
338             return ( $userid, $cookie, $sessionID, $flags );
339         }
340     }
341
342     # else we have a problem...
343     # get the inputs from the incoming query
344     my @inputs = ();
345     foreach my $name ( param $query) {
346         (next) if ( $name eq 'userid' || $name eq 'password' );
347         my $value = $query->param($name);
348         push @inputs, { name => $name, value => $value };
349     }
350
351     my $path=C4::Context->config('intrahtdocs')."/prog/".($query->param('language')?$query->param('language'):"en");
352     my $template       = HTML::Template::Pro->new(
353         filename          => "$path/$template_name",
354         die_on_bad_params => 1,
355         global_vars       => 1,
356         case_sensitive    => 1,
357         path              => ["$path/includes"]
358     );
359     $template->param(
360         INPUTS               => \@inputs,
361
362     );
363     $template->param( loginprompt => 1 ) unless $info{'nopermission'};
364
365     my $self_url = $query->url( -absolute => 1 );
366     $template->param(
367         url         => $self_url,
368     );
369     $template->param( \%info );
370     $cookie = $query->cookie(
371         -name    => 'CGISESSID',
372         -value   => $sessionID,
373         -expires => ''
374     );
375     print $query->header(
376         -type   => 'utf-8',
377         -cookie => $cookie
378       ),
379       $template->output;
380     exit;
381 }
382
383 sub checkpw {
384
385     my ( $userid, $password ) = @_;
386
387     if (   $userid && $userid eq C4::Context->config('user')
388         && "$password" eq C4::Context->config('pass') )
389     {
390 # Koha superuser account
391                 C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
392         return 2;
393     }
394     if (   $userid && $userid eq 'demo'
395         && "$password" eq 'demo'
396         && C4::Context->config('demo') )
397     {
398
399 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
400 # some features won't be effective : modify systempref, modify MARC structure,
401         return 2;
402     }
403     return 0;
404 }
405
406
407 END { }    # module clean-up code here (global destructor)
408 1;
409 __END__
410
411 =back
412
413 =head1 SEE ALSO
414
415 CGI(3)
416
417 C4::Output(3)
418
419 Digest::MD5(3)
420
421 =cut