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