Bug that broke access flag checking when logging in using a borrower card
[koha.git] / C4 / Auth.pm
1 package C4::Auth;
2
3
4 # Copyright 2000-2002 Katipo Communications
5 #
6 # This file is part of Koha.
7 #
8 # Koha is free software; you can redistribute it and/or modify it under the
9 # terms of the GNU General Public License as published by the Free Software
10 # Foundation; either version 2 of the License, or (at your option) any later
11 # version.
12 #
13 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
14 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License along with
18 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
19 # Suite 330, Boston, MA  02111-1307 USA
20
21 use strict;
22 use Digest::MD5 qw(md5_base64);
23
24
25 require Exporter;
26 use C4::Database;
27 use C4::Koha;
28
29 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
30
31 # set the version for version checking
32 $VERSION = 0.01;
33
34 @ISA = qw(Exporter);
35 @EXPORT = qw(
36              &checkauth
37 );
38
39
40 sub getuserflags {
41     my $cardnumber=shift;
42     my $dbh=shift;
43     my $userflags;
44     my $sth=$dbh->prepare("select flags from borrowers where cardnumber=?");
45     $sth->execute($cardnumber);
46     my ($flags) = $sth->fetchrow;
47     $sth=$dbh->prepare("select bit,flag from userflags");
48     $sth->execute;
49     while (my ($bit, $flag) = $sth->fetchrow) {
50         if ($flags & (2**$bit)) {
51             $userflags->{$flag}=1;
52         }
53     }
54     return $userflags;
55 }
56
57 sub checkauth {
58     my $query=shift;
59     # $authnotrequired will be set for scripts which will run without authentication
60     my $authnotrequired=shift;
61     my $flagsrequired=shift;
62     if (my $userid=$ENV{'REMOTE_USER'}) {
63         # Using Basic Authentication, no cookies required
64         my $cookie=$query->cookie(-name => 'sessionID',
65                                   -value => '',
66                                   -expires => '+1y');
67         return ($userid, $cookie, '');
68     }
69     my $sessionID=$query->cookie('sessionID');
70     my $message='';
71
72     my $dbh=C4Connect();
73     my $sth=$dbh->prepare("select userid,ip,lasttime from sessions where sessionid=?");
74     $sth->execute($sessionID);
75     if ($sth->rows) {
76         my ($userid, $ip, $lasttime) = $sth->fetchrow;
77         if ($lasttime<time()-7200) {
78             # timed logout
79             $message="You have been logged out due to inactivity.";
80             my $sti=$dbh->prepare("delete from sessions where sessionID=?");
81             $sti->execute($sessionID);
82             my $scriptname=$ENV{'SCRIPT_NAME'};
83             my $selfurl=$query->self_url();
84             $sti=$dbh->prepare("insert into sessionqueries (sessionID, userid, value) values (?, ?, ?)");
85             $sti->execute($sessionID, $userid, $selfurl);
86             open L, ">>/tmp/sessionlog";
87             my $time=localtime(time());
88             printf L "%20s from %16s logged out at %30s (inactivity).\n", $userid, $ip, $time;
89             close L;
90         } elsif ($ip ne $ENV{'REMOTE_ADDR'}) {
91             # Different ip than originally logged in from
92             my $newip=$ENV{'REMOTE_ADDR'};
93
94             $message="ERROR ERROR ERROR ERROR<br>Attempt to re-use a cookie from a different ip address.<br>(authenticated from $ip, this request from $newip)";
95         } else {
96             my $cookie=$query->cookie(-name => 'sessionID',
97                                       -value => $sessionID,
98                                       -expires => '+1y');
99             my $sti=$dbh->prepare("update sessions set lasttime=? where sessionID=?");
100             $sti->execute(time(), $sessionID);
101             my $sth=$dbh->prepare("select cardnumber from borrowers where userid=?");
102             $sth->execute($userid);
103             my ($cardnumber) = $sth->fetchrow;
104             ($cardnumber) || ($cardnumber=$userid);
105             my $flags=getuserflags($cardnumber,$dbh);
106             my $configfile=configfile();
107             if ($userid eq $configfile->{'user'}) {
108                 # Super User Account from /etc/koha.conf
109                 $flags->{'superlibrarian'}=1;
110             }
111             foreach (keys %$flagsrequired) {
112                 warn "Checking required flag $_";
113                 unless ($flags->{superlibrarian}) {
114                     unless ($flags->{$_}) {
115                         print qq|Content-type: text/html
116
117 <html>
118 <body>
119 REJECTED
120 <hr>
121 You do not have access to this portion of Koha
122 </body>
123 </html>
124 |;
125                         exit;
126                     }
127                 }
128             }
129             return ($userid, $cookie, $sessionID, $flags);
130         }
131     }
132
133
134
135     if ($authnotrequired) {
136         my $cookie=$query->cookie(-name => 'sessionID',
137                                   -value => '',
138                                   -expires => '+1y');
139         return('', $cookie, '');
140     } else {
141         ($sessionID) || ($sessionID=int(rand()*100000).'-'.time());
142         my $userid=$query->param('userid');
143         my $password=$query->param('password');
144         my ($return, $cardnumber) = checkpw($dbh,$userid,$password);
145         if ($return) {
146             my $sti=$dbh->prepare("delete from sessions where sessionID=? and userid=?");
147             $sti->execute($sessionID, $userid);
148             $sti=$dbh->prepare("insert into sessions (sessionID, userid, ip,lasttime) values (?, ?, ?, ?)");
149             $sti->execute($sessionID, $userid, $ENV{'REMOTE_ADDR'}, time());
150             $sti=$dbh->prepare("select url from sessionqueries where sessionID=? and userid=?");
151             $sti->execute($sessionID, $userid);
152             if ($sti->rows) {
153                 my ($selfurl) = $sti->fetchrow;
154                 my $stj=$dbh->prepare("delete from sessionqueries where sessionID=?");
155                 $stj->execute($sessionID);
156                 ($selfurl) || ($selfurl=$ENV{'SCRIPT_NAME'});
157                 print $query->redirect($selfurl);
158                 exit;
159             }
160             open L, ">>/tmp/sessionlog";
161             my $time=localtime(time());
162             printf L "%20s from %16s logged in  at %30s.\n", $userid, $ENV{'REMOTE_ADDR'}, $time;
163             close L;
164             my $cookie=$query->cookie(-name => 'sessionID',
165                                       -value => $sessionID,
166                                       -expires => '+1y');
167             my $flags;
168             if ($return==2) {
169                 $flags->{'superlibrarian'}=1;
170             } else {
171                 $flags=getuserflags($cardnumber, $dbh);
172             }
173             foreach (keys %$flagsrequired) {
174                 warn "Checking required flag $_";
175                 unless ($flags->{superlibrarian}) {
176                     unless ($flags->{$_}) {
177                         print qq|Content-type: text/html
178
179 <html>
180 <body>
181 REJECTED
182 <hr>
183 You do not have access to this portion of Koha
184 </body>
185 </html>
186 |;
187                         exit;
188                     }
189                 }
190             }
191             return ($userid, $cookie, $sessionID, $flags);
192         } else {
193             if ($userid) {
194                 $message="Invalid userid or password entered.";
195             }
196             my $parameters;
197             foreach (param $query) {
198                 $parameters->{$_}=$query->{$_};
199             }
200             my $cookie=$query->cookie(-name => 'sessionID',
201                                       -value => $sessionID,
202                                       -expires => '+1y');
203             print $query->header(-cookie=>$cookie);
204             print qq|
205 <html>
206 <body background=/images/kohaback.jpg>
207 <center>
208 <h2>$message</h2>
209
210 <form method=post>
211 <table border=0 cellpadding=10 cellspacing=0 width=60%>
212     <tr><td align=center valign=top>
213
214     <table border=0 bgcolor=#dddddd cellpadding=10 cellspacing=0>
215     <tr><th colspan=2 background=/images/background-mem.gif><font size=+2>Koha Login</font></th></tr>
216     <tr><td>Name:</td><td><input name=userid></td></tr>
217     <tr><td>Password:</td><td><input type=password name=password></td></tr>
218     <tr><td colspan=2 align=center><input type=submit value=login></td></tr>
219     </table>
220 <!--
221     
222     </td><td align=center valign=top>
223
224     <table border=0 bgcolor=#dddddd cellpadding=10 cellspacing=0>
225     <tr><th background=/images/background-mem.gif><font size=+2>Demo Information</font></th></tr>
226     <td>
227     Log in as librarian/koha or patron/koha.  The timeout is set to 40 seconds of
228     inactivity for the purposes of this demo.  You can navigate to the Circulation
229     or Acquisitions modules and you should see an indicator in the upper left of
230     the screen saying who you are logged in as.  If you want to try it out with
231     a longer timout period, log in as tonnesen/koha and there will be no
232     timeout period.
233     <p>
234     You can also log in using a patron cardnumber.   Try V10000008 and
235     V1000002X with password koha.
236     </td>
237     </tr>
238     </table>
239 -->
240
241     </td></tr>
242 </table>
243 </form>
244 </body>
245 </html>
246 |;
247             exit;
248         }
249     }
250 }
251
252
253 sub checkpw {
254
255 # This should be modified to allow a select of authentication schemes (ie LDAP)
256 # as well as local authentication through the borrowers tables passwd field
257 #
258
259     my ($dbh, $userid, $password) = @_;
260     my $sth=$dbh->prepare("select password,cardnumber from borrowers where userid=?");
261     $sth->execute($userid);
262     if ($sth->rows) {
263         my ($md5password,$cardnumber) = $sth->fetchrow;
264         if (md5_base64($password) eq $md5password) {
265             return 1,$cardnumber;
266         }
267     }
268     my $sth=$dbh->prepare("select password from borrowers where cardnumber=?");
269     $sth->execute($userid);
270     if ($sth->rows) {
271         my ($md5password) = $sth->fetchrow;
272         if (md5_base64($password) eq $md5password) {
273             return 1,$userid;
274         }
275     }
276     my $configfile=configfile();
277     if ($userid eq $configfile->{'user'} && $password eq $configfile->{'pass'}) {
278         # Koha superuser account
279         return 2;
280     }
281     return 0;
282 }
283
284
285 END { }       # module clean-up code here (global destructor)