[system] / trunk / webwork2 / lib / WeBWorK / Authen.pm Repository:
ViewVC logotype

Annotation of /trunk/webwork2/lib/WeBWorK/Authen.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4029 - (view) (download) (as text)

1 : sh002i 455 ################################################################################
2 : sh002i 1663 # WeBWorK Online Homework Delivery System
3 : sh002i 3973 # Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/
4 : glarose 4029 # $CVSHeader: webwork2/lib/WeBWorK/Authen.pm,v 1.50 2006/01/25 23:13:51 sh002i Exp $
5 : sh002i 1663 #
6 :     # This program is free software; you can redistribute it and/or modify it under
7 :     # the terms of either: (a) the GNU General Public License as published by the
8 :     # Free Software Foundation; either version 2, or (at your option) any later
9 :     # version, or (b) the "Artistic License" which comes with this package.
10 :     #
11 :     # This program is distributed in the hope that it will be useful, but WITHOUT
12 :     # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 :     # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
14 :     # Artistic License for more details.
15 : sh002i 455 ################################################################################
16 :    
17 : malsyned 305 package WeBWorK::Authen;
18 :    
19 : sh002i 455 =head1 NAME
20 :    
21 :     WeBWorK::Authen - Check user identity, manage session keys.
22 :    
23 :     =cut
24 :    
25 : malsyned 441 use strict;
26 :     use warnings;
27 : sh002i 1683 use Apache::Cookie;
28 : sh002i 1777 use Date::Format;
29 : sh002i 3799 use Socket qw/unpack_sockaddr_in inet_ntoa/;
30 :     use WeBWorK::Utils qw/writeCourseLog/;
31 : malsyned 335
32 : sh002i 1777 use constant COOKIE_LIFESPAN => 60*60*24*30; # 30 days
33 : sh002i 3799 use constant GENERIC_ERROR_MESSAGE => "Invalid user ID or password.";
34 : sh002i 1777
35 : sh002i 3741 ################################################################################
36 :     # Public API
37 :     ################################################################################
38 :    
39 :     =head1 CONSTRUCTOR
40 :    
41 :     =over
42 :    
43 :     =item new($r)
44 :    
45 :     Instantiates a new WeBWorK::Authen object for the given WeBWorK::Requst ($r).
46 :    
47 :     =cut
48 :    
49 : sh002i 1885 sub new {
50 :     my ($invocant, $r) = @_;
51 : malsyned 323 my $class = ref($invocant) || $invocant;
52 : sh002i 1885 my $self = {
53 :     r => $r,
54 :     };
55 : malsyned 305 bless $self, $class;
56 :     return $self;
57 :     }
58 :    
59 : sh002i 3741 =back
60 : sh002i 817
61 : sh002i 3741 =cut
62 : sh002i 3366
63 : sh002i 3741 =head1 METHODS
64 : sh002i 817
65 : sh002i 3741 =over
66 : malsyned 323
67 : sh002i 3741 =item verify()
68 : sh002i 817
69 : sh002i 3741 verify() checks several properties of the WeBWorK::Request with which it was
70 :     created to determine if a user is who they say they are. If the verification
71 :     failed because of of invalid authentication data, a note will be written in the
72 :     request explaining why it failed. If the request failed because no
73 :     authentication data was provided, however, no note will be written, as this is
74 :     expected to happen whenever someone types in a URL manually, and is not
75 :     considered an error condition.
76 : sh002i 1683
77 : sh002i 3741 =cut
78 : sh002i 1683
79 : glarose 3377 # much of the code in verify() is duplicated in verifyProctor(), below. any
80 :     # changes that are made to this subroutine should be checked against
81 :     # verifyProctor() to ensure that the the two routines continue to work in
82 :     # approximately the same manner.
83 : malsyned 305 sub verify($) {
84 :     my $self = shift;
85 :     my $r = $self->{r};
86 : sh002i 1885 my $ce = $r->ce;
87 :     my $db = $r->db;
88 : sh002i 3004 my $authz = $r->authz;
89 : malsyned 305
90 : sh002i 817 my $practiceUserPrefix = $ce->{practiceUserPrefix};
91 :     my $debugPracticeUser = $ce->{debugPracticeUser};
92 :    
93 : malsyned 825 my $force_passwd_authen = $r->param('force_passwd_authen');
94 : sh002i 1684 my $login_practice_user = $r->param('login_practice_user');
95 :     my $send_cookie = $r->param("send_cookie");
96 : gage 3125 my @temp_users = $r->param("user");
97 :     warn "users start out as ", join(" ", @temp_users) if @temp_users >1;
98 : malsyned 441 my $error;
99 : sh002i 817 my $failWithoutError = 0;
100 : sh002i 1685 my $credentialSource = "params";
101 : malsyned 313
102 : sh002i 1685 my $user = $r->param('user');
103 :     my $passwd = $r->param('passwd');
104 :     my $key = $r->param('key');
105 :    
106 : sh002i 1777 my ($cookieUser, $cookieKey) = $self->fetchCookie;
107 :     #warn __PACKAGE__, ": verify: cookieUser=$cookieUser cookieKey=$cookieKey\n";
108 : sh002i 1685
109 : sh002i 817 VERIFY: {
110 :     # This block is here so we can "last" out of it when we've
111 :     # decided whether we're going to succeed or fail.
112 :    
113 : sh002i 3834 # no database means no user/password/permission records
114 :     unless ($db) {
115 :     $failWithoutError = 1;
116 :     last VERIFY;
117 :     }
118 :    
119 : sh002i 1684 if ($login_practice_user) {
120 :     # ignore everything else, find an unused practice user
121 :     my $found = 0;
122 : sh002i 3693
123 :     # figure out if there are any valid practice users
124 :     my @guestUserIDs = grep m/^$practiceUserPrefix/, $db->listUsers;
125 :     my @GuestUsers = $db->getUsers(@guestUserIDs);
126 :     my @allowedGuestUsers = grep { $ce->status_abbrev_has_behavior($_->status, "allow_course_access") } @GuestUsers;
127 :     my @allowedGestUserIDs = map { $_->user_id } @allowedGuestUsers;
128 :    
129 :     foreach my $userID (@allowedGestUserIDs) {
130 : sh002i 1684 if (not $self->unexpiredKeyExists($userID)) {
131 :     my $Key = $self->generateKey($userID);
132 :     $db->addKey($Key);
133 :     $r->param("user", $userID);
134 :     $r->param("key", $Key->key);
135 :     $found = 1;
136 : sh002i 3799 $credentialSource = "guest";
137 :     $self->write_log_entry("GUEST LOGIN OK $userID");
138 : sh002i 1684 last;
139 :     }
140 :     }
141 :     unless ($found) {
142 : sh002i 3799 $self->write_log_entry("GUEST LOGIN FAILED - no logins availabe");
143 : sh002i 1684 $error = "No practice users are available. Please try again in a few minutes.";
144 :     }
145 :     last VERIFY;
146 :     }
147 :    
148 : sh002i 817 # no authentication data was given. this is OK.
149 :     unless (defined $user or defined $passwd or defined $key) {
150 : sh002i 1683 # check to see if a cookie was sent by the browser. if so, use the
151 :     # user and key from the cookie for authentication. note that the
152 :     # cookie is only used if no credentials are sent as parameters.
153 :     if ($cookieUser and $cookieKey) {
154 :     $user = $cookieUser;
155 :     $key = $cookieKey;
156 : sh002i 1684 $r->param("user", $user);
157 : gage 3125 #$r->args->{user} = $user;
158 : sh002i 1684 $r->param("key", $key);
159 : sh002i 1685 $credentialSource = "cookie";
160 : sh002i 3799 $self->write_log_entry("COOKIE LOGIN OK $user");
161 : sh002i 1683 } else {
162 :     $failWithoutError = 1;
163 :     last VERIFY;
164 :     }
165 : sh002i 817 }
166 :    
167 : malsyned 825 if (defined $user and $force_passwd_authen) {
168 :     $failWithoutError = 1;
169 :     last VERIFY;
170 :     }
171 :    
172 :     # no user was supplied. somebody's building their own GET
173 : sh002i 817 unless ($user) {
174 :     $error = "You must specify a username.";
175 :     last VERIFY;
176 :     }
177 : sh002i 3004
178 : gage 1724 # Make sure user is in the database
179 : sh002i 3688 my $User = $db->getUser($user); # checked
180 :     unless (defined $User) {
181 : sh002i 3799 $self->write_log_entry("LOGIN FAILED $user - user unknown");
182 :     $error = GENERIC_ERROR_MESSAGE;
183 : gage 1724 last VERIFY;
184 :     }
185 : sh002i 3004
186 :     # fix invalid status values (FIXME this should be in DB!)
187 : sh002i 3688 if (not defined $User->status or not defined $ce->status_abbrev_to_name($User->status)) {
188 :     my $default_status = $ce->{default_status};
189 :     die "default_status not defined in course environment" unless defined $default_status;
190 :     my ($default_abbrev) = $ce->status_name_to_abbrevs($default_status);
191 :     die "default status has no abbrevs in course environment" unless defined $default_abbrev;
192 :     $User->status($default_abbrev);
193 :     $db->putUser($User);
194 :     warn "Setting status for user $user to '$default_abbrev'. It was previously unset or set to an invalid value.";
195 : gage 1724 }
196 : sh002i 3004
197 : sh002i 3688 # make sure users with this user's status are allowed to access the course (jeez...)
198 :     unless ($ce->status_abbrev_has_behavior($User->status, "allow_course_access")) {
199 : sh002i 3799 $self->write_log_entry("LOGIN FAILED $user - course access denied");
200 :     $error = GENERIC_ERROR_MESSAGE;
201 : gage 1724 last VERIFY;
202 :     }
203 : sh002i 3004
204 :     # make sure the user is allowed to login
205 :     unless ($authz->hasPermissions($user, "login")) {
206 : sh002i 3799 $self->write_log_entry("LOGIN FAILED $user - no permission to login");
207 :     $error = GENERIC_ERROR_MESSAGE;
208 : sh002i 3004 last VERIFY;
209 :     }
210 :    
211 : sh002i 817 # it's a practice user.
212 : malsyned 349 if ($practiceUserPrefix and $user =~ /^$practiceUserPrefix/) {
213 : sh002i 817 # we're not interested in a practice user's password
214 :     $r->param("passwd", "");
215 : gage 1724
216 :    
217 : sh002i 817 # we've got a key.
218 :     if ($key) {
219 :     if ($self->checkKey($user, $key)) {
220 :     # they key was valid.
221 : sh002i 3799 #$self->write_log_entry("KEY LOGIN OK $user");
222 : sh002i 817 last VERIFY;
223 :     } else {
224 :     # the key was invalid.
225 : sh002i 3799 #$self->write_log_entry("KEY LOGIN FAILED $user - invalid key");
226 : malsyned 827 $error = "Your session has timed out due to inactivity. You must login again.";
227 : sh002i 817 last VERIFY;
228 :     }
229 :     }
230 :    
231 :     # -- here we know that a key was not supplied. --
232 :    
233 :     # it's the debug user.
234 :     if ($debugPracticeUser and $user eq $debugPracticeUser) {
235 :     # clobber any existing session, valid or not.
236 :     my $Key = $self->generateKey($user);
237 : sh002i 912 eval { $db->deleteKey($user) };
238 : sh002i 817 $db->addKey($Key);
239 :     $r->param("key", $Key->key());
240 :     last VERIFY;
241 :     }
242 :    
243 :     # an unexpired key exists -- the account is in use.
244 : sh002i 3799 # FIXME improve the error message here
245 : sh002i 817 if ($self->unexpiredKeyExists($user)) {
246 : sh002i 3799 $self->write_log_entry("GUEST LOGIN FAILED $user - in use");
247 : sh002i 817 $error = "That practice account is in use.";
248 :     last VERIFY;
249 :     }
250 :    
251 :     # here we know the account is not in use, so we
252 :     # generate a new session key (unexpiredKeyExists
253 :     # deleted any expired key) and succeed!
254 :     my $Key = $self->generateKey($user);
255 :     $db->addKey($Key);
256 :     $r->param("key", $Key->key());
257 :     last VERIFY;
258 :     }
259 :    
260 :     # -- here we know it's a regular user. --
261 : gage 1724
262 : sh002i 817
263 :     # a key was supplied.
264 :     if ($key) {
265 :     # we're not interested in a user's password if they're
266 : toenail 2843 # supplying a key unless that key comes from a cookie in which case
267 :     # the key could be expired but the password good.
268 :     $r->param("passwd", "") unless $cookieKey;
269 : sh002i 817
270 :     if ($self->checkKey($user, $key)) {
271 :     # valid key, so succeed.
272 :     last VERIFY;
273 : malsyned 349 } else {
274 : sh002i 817 # invalid key. the login page doesn't propogate the key,
275 :     # so we know this is an expired session.
276 : toenail 2843 unless ($passwd) {
277 :     $error = "Your session has timed out due to inactivity. You must login again.";
278 :     last VERIFY;
279 :     }
280 : malsyned 349 }
281 :     }
282 : gage 1724
283 : sh002i 817 # a password was supplied.
284 :     if ($passwd) {
285 : gage 1721
286 : sh002i 817 if ($self->checkPassword($user, $passwd)) {
287 :     # valid password, so create a new session. (we don't want
288 :     # to reuse an old one, duh.)
289 :     my $Key = $self->generateKey($user);
290 : sh002i 912 eval { $db->deleteKey($user) };
291 : sh002i 817 $db->addKey($Key);
292 :     $r->param("key", $Key->key());
293 :     # also delete the password
294 :     $r->param("passwd", "");
295 : sh002i 3799 $self->write_log_entry("LOGIN OK $user - valid password");
296 : sh002i 817 last VERIFY;
297 :     } else {
298 :     # incorrect password. fail.
299 : sh002i 3799 $self->write_log_entry("LOGIN FAILED $user - invalid password");
300 :     $error = "Invalid user ID or password.";
301 : sh002i 817 last VERIFY;
302 :     }
303 : malsyned 313 }
304 : sh002i 817
305 :     # neither a key or a password were supplied.
306 :     $error = "You must enter a password."
307 : sh002i 3834 } # /* VERIFY */
308 : sh002i 3657
309 :     # check for multiply defined users
310 : gage 3125 my @test_users = $r->param("user");
311 :     if (@test_users>1) {
312 :     warn "User has been multiply defined in Authen.pm ", join(" ", @test_users) ;
313 :     $r->param("user"=>$test_users[0]);
314 :     @test_users = $r->param("user");
315 :     warn "New value of user is ", join(" ", @test_users);
316 :     }
317 : sh002i 817
318 :     if (defined $error) {
319 : sh002i 1685 # authentication failed, store the error message
320 : sh002i 3657 $r->notes("authen_error", $error);
321 : sh002i 1685
322 :     # if we got a cookie, it probably has incorrect information in it. so
323 :     # we want to get rid of it
324 :     if ($cookieUser or $cookieKey) {
325 :     #warn "fail with error: killing cookie";
326 :     $self->killCookie;
327 :     }
328 :    
329 : sh002i 3741 # store verification result for fast retrevial later
330 :     $self->{was_verified} = 0;
331 :    
332 : sh002i 817 return 0;
333 : sh002i 1682 } elsif ($failWithoutError) {
334 : sh002i 1685 # authentication failed, but we don't have any error message to report
335 :    
336 :     # if we got a cookie, it probably has incorrect information in it. so
337 :     # we want to get rid of it
338 :     if ($cookieUser or $cookieKey) {
339 :     #warn "fail without error: killing cookie";
340 :     $self->killCookie;
341 :     }
342 :    
343 : sh002i 3741 # store verification result for fast retrevial later
344 :     $self->{was_verified} = 0;
345 :    
346 : sh002i 1682 return 0;
347 : malsyned 313 } else {
348 : sh002i 1682 # autentication succeeded!
349 : sh002i 1685
350 :     # we send a cookie if any of these conditions are met:
351 :     # (a) a cookie was used for authentication
352 :     # (b) a cookie was sent but not used for authentication, and the
353 :     # credentials used for authentication were the same as those in
354 :     # the cookie
355 :     # (c) the user asked to have a cookie sent and is not a guest user.
356 :     my $usedCookie = ($credentialSource eq "cookie") || 0;
357 : gage 1694
358 :     my $unusedCookieMatched = (defined($key) and defined($cookieUser) and defined($cookieKey) and
359 :     $user eq $cookieUser and $key eq $cookieKey) || 0;
360 : sh002i 1685 my $userRequestsCookie = ($send_cookie and not $login_practice_user) || 0;
361 :     #warn "usedCookie=$usedCookie\n";
362 :     #warn "unusedCookieMatched=$unusedCookieMatched\n";
363 :     #warn "userRequestsCookie=$userRequestsCookie\n";
364 :     if ($usedCookie or $unusedCookieMatched or $userRequestsCookie) {
365 :     #warn "succeed: sending cookie";
366 : sh002i 1683 $self->sendCookie($r->param("user"), $r->param("key"));
367 : sh002i 1685 } elsif ($cookieUser or $cookieKey) {
368 :     # otherwise, we don't want any bad cookies sticking around
369 :     #warn "succeed: killing cookie";
370 :     $self->killCookie;
371 : sh002i 1683 }
372 : sh002i 3741
373 :     # store verification result for fast retrevial later
374 :     $self->{was_verified} = 1;
375 :    
376 : sh002i 1682 return 1;
377 : malsyned 305 }
378 :     }
379 :    
380 : sh002i 3741 =item was_verified()
381 :    
382 :     Returns true if verify() returned true the last time it was called.
383 :    
384 :     =cut
385 :    
386 :     sub was_verified {
387 :     my ($self) = @_;
388 :    
389 :     return 1 if exists $self->{was_verified} and $self->{was_verified};
390 :     return 0;
391 :     }
392 :    
393 :     =item forget_verification()
394 :    
395 :     Future calls to was_verified() will return false, until verify() is called again and succeeds.
396 :    
397 :     =cut
398 :    
399 :     sub forget_verification {
400 :     my ($self) = @_;
401 :    
402 :     $self->{was_verified} = 0;
403 :     }
404 :    
405 :     =item verifyProctor()
406 :    
407 :     verifyProctor() checks several properties of the WeBWorK::Request with which it
408 :     was created to determine if a proctor is who they say they are. It is
409 :     essentially the same as verify(), but pulls out the proctor data from the form
410 :     input and uses that with the appropriate database entry names to determine
411 :     whether the proctor is valid.
412 :    
413 :     =cut
414 :    
415 : sh002i 3688 sub verifyProctor {
416 :     my $self = shift();
417 :     my $r = $self->{r};
418 :     my $ce = $r->ce;
419 :     my $db = $r->db;
420 :    
421 :     my $user = $r->param('effectiveUser');
422 :     my $proctorUser = $r->param('proctor_user');
423 :     my $proctorPasswd = $r->param('proctor_passwd');
424 :     my $proctorKey = $r->param('proctor_key');
425 :    
426 :     # we use the following to require a second proctor authorization to grade the test
427 :     my $submitAnswers = defined($r->param('submitAnswers'))
428 :     ? $r->param('submitAnswers')
429 :     : '';
430 :    
431 :     my $failWithoutError = 0;
432 :     my $error = '';
433 :    
434 :     # we define a key for "effectiveuser,proctoruser" to authorize a test, and
435 :     # "effectiveuser,proctoruser,g" to authorize grading.
436 :     my $prKeyIndex = '';
437 :    
438 :     VERIFY: {
439 :     unless(
440 :     defined $proctorUser && $proctorUser
441 :     or
442 :     defined $proctorPasswd && $proctorPasswd
443 :     or
444 :     defined $proctorKey && $proctorKey
445 :     ) {
446 :     $failWithoutError = 1;
447 :     last VERIFY;
448 :     }
449 :    
450 :     unless(defined $proctorUser) {
451 :     $error = 'Proctor username must be specified.';
452 :     last VERIFY;
453 :     }
454 :    
455 :     my $Proctor = $db->getUser($proctorUser);
456 :     unless(defined $Proctor) {
457 : sh002i 3799 $self->write_log_entry("PROCTOR LOGIN FAILED $proctorUser - user unknown");
458 :     $error = "Invalid user ID or password";
459 : sh002i 3688 last VERIFY;
460 :     }
461 :    
462 :     # make sure proctor has valid status
463 :     unless($ce->status_abbrev_has_behavior($Proctor->status, "allow_course_access")) {
464 : sh002i 3799 $self->write_log_entry("PROCTOR LOGIN FAILED $proctorUser - course access denied");
465 :     $error = GENERIC_ERROR_MESSAGE;
466 : sh002i 3688 last VERIFY;
467 :     }
468 :    
469 :     if ($proctorKey) {
470 :     $r->param('proctor_password', '');
471 :    
472 :     $prKeyIndex = "$user,$proctorUser" . (($submitAnswers) ? ',g' : '');
473 :     if ($self->checkKey($prKeyIndex, $proctorKey)) {
474 :     last VERIFY;
475 :     } else {
476 :     if ($submitAnswers) {
477 :     $error = 'Assignment requires valid proctor authorization for grading';
478 :     } else {
479 :     $error = "Invalid or expired proctor session key.";
480 :     }
481 :     last VERIFY;
482 :     }
483 :     }
484 :    
485 :     if ($proctorPasswd) {
486 :     if ($self->checkPassword($proctorUser, $proctorPasswd)) {
487 :     $prKeyIndex = "$user,$proctorUser" . (($submitAnswers) ? ',g' : '');
488 :     my $newKeyObject = $self->generateKey( $prKeyIndex );
489 :     $r->param('proctor_passwd', '');
490 :    
491 :     eval{ $db->deleteKey($prKeyIndex); };
492 :     $db->addKey($newKeyObject);
493 :    
494 :     $r->param('proctor_key', $newKeyObject->key);
495 : glarose 4029
496 :     my $atype = ( $submitAnswers ) ? '(GRADING)' : '(LOGIN)';
497 : sh002i 3688
498 : glarose 4029 $self->write_log_entry("PROCTOR AUTHORIZATION $atype OK $proctorUser authorizing $user - valid password");
499 :    
500 : sh002i 3688 last VERIFY;
501 :     } else {
502 : glarose 4029 my $atype = ( $submitAnswers ) ? '(GRADING)' : '(LOGIN)';
503 :     $self->write_log_entry("PROCTOR AUTHORIZATION $atype FAILED $proctorUser authorizing $user - invalid password");
504 :    
505 : sh002i 3688 $error = 'Incorrect proctor username or password.';
506 :     last VERIFY;
507 :     }
508 :     }
509 :     }
510 :    
511 :     if (defined $error and $error) {
512 :     $r->notes("authen_error", $error);
513 :     return 0;
514 :     } elsif ($failWithoutError) {
515 :     return 0;
516 :     } else {
517 :     return 1;
518 :     }
519 : glarose 3377 }
520 :    
521 : sh002i 3741 =back
522 :    
523 :     =cut
524 :    
525 :     ################################################################################
526 :     # Password management
527 :     ################################################################################
528 :    
529 :     sub checkPassword($$$) {
530 :     my ($self, $userID, $possibleClearPassword) = @_;
531 :     my $db = $self->{r}->db;
532 :    
533 :     my $Password = $db->getPassword($userID); # checked
534 :     return 0 unless defined $Password;
535 :    
536 :     # check against WW password database
537 :     my $possibleCryptPassword = crypt($possibleClearPassword, $Password->password());
538 :     return 1 if $possibleCryptPassword eq $Password->password;
539 :    
540 :     # check site-specific verification method
541 :     return 1 if $self->site_checkPassword($userID, $possibleClearPassword);
542 :    
543 :     # fail by default
544 :     return 0;
545 :     }
546 :    
547 :     # Site-specific password checking
548 :     #
549 :     # The site_checkPassword routine can be used to provide a hook to your institution's
550 :     # authentication system. If authentication against the course's password database, the
551 :     # method $self->site_checkPassword($userID, $clearTextPassword) is called. If this
552 :     # method returns a true value, authentication succeeds.
553 :     #
554 :     # Here is an example site_checkPassword which checks the password against the Ohio State
555 :     # popmail server:
556 :     # sub site_checkPassword($$) {
557 :     # my ($self, $userID, $clearTextPassword) = @_;
558 :     # use Net::POP3;
559 :     # my $pop = Net::POP3->new('pop.service.ohio-state.edu', Timeout => 60);
560 :     # if ($pop->login($userID, $clearTextPassword)) {
561 :     # return 1;
562 :     # }
563 :     # return 0;
564 :     # }
565 :     #
566 :     # Since you have access to the WeBWorK::Authen object, the possibilities are limitless!
567 :     # This example checks the password against the system password database and updates the
568 :     # user's password in the course database if it succeeds:
569 :     # sub site_checkPassword {
570 :     # my ($self, $userID, $clearTextPassword) = @_;
571 :     # my $realCryptPassword = (getpwnam $userID)[1] or return 0;
572 :     # my $possibleCryptPassword = crypt($possibleClearPassword, $realCryptPassword); # user real PW as salt
573 :     # if ($possibleCryptPassword eq $realCryptPassword) {
574 :     # # update WeBWorK password
575 :     # use WeBWorK::Utils qw(cryptPassword);
576 :     # my $db = $self->{r}->db;
577 :     # my $Password = $db->getPassword($userID);
578 :     # my $pass = cryptPassword($clearTextPassword);
579 :     # $Password->password($pass);
580 :     # $db->putPassword($Password);
581 :     # return 1;
582 :     # } else {
583 :     # return 0;
584 :     # }
585 :     # }
586 :     #
587 :     #
588 :     # The default site_checkPassword always fails:
589 :     sub site_checkPassword {
590 :     my ($self, $userID, $clearTextPassword) = @_;
591 :     return 0;
592 :     }
593 :    
594 :     ################################################################################
595 :     # Session key management
596 :     ################################################################################
597 :    
598 :     sub generateKey($$) {
599 :     my ($self, $userID) = @_;
600 :     my $ce = $self->{r}->ce;
601 :    
602 :     my @chars = @{ $ce->{sessionKeyChars} };
603 :     my $length = $ce->{sessionKeyLength};
604 :    
605 :     srand;
606 :     my $key = join ("", @chars[map rand(@chars), 1 .. $length]);
607 :     return WeBWorK::DB::Record::Key->new(user_id=>$userID, key=>$key, timestamp=>time);
608 :     }
609 :    
610 :     sub checkKey($$$) {
611 :     my ($self, $userID, $possibleKey) = @_;
612 :     my $ce = $self->{r}->ce;
613 :     my $db = $self->{r}->db;
614 :    
615 :     my $Key = $db->getKey($userID); # checked
616 :     return 0 unless defined $Key;
617 :     if (time <= $Key->timestamp()+$ce->{sessionKeyTimeout}) {
618 :     if ($possibleKey eq $Key->key()) {
619 :     # unexpired and matches -- update timestamp
620 :     $Key->timestamp(time);
621 :     $db->putKey($Key);
622 :     return 1;
623 :     } else {
624 :     # unexpired but doesn't match -- leave timestamp alone
625 :     # we do this to keep an attacker from keeping someone's session
626 :     # alive. (yeah, we don't match IPs.)
627 :     return 0;
628 :     }
629 :     } else {
630 :     # expired -- delete key
631 :     $db->deleteKey($userID);
632 :     return 0;
633 :     }
634 :     }
635 :    
636 :     sub unexpiredKeyExists($$) {
637 :     my ($self, $userID) = @_;
638 :     my $ce = $self->{r}->ce;
639 :     my $db = $self->{r}->db;
640 :    
641 :     my $Key = $db->getKey($userID); # checked
642 :     return 0 unless defined $Key;
643 :     if (time <= $Key->timestamp()+$ce->{sessionKeyTimeout}) {
644 :     # unexpired, but leave timestamp alone
645 :     return 1;
646 :     } else {
647 :     # expired -- delete key
648 :     $db->deleteKey($userID);
649 :     return 0;
650 :     }
651 :     }
652 :    
653 :     ################################################################################
654 :     # Cookie management
655 :     ################################################################################
656 :    
657 :     sub fetchCookie {
658 :     my ($self, $user, $key) = @_;
659 :     my $r = $self->{r};
660 :     my $ce = $r->ce;
661 :     my $urlpath = $r->urlpath;
662 :    
663 :     my $courseID = $urlpath->arg("courseID");
664 :    
665 :     my %cookies = Apache::Cookie->fetch;
666 :     my $cookie = $cookies{"WeBWorKCourseAuthen.$courseID"};
667 :    
668 :     if ($cookie) {
669 :     #warn __PACKAGE__, ": fetchCookie: found a cookie for this course: \"", $cookie->as_string, "\"\n";
670 :     #warn __PACKAGE__, ": fetchCookie: cookie has this value: \"", $cookie->value, "\"\n";
671 :     my ($userID, $key) = split "\t", $cookie->value;
672 :     if (defined $userID and defined $key and $userID ne "" and $key ne "") {
673 :     #warn __PACKAGE__, ": fetchCookie: looks good, returning userID=$userID key=$key\n";
674 :     return $userID, $key;
675 :     } else {
676 :     #warn __PACKAGE__, ": fetchCookie: malformed cookie. returning empty strings.\n";
677 :     return "", "";
678 :     }
679 :     } else {
680 :     #warn __PACKAGE__, ": fetchCookie: found no cookie for this course. returning empty strings.\n";
681 :     return "", "";
682 :     }
683 :     }
684 :    
685 :     sub sendCookie {
686 :     my ($self, $userID, $key) = @_;
687 :     my $r = $self->{r};
688 :     my $ce = $r->ce;
689 :    
690 :     my $courseID = $r->urlpath->arg("courseID");
691 :    
692 :     my $expires = time2str("%a, %d-%h-%Y %H:%M:%S %Z", time+COOKIE_LIFESPAN, "GMT");
693 :     my $cookie = Apache::Cookie->new($r,
694 :     -name => "WeBWorKCourseAuthen.$courseID",
695 :     -value => "$userID\t$key",
696 :     -expires => $expires,
697 :     -domain => $r->hostname,
698 :     -path => $ce->{webworkURLRoot},
699 :     -secure => 0,
700 :     );
701 :     my $cookieString = $cookie->as_string;
702 :    
703 :     #warn __PACKAGE__, ": sendCookie: about to add Set-Cookie header with this string: \"", $cookie->as_string, "\"\n";
704 :     $r->headers_out->set("Set-Cookie" => $cookie->as_string);
705 :     }
706 :    
707 :     sub killCookie {
708 :     my ($self) = @_;
709 :     my $r = $self->{r};
710 :     my $ce = $r->ce;
711 :    
712 :     my $courseID = $r->urlpath->arg("courseID");
713 :    
714 :     my $expires = time2str("%a, %d-%h-%Y %H:%M:%S %Z", time-60*60*24, "GMT");
715 :     my $cookie = Apache::Cookie->new($r,
716 :     -name => "WeBWorKCourseAuthen.$courseID",
717 :     -value => "\t",
718 :     -expires => $expires,
719 :     -domain => $r->hostname,
720 :     -path => $ce->{webworkURLRoot},
721 :     -secure => 0,
722 :     );
723 :     my $cookieString = $cookie->as_string;
724 :    
725 :     #warn __PACKAGE__, ": killCookie: about to add Set-Cookie header with this string: \"", $cookie->as_string, "\"\n";
726 :     $r->headers_out->set("Set-Cookie" => $cookie->as_string);
727 :     }
728 :    
729 :     ################################################################################
730 :     # Utilities
731 :     ################################################################################
732 :    
733 : sh002i 3799 sub write_log_entry {
734 :     my ($self, $message) = @_;
735 :    
736 : sh002i 3741 my $r = $self->{r};
737 :     my $ce = $r->ce;
738 : sh002i 3799
739 :     my ($remote_port, $remote_host) = unpack_sockaddr_in($r->connection->remote_addr);
740 :     $remote_host = defined $remote_host ? inet_ntoa($remote_host) : "UNKNOWN";
741 :     $remote_port = "UNKNOWN" unless defined $remote_port;
742 : sh002i 3741 my $user_agent = $r->header_in("User-Agent");
743 : sh002i 3799
744 :     writeCourseLog($ce, "login_log", "$message (host=$remote_host port=$remote_port UA=$user_agent");
745 : sh002i 3741 }
746 :    
747 : malsyned 305 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9