more instances of branches => libraries
[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
31 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
32
33 # set the version for version checking
34 $VERSION = do { my @v = '$Revision$' =~ /\d+/g;
35     shift(@v) . "." . join( "_", map { sprintf "%03d", $_ } @v );
36 };
37
38 =head1 NAME
39
40 InstallAuth - Authenticates Koha users for Install process
41
42 =head1 SYNOPSIS
43
44   use CGI;
45   use InstallAuth;
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   print $query->header(
58     -type => 'utf-8',
59     -cookie => $cookie
60   ), $template->output;
61
62
63 =head1 DESCRIPTION
64
65     The main function of this module is to provide
66     authentification. However the get_template_and_user function has
67     been provided so that a users login information is passed along
68     automatically. This gets loaded into the template.
69     This package is different from C4::Auth in so far as 
70     C4::Auth uses many preferences which are supposed NOT to be obtainable when installing the database.
71     
72     As in C4::Auth, Authentication is based on cookies.
73
74 =head1 FUNCTIONS
75
76 =over 2
77
78 =cut
79
80 @ISA    = qw(Exporter);
81 @EXPORT = qw(
82   &checkauth
83   &get_template_and_user
84   &setlanguagecookie
85 );
86
87 =item get_template_and_user
88
89   my ($template, $borrowernumber, $cookie)
90     = get_template_and_user({template_name   => "opac-main.tmpl",
91                              query           => $query,
92                              type            => "opac",
93                              authnotrequired => 1,
94                              flagsrequired   => {borrow => 1},
95                           });
96
97     This call passes the C<query>, C<flagsrequired> and C<authnotrequired>
98     to C<&checkauth> (in this module) to perform authentification.
99     See C<&checkauth> for an explanation of these parameters.
100
101     The C<template_name> is then used to find the correct template for
102     the page. The authenticated users details are loaded onto the
103     template in the HTML::Template LOOP variable C<USER_INFO>. Also the
104     C<sessionID> is passed to the template. This can be used in templates
105     if cookies are disabled. It needs to be put as and input to every
106     authenticated page.
107
108     More information on the C<gettemplate> sub can be found in the
109     Output.pm module.
110
111 =cut
112
113 sub get_template_and_user {
114     my $in       = shift;
115     my $query=$in->{'query'};
116     my $language=$query->cookie('KohaOpacLanguage');
117     my $path=C4::Context->config('intrahtdocs')."/prog/".($language?$language:"en");
118     my $template       = HTML::Template::Pro->new(
119         filename          => "$path/".$in->{template_name},
120         die_on_bad_params => 1,
121         global_vars       => 1,
122         case_sensitive    => 1,
123         path              => ["$path/includes"]
124     );
125     
126     my ( $user, $cookie, $sessionID, $flags ) = checkauth(
127         $in->{'query'},
128         $in->{'authnotrequired'},
129         $in->{'flagsrequired'},
130         $in->{'type'}
131     );
132 #     use Data::Dumper;warn "utilisateur $user cookie : ".Dumper($cookie);
133
134     my $borrowernumber;
135     if ($user) {
136         $template->param( loggedinusername => $user );
137         $template->param( sessionID        => $sessionID );
138
139
140         # We are going to use the $flags returned by checkauth
141         # to create the template's parameters that will indicate
142         # which menus the user can access.
143         if (( $flags && $flags->{superlibrarian}==1)) {
144             $template->param( CAN_user_circulate        => 1 );
145             $template->param( CAN_user_catalogue        => 1 );
146             $template->param( CAN_user_parameters       => 1 );
147             $template->param( CAN_user_borrowers        => 1 );
148             $template->param( CAN_user_permission       => 1 );
149             $template->param( CAN_user_reserveforothers => 1 );
150             $template->param( CAN_user_borrow           => 1 );
151             $template->param( CAN_user_editcatalogue    => 1 );
152             $template->param( CAN_user_updatecharge     => 1 );
153             $template->param( CAN_user_acquisition      => 1 );
154             $template->param( CAN_user_management       => 1 );
155             $template->param( CAN_user_tools            => 1 ); 
156             $template->param( CAN_user_editauthorities  => 1 );
157             $template->param( CAN_user_serials          => 1 );
158             $template->param( CAN_user_reports          => 1 );
159         }
160     }
161     return ( $template, $borrowernumber, $cookie );
162 }
163
164 =item checkauth
165
166   ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
167
168 Verifies that the user is authorized to run this script.  If
169 the user is authorized, a (userid, cookie, session-id, flags)
170 quadruple is returned.  If the user is not authorized but does
171 not have the required privilege (see $flagsrequired below), it
172 displays an error page and exits.  Otherwise, it displays the
173 login page and exits.
174
175 Note that C<&checkauth> will return if and only if the user
176 is authorized, so it should be called early on, before any
177 unfinished operations (e.g., if you've opened a file, then
178 C<&checkauth> won't close it for you).
179
180 C<$query> is the CGI object for the script calling C<&checkauth>.
181
182 The C<$noauth> argument is optional. If it is set, then no
183 authorization is required for the script.
184
185 C<&checkauth> fetches user and session information from C<$query> and
186 ensures that the user is authorized to run scripts that require
187 authorization.
188
189 The C<$flagsrequired> argument specifies the required privileges
190 the user must have if the username and password are correct.
191 It should be specified as a reference-to-hash; keys in the hash
192 should be the "flags" for the user, as specified in the Members
193 intranet module. Any key specified must correspond to a "flag"
194 in the userflags table. E.g., { circulate => 1 } would specify
195 that the user must have the "circulate" privilege in order to
196 proceed. To make sure that access control is correct, the
197 C<$flagsrequired> parameter must be specified correctly.
198
199 The C<$type> argument specifies whether the template should be
200 retrieved from the opac or intranet directory tree.  "opac" is
201 assumed if it is not specified; however, if C<$type> is specified,
202 "intranet" is assumed if it is not "opac".
203
204 If C<$query> does not have a valid session ID associated with it
205 (i.e., the user has not logged in) or if the session has expired,
206 C<&checkauth> presents the user with a login page (from the point of
207 view of the original script, C<&checkauth> does not return). Once the
208 user has authenticated, C<&checkauth> restarts the original script
209 (this time, C<&checkauth> returns).
210
211 The login page is provided using a HTML::Template, which is set in the
212 systempreferences table or at the top of this file. The variable C<$type>
213 selects which template to use, either the opac or the intranet 
214 authentification template.
215
216 C<&checkauth> returns a user ID, a cookie, and a session ID. The
217 cookie should be sent back to the browser; it verifies that the user
218 has authenticated.
219
220 =cut
221
222 sub checkauth {
223     my $query = shift;
224
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
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('sessionID') ) {
241         C4::Context->_new_userenv($sessionID);
242         if ( my %hash = $query->cookie('userenv') ) {
243             C4::Context::set_userenv(
244                 $hash{number},       $hash{id},
245                 $hash{cardnumber},   $hash{firstname},
246                 $hash{surname},      $hash{branch},
247                 $hash{branchname},   $hash{flags},
248                 $hash{emailaddress}, $hash{branchprinter}
249             );
250             $cookie = $query->cookie(
251                 -name    => 'sessionID',
252                 -value   => $sessionID,
253                 -expires => ''
254             );
255             $loggedin=1;
256             $userid = $hash{cardnumber};
257         }
258         my ( $ip, $lasttime );
259
260         if ($logout) {
261
262             # voluntary logout the user
263             C4::Context->_unset_userenv($sessionID);
264             $sessionID = undef;
265             $userid    = undef;
266             open L, ">>/tmp/sessionlog";
267             my $time = localtime( time() );
268             printf L "%20s from %16s logged out at %30s (manually).\n", $userid,
269               $ip, $time;
270             close L;
271         }
272     }
273     unless ($userid) {
274         $sessionID = int( rand() * 100000 ) . '-' . time();
275         $userid    = $query->param('userid');
276         C4::Context->_new_userenv($sessionID);
277         my $password = $query->param('password');
278         C4::Context->_new_userenv($sessionID);
279         my ( $return, $cardnumber ) = checkpw( $userid, $password );
280         if ($return) {
281             $loggedin=1;
282             open L, ">>/tmp/sessionlog";
283             my $time = localtime( time() );
284             printf L "%20s from %16s logged in  at %30s.\n", $userid,
285               $ENV{'REMOTE_ADDR'}, $time;
286             close L;
287             $cookie = $query->cookie(
288                 -name    => 'sessionID',
289                 -value   => $sessionID,
290                 -expires => ''
291             );
292             if ( $return == 2 ) {
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,
298                     0,
299                     C4::Context->config('user'),
300                     C4::Context->config('user'),
301                     C4::Context->config('user'),
302                     "",
303                     "NO_LIBRARY_SET",
304                     1,""
305                 );
306                 $envcookie = $query->cookie(
307                     -name    => 'userenv',
308                     -value   => $hash,
309                     -expires => ''
310                 );
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    => 'sessionID',
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    => 'sessionID',
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