[system] / trunk / webwork-modperl / lib / WeBWorK / Authen.pm Repository:
ViewVC logotype

Annotation of /trunk/webwork-modperl/lib/WeBWorK/Authen.pm

Parent Directory Parent Directory | Revision Log Revision Log


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

1 : sh002i 455 ################################################################################
2 : sh002i 1663 # WeBWorK Online Homework Delivery System
3 : sh002i 5319 # Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
4 :     # $CVSHeader: webwork2/lib/WeBWorK/Authen.pm,v 1.62 2007/03/06 22:03:15 glarose 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 : sh002i 4303 =head1 SYNOPSIS
24 :    
25 :     # get the name of the appropriate Authen class, based on the %authen hash in $ce
26 :     my $class_name = WeBWorK::Authen::class($ce, "user_module");
27 :    
28 :     # load that class
29 :     require $class_name;
30 :    
31 :     # create an authen object
32 :     my $authen = $class_name->new($r);
33 :    
34 :     # verify credentials
35 :     $authen->verify or die "Authentication failed";
36 :    
37 :     # verification status is stored for quick retrieval later
38 :     my $auth_ok = $authen->was_verified;
39 :    
40 :     # for some reason, you might want to clear that cache
41 :     $authen->forget_verification;
42 :    
43 :     =head1 DESCRIPTION
44 :    
45 :     WeBWorK::Authen is the base class for all WeBWorK authentication classes. It
46 :     provides default authentication behavior which can be selectively overridden in
47 :     subclasses.
48 :    
49 : sh002i 455 =cut
50 :    
51 : malsyned 441 use strict;
52 :     use warnings;
53 : sh002i 4192 use WeBWorK::Cookie;
54 : sh002i 1777 use Date::Format;
55 : sh002i 4045 use Socket qw/unpack_sockaddr_in inet_ntoa/; # for logging
56 :     use WeBWorK::Debug;
57 : sh002i 3799 use WeBWorK::Utils qw/writeCourseLog/;
58 : malsyned 335
59 : sh002i 1777 use constant COOKIE_LIFESPAN => 60*60*24*30; # 30 days
60 : sh002i 3799 use constant GENERIC_ERROR_MESSAGE => "Invalid user ID or password.";
61 : sh002i 1777
62 : sh002i 4192 use mod_perl;
63 :     use constant MP2 => ( exists $ENV{MOD_PERL_API_VERSION} and $ENV{MOD_PERL_API_VERSION} >= 2 );
64 :    
65 : sh002i 4263 BEGIN {
66 :     if (MP2) {
67 :     require APR::SockAddr;
68 :     APR::SockAddr->import();
69 :     require Apache2::Connection;
70 :     Apache2::Connection->import();
71 :     }
72 :     }
73 :    
74 : sh002i 3741 ################################################################################
75 :     # Public API
76 :     ################################################################################
77 :    
78 : sh002i 4303 =head1 FACTORY
79 :    
80 :     =over
81 :    
82 :     =item class($ce, $type)
83 :    
84 :     This subroutine consults the given WeBWorK::CourseEnvironment object to
85 :     determine which WeBWorK::Authen subclass should be used. $type can be any key
86 :     given in the %authen hash in the course environment. If the type is not found in
87 :     the %authen hash, an exception is thrown.
88 :    
89 :     =cut
90 :    
91 :     sub class {
92 :     my ($ce, $type) = @_;
93 :    
94 :     if (exists $ce->{authen}{$type}) {
95 :     if (ref $ce->{authen}{$type} eq "HASH") {
96 :     if (exists $ce->{authen}{$type}{$ce->{dbLayoutName}}) {
97 :     return $ce->{authen}{$type}{$ce->{dbLayoutName}};
98 :     } elsif (exists $ce->{authen}{$type}{"*"}) {
99 :     return $ce->{authen}{$type}{"*"};
100 :     } else {
101 :     die "authentication type '$type' in %authen hash in course environemnt has no entry for db layout '", $ce->{dbLayoutName}, "' and no default entry (*)";
102 :     }
103 :     } else {
104 :     return $ce->{authen}{$type};
105 :     }
106 :     } else {
107 :     die "authentication type '$type' not found in course environment \%authen hash";
108 :     }
109 :     }
110 :    
111 :     =back
112 :    
113 :     =cut
114 :    
115 : sh002i 3741 =head1 CONSTRUCTOR
116 :    
117 :     =over
118 :    
119 :     =item new($r)
120 :    
121 :     Instantiates a new WeBWorK::Authen object for the given WeBWorK::Requst ($r).
122 :    
123 :     =cut
124 :    
125 : sh002i 1885 sub new {
126 :     my ($invocant, $r) = @_;
127 : malsyned 323 my $class = ref($invocant) || $invocant;
128 : sh002i 1885 my $self = {
129 :     r => $r,
130 :     };
131 : malsyned 305 bless $self, $class;
132 :     return $self;
133 :     }
134 :    
135 : sh002i 3741 =back
136 : sh002i 817
137 : sh002i 3741 =cut
138 : sh002i 3366
139 : sh002i 3741 =head1 METHODS
140 : sh002i 817
141 : sh002i 3741 =over
142 : malsyned 323
143 : sh002i 3741 =cut
144 : sh002i 1683
145 : sh002i 4045 sub verify {
146 :     debug("BEGIN VERIFY");
147 : malsyned 305 my $self = shift;
148 :     my $r = $self->{r};
149 :    
150 : sh002i 4045 my $result = $self->do_verify;
151 :     my $error = $self->{error};
152 : sh002i 4646 my $log_error = $self->{log_error};
153 : sh002i 817
154 : sh002i 4045 $self->{was_verified} = $result ? 1 : 0;
155 : malsyned 313
156 : sh002i 4045 if ($self->can("site_fixup")) {
157 :     $self->site_fixup;
158 : gage 3125 }
159 : sh002i 817
160 : sh002i 4045 if ($result) {
161 : sh002i 4646 $self->write_log_entry("LOGIN OK") if $self->{initial_login};
162 : sh002i 4045 $self->maybe_send_cookie;
163 :     $self->set_params;
164 : malsyned 313 } else {
165 : sh002i 4646 if (defined $log_error) {
166 :     $self->write_log_entry("LOGIN FAILED $log_error");
167 :     }
168 : sh002i 4135 $self->maybe_kill_cookie;
169 : sh002i 4045 if ($error) {
170 : sh002i 4192 MP2 ? $r->notes->set(authen_error => $error) : $r->notes("authen_error" => $error);
171 : sh002i 1683 }
172 : malsyned 305 }
173 : sh002i 4045
174 :     debug("END VERIFY");
175 :     return $result;
176 : malsyned 305 }
177 :    
178 : sh002i 3741 =item was_verified()
179 :    
180 :     Returns true if verify() returned true the last time it was called.
181 :    
182 :     =cut
183 :    
184 :     sub was_verified {
185 :     my ($self) = @_;
186 :    
187 :     return 1 if exists $self->{was_verified} and $self->{was_verified};
188 :     return 0;
189 :     }
190 :    
191 :     =item forget_verification()
192 :    
193 :     Future calls to was_verified() will return false, until verify() is called again and succeeds.
194 :    
195 :     =cut
196 :    
197 :     sub forget_verification {
198 :     my ($self) = @_;
199 :    
200 :     $self->{was_verified} = 0;
201 :     }
202 :    
203 : sh002i 4045 =back
204 : sh002i 3741
205 :     =cut
206 :    
207 : sh002i 4045 ################################################################################
208 :     # Helper functions (called by verify)
209 :     ################################################################################
210 :    
211 :     sub do_verify {
212 :     my $self = shift;
213 : sh002i 3688 my $r = $self->{r};
214 :     my $ce = $r->ce;
215 :     my $db = $r->db;
216 :    
217 : sh002i 4045 return 0 unless $db;
218 : sh002i 3688
219 : sh002i 4045 return 0 unless $self->get_credentials;
220 : sh002i 3688
221 : sh002i 4045 return 0 unless $self->check_user;
222 : sh002i 3688
223 : sh002i 4045 my $practiceUserPrefix = $ce->{practiceUserPrefix};
224 : glarose 4850 if (defined($self->{login_type}) && $self->{login_type} eq "guest"){
225 : sh002i 4045 return $self->verify_practice_user;
226 :     } else {
227 :     return $self->verify_normal_user;
228 :     }
229 :     }
230 :    
231 :     sub get_credentials {
232 :     my ($self) = @_;
233 :     my $r = $self->{r};
234 :     my $ce = $r->ce;
235 :     my $db = $r->db;
236 : sh002i 3688
237 : sh002i 4045 # allow guest login: if the "Guest Login" button was clicked, we find an unused
238 :     # practice user and create a session for it.
239 :     if ($r->param("login_practice_user")) {
240 :     my $practiceUserPrefix = $ce->{practiceUserPrefix};
241 : sh002i 4518 # DBFIX search should happen in database
242 : sh002i 4045 my @guestUserIDs = grep m/^$practiceUserPrefix/, $db->listUsers;
243 :     my @GuestUsers = $db->getUsers(@guestUserIDs);
244 :     my @allowedGuestUsers = grep { $ce->status_abbrev_has_behavior($_->status, "allow_course_access") } @GuestUsers;
245 :     my @allowedGestUserIDs = map { $_->user_id } @allowedGuestUsers;
246 : sh002i 3688
247 : sh002i 4045 foreach my $userID (@allowedGestUserIDs) {
248 :     if (not $self->unexpired_session_exists($userID)) {
249 :     my $newKey = $self->create_session($userID);
250 : sh002i 4646 $self->{initial_login} = 1;
251 : sh002i 4045
252 :     $self->{user_id} = $userID;
253 :     $self->{session_key} = $newKey;
254 : sh002i 4646 $self->{login_type} = "guest";
255 :     $self->{credential_source} = "none";
256 : sh002i 4045 debug("guest user '", $userID. "' key '", $newKey. "'");
257 :     return 1;
258 :     }
259 : sh002i 3688 }
260 :    
261 : sh002i 4646 $self->{log_error} = "no guest logins are available";
262 :     $self->{error} = "No guest logins are available. Please try again in a few minutes.";
263 : sh002i 4045 return 0;
264 :     }
265 :    
266 :     # at least the user ID is available in request parameters
267 :     if (defined $r->param("user")) {
268 :     $self->{user_id} = $r->param("user");
269 :     $self->{session_key} = $r->param("key");
270 :     $self->{password} = $r->param("passwd");
271 : sh002i 4646 $self->{login_type} = "normal";
272 : sh002i 4045 $self->{credential_source} = "params";
273 :     debug("params user '", $self->{user_id}, "' password '", $self->{password}, "' key '", $self->{session_key}, "'");
274 :     return 1;
275 :     }
276 :    
277 :     my ($cookieUser, $cookieKey) = $self->fetchCookie;
278 :     if (defined $cookieUser) {
279 :     $self->{user_id} = $cookieUser;
280 :     $self->{session_key} = $cookieKey;
281 : sh002i 4646 $self->{login_type} = "normal";
282 : sh002i 4045 $self->{credential_source} = "cookie";
283 :     debug("cookie user '", $self->{user_id}, "' key '", $self->{session_key}, "'");
284 :     return 1;
285 :     }
286 :     }
287 :    
288 :     sub check_user {
289 :     my $self = shift;
290 :     my $r = $self->{r};
291 :     my $ce = $r->ce;
292 :     my $db = $r->db;
293 :     my $authz = $r->authz;
294 :    
295 :     my $user_id = $self->{user_id};
296 :    
297 :     if (defined $user_id and $user_id eq "") {
298 : sh002i 4646 $self->{log_error} = "no user id specified";
299 : sh002i 4045 $self->{error} = "You must specify a user ID.";
300 :     return 0;
301 :     }
302 :    
303 :     my $User = $db->getUser($user_id);
304 :    
305 :     unless ($User) {
306 : sh002i 4646 $self->{log_error} = "user unknown";
307 : sh002i 4045 $self->{error} = GENERIC_ERROR_MESSAGE;
308 :     return 0;
309 :     }
310 :    
311 :     # FIXME "fix invalid status values" used to be here, but it needs to move to $db->getUser
312 :    
313 :     unless ($ce->status_abbrev_has_behavior($User->status, "allow_course_access")) {
314 : sh002i 4646 $self->{log_error} = "user not allowed course access";
315 : sh002i 4045 $self->{error} = GENERIC_ERROR_MESSAGE;
316 :     return 0;
317 :     }
318 :    
319 :     unless ($authz->hasPermissions($user_id, "login")) {
320 : sh002i 4646 $self->{log_error} = "user not permitted to login";
321 : sh002i 4045 $self->{error} = GENERIC_ERROR_MESSAGE;
322 :     return 0;
323 :     }
324 :    
325 :     return 1;
326 :     }
327 :    
328 :     sub verify_practice_user {
329 :     my $self = shift;
330 :     my $r = $self->{r};
331 :     my $ce = $r->ce;
332 :    
333 :     my $user_id = $self->{user_id};
334 :     my $session_key = $self->{session_key};
335 :    
336 :     my ($sessionExists, $keyMatches, $timestampValid) = $self->check_session($user_id, $session_key, 1);
337 :     debug("sessionExists='", $sessionExists, "' keyMatches='", $keyMatches, "' timestampValid='", $timestampValid, "'");
338 :    
339 :     if ($sessionExists) {
340 :     if ($keyMatches) {
341 :     if ($timestampValid) {
342 :     return 1;
343 : sh002i 3688 } else {
344 : sh002i 4045 $self->{session_key} = $self->create_session($user_id);
345 : sh002i 4646 $self->{initial_login} = 1;
346 : sh002i 4045 return 1;
347 :     }
348 :     } else {
349 :     if ($timestampValid) {
350 :     my $debugPracticeUser = $ce->{debugPracticeUser};
351 :     if (defined $debugPracticeUser and $user_id eq $debugPracticeUser) {
352 :     $self->{session_key} = $self->create_session($user_id);
353 : sh002i 4646 $self->{initial_login} = 1;
354 : sh002i 4045 return 1;
355 : sh002i 3688 } else {
356 : sh002i 4646 $self->{log_error} = "guest account in use";
357 : sh002i 4045 $self->{error} = "That guest account is in use.";
358 :     return 0;
359 : sh002i 3688 }
360 : sh002i 4045 } else {
361 :     $self->{session_key} = $self->create_session($user_id);
362 : sh002i 4646 $self->{initial_login} = 1;
363 : sh002i 4045 return 1;
364 : sh002i 3688 }
365 :     }
366 : sh002i 4045 } else {
367 :     $self->{session_key} = $self->create_session($user_id);
368 : sh002i 4646 $self->{initial_login} = 1;
369 : sh002i 4045 return 1;
370 :     }
371 :     }
372 : glarose 4029
373 : sh002i 4045 sub verify_normal_user {
374 :     my $self = shift;
375 :     my $r = $self->{r};
376 :    
377 :     my $user_id = $self->{user_id};
378 :     my $session_key = $self->{session_key};
379 :    
380 :     my ($sessionExists, $keyMatches, $timestampValid) = $self->check_session($user_id, $session_key, 1);
381 :     debug("sessionExists='", $sessionExists, "' keyMatches='", $keyMatches, "' timestampValid='", $timestampValid, "'");
382 :    
383 :     if ($keyMatches and $timestampValid) {
384 :     return 1;
385 :     } else {
386 :     my $auth_result = $self->authenticate;
387 :    
388 :     if ($auth_result > 0) {
389 :     $self->{session_key} = $self->create_session($user_id);
390 : sh002i 4646 $self->{initial_login} = 1;
391 : sh002i 4045 return 1;
392 :     } elsif ($auth_result == 0) {
393 : sh002i 4646 $self->{log_error} = "authentication failed";
394 : sh002i 4045 $self->{error} = GENERIC_ERROR_MESSAGE;
395 :     return 0;
396 :     } else { # ($auth_result < 0) => required data was not present
397 :     if ($keyMatches and not $timestampValid) {
398 :     $self->{error} = "Your session has timed out due to inactivity. Please log in again.";
399 : sh002i 3688 }
400 : sh002i 4045 return 0;
401 : sh002i 3688 }
402 :     }
403 : sh002i 4045 }
404 :    
405 :     # 1 == authentication succeeded
406 :     # 0 == required data was present, but authentication failed
407 :     # -1 == required data was not present (i.e. password missing)
408 :     sub authenticate {
409 :     my $self = shift;
410 :     my $r = $self->{r};
411 : sh002i 3688
412 : sh002i 4045 my $user_id = $self->{user_id};
413 :     my $password = $self->{password};
414 :    
415 :     if (defined $password) {
416 :     return $self->checkPassword($user_id, $password);
417 : sh002i 3688 } else {
418 : sh002i 4045 return -1;
419 : sh002i 3688 }
420 : glarose 3377 }
421 :    
422 : sh002i 4045 sub maybe_send_cookie {
423 :     my $self = shift;
424 :     my $r = $self->{r};
425 :    
426 :     my ($cookie_user, $cookie_key) = $self->fetchCookie;
427 :    
428 :     # we send a cookie if any of these conditions are met:
429 :    
430 :     # (a) a cookie was used for authentication
431 :     my $used_cookie = ($self->{credential_source} eq "cookie");
432 :    
433 :     # (b) a cookie was sent but not used for authentication, and the
434 :     # credentials used for authentication were the same as those in
435 :     # the cookie
436 :     my $unused_valid_cookie = ($self->{credential_source} ne "cookie"
437 :     and defined $cookie_user and $self->{user_id} eq $cookie_user
438 :     and defined $cookie_key and $self->{session_key} eq $cookie_key);
439 :    
440 :     # (c) the user asked to have a cookie sent and is not a guest user.
441 : sh002i 4646 my $user_requests_cookie = ($self->{login_type} ne "guest"
442 : sh002i 4045 and $r->param("send_cookie"));
443 :    
444 :     debug("used_cookie='", $used_cookie, "' unused_valid_cookie='", $unused_valid_cookie, "' user_requests_cookie='", $user_requests_cookie, "'");
445 :    
446 :     if ($used_cookie or $unused_valid_cookie or $user_requests_cookie) {
447 :     $self->sendCookie($self->{user_id}, $self->{session_key});
448 :     } else {
449 :     $self->killCookie;
450 :     }
451 :     }
452 : sh002i 3741
453 : sh002i 4135 sub maybe_kill_cookie {
454 :     my $self = shift;
455 :     $self->killCookie(@_);
456 :     }
457 :    
458 : sh002i 4045 sub set_params {
459 :     my $self = shift;
460 :     my $r = $self->{r};
461 :    
462 : sh002i 4192 # A2 - params are not non-modifiable, with no explanation or workaround given in docs. WTF!
463 : sh002i 4045 $r->param("user", $self->{user_id});
464 :     $r->param("key", $self->{session_key});
465 :     $r->param("passwd", "");
466 :    
467 :     debug("params user='", $r->param("user"), "' key='", $r->param("key"), "' passwd='", $r->param("passwd"), "'");
468 :     }
469 : sh002i 3741
470 :     ################################################################################
471 :     # Password management
472 :     ################################################################################
473 :    
474 : sh002i 4045 sub checkPassword {
475 : sh002i 3741 my ($self, $userID, $possibleClearPassword) = @_;
476 :     my $db = $self->{r}->db;
477 :    
478 :     my $Password = $db->getPassword($userID); # checked
479 : sh002i 4646 if (defined $Password) {
480 :     # check against WW password database
481 :     my $possibleCryptPassword = crypt $possibleClearPassword, $Password->password;
482 :     if ($possibleCryptPassword eq $Password->password) {
483 :     $self->write_log_entry("AUTH WWDB: password accepted");
484 :     return 1;
485 :     } else {
486 :     if ($self->can("site_checkPassword")) {
487 :     $self->write_log_entry("AUTH WWDB: password rejected, deferring to site_checkPassword");
488 :     return $self->site_checkPassword($userID, $possibleClearPassword);
489 :     } else {
490 :     $self->write_log_entry("AUTH WWDB: password rejected");
491 :     return 0;
492 :     }
493 :     }
494 :     } else {
495 :     $self->write_log_entry("AUTH WWDB: user has no password record");
496 :     return 0;
497 :     }
498 : sh002i 3741 }
499 :    
500 :     # Site-specific password checking
501 :     #
502 :     # The site_checkPassword routine can be used to provide a hook to your institution's
503 :     # authentication system. If authentication against the course's password database, the
504 :     # method $self->site_checkPassword($userID, $clearTextPassword) is called. If this
505 :     # method returns a true value, authentication succeeds.
506 :     #
507 :     # Here is an example site_checkPassword which checks the password against the Ohio State
508 :     # popmail server:
509 : sh002i 4045 # sub site_checkPassword {
510 : sh002i 3741 # my ($self, $userID, $clearTextPassword) = @_;
511 :     # use Net::POP3;
512 :     # my $pop = Net::POP3->new('pop.service.ohio-state.edu', Timeout => 60);
513 :     # if ($pop->login($userID, $clearTextPassword)) {
514 :     # return 1;
515 :     # }
516 :     # return 0;
517 :     # }
518 :     #
519 :     # Since you have access to the WeBWorK::Authen object, the possibilities are limitless!
520 :     # This example checks the password against the system password database and updates the
521 :     # user's password in the course database if it succeeds:
522 :     # sub site_checkPassword {
523 :     # my ($self, $userID, $clearTextPassword) = @_;
524 :     # my $realCryptPassword = (getpwnam $userID)[1] or return 0;
525 :     # my $possibleCryptPassword = crypt($possibleClearPassword, $realCryptPassword); # user real PW as salt
526 :     # if ($possibleCryptPassword eq $realCryptPassword) {
527 :     # # update WeBWorK password
528 :     # use WeBWorK::Utils qw(cryptPassword);
529 :     # my $db = $self->{r}->db;
530 :     # my $Password = $db->getPassword($userID);
531 :     # my $pass = cryptPassword($clearTextPassword);
532 :     # $Password->password($pass);
533 :     # $db->putPassword($Password);
534 :     # return 1;
535 :     # } else {
536 :     # return 0;
537 :     # }
538 :     # }
539 :    
540 :     ################################################################################
541 :     # Session key management
542 :     ################################################################################
543 :    
544 : sh002i 4045 sub unexpired_session_exists {
545 : sh002i 3741 my ($self, $userID) = @_;
546 :     my $ce = $self->{r}->ce;
547 :     my $db = $self->{r}->db;
548 :    
549 :     my $Key = $db->getKey($userID); # checked
550 :     return 0 unless defined $Key;
551 :     if (time <= $Key->timestamp()+$ce->{sessionKeyTimeout}) {
552 : sh002i 4045 # unexpired, but leave timestamp alone
553 :     return 1;
554 : sh002i 3741 } else {
555 :     # expired -- delete key
556 : sh002i 4045 # NEW: no longer delete the key here -- a user re-visiting with a formerly-valid key should
557 :     # always get a "session expired" message. formerly, if they i.e. reload the login screen
558 :     # the message disappears, which is confusing (i claim ;)
559 :     #$db->deleteKey($userID);
560 : sh002i 3741 return 0;
561 :     }
562 :     }
563 :    
564 : sh002i 4045 # clobbers any existing session for this $userID
565 :     # if $newKey is not specified, a random key is generated
566 :     # the key is returned
567 :     sub create_session {
568 :     my ($self, $userID, $newKey) = @_;
569 : sh002i 3741 my $ce = $self->{r}->ce;
570 :     my $db = $self->{r}->db;
571 :    
572 : sh002i 4045 my $timestamp = time;
573 :     unless ($newKey) {
574 :     my @chars = @{ $ce->{sessionKeyChars} };
575 :     my $length = $ce->{sessionKeyLength};
576 :    
577 :     srand;
578 :     $newKey = join ("", @chars[map rand(@chars), 1 .. $length]);
579 :     }
580 :    
581 :     my $Key = $db->newKey(user_id=>$userID, key=>$newKey, timestamp=>$timestamp);
582 : sh002i 4518 # DBFIXME this should be a REPLACE
583 : sh002i 4045 eval { $db->deleteKey($userID) };
584 :     $db->addKey($Key);
585 :     return $newKey;
586 :     }
587 :    
588 :     # returns ($sessionExists, $keyMatches, $timestampValid)
589 :     # if $updateTimestamp is true, the timestamp on a valid session is updated
590 :     sub check_session {
591 :     my ($self, $userID, $possibleKey, $updateTimestamp) = @_;
592 :     my $ce = $self->{r}->ce;
593 :     my $db = $self->{r}->db;
594 :    
595 : sh002i 3741 my $Key = $db->getKey($userID); # checked
596 :     return 0 unless defined $Key;
597 : sh002i 4045
598 :     my $keyMatches = (defined $possibleKey and $possibleKey eq $Key->key);
599 :     my $timestampValid = (time <= $Key->timestamp()+$ce->{sessionKeyTimeout});
600 :    
601 :     if ($keyMatches and $timestampValid and $updateTimestamp) {
602 :     $Key->timestamp(time);
603 :     $db->putKey($Key);
604 : sh002i 3741 }
605 : sh002i 4045
606 :     return (1, $keyMatches, $timestampValid);
607 : sh002i 3741 }
608 :    
609 :     ################################################################################
610 :     # Cookie management
611 :     ################################################################################
612 :    
613 :     sub fetchCookie {
614 : sh002i 4045 my $self = shift;
615 : sh002i 3741 my $r = $self->{r};
616 :     my $ce = $r->ce;
617 :     my $urlpath = $r->urlpath;
618 :    
619 :     my $courseID = $urlpath->arg("courseID");
620 :    
621 : sh002i 4192 # AP2 - Apache2::Cookie needs $r, Apache::Cookie doesn't
622 :     my %cookies = WeBWorK::Cookie->fetch( MP2 ? $r : () );
623 : sh002i 3741 my $cookie = $cookies{"WeBWorKCourseAuthen.$courseID"};
624 :    
625 :     if ($cookie) {
626 : sh002i 4045 debug("found a cookie for this course: '", $cookie->as_string, "'");
627 :     debug("cookie has this value: '", $cookie->value, "'");
628 : sh002i 3741 my ($userID, $key) = split "\t", $cookie->value;
629 :     if (defined $userID and defined $key and $userID ne "" and $key ne "") {
630 : sh002i 4045 debug("looks good, returning userID='$userID' key='$key'");
631 : sh002i 3741 return $userID, $key;
632 :     } else {
633 : sh002i 4045 debug("malformed cookie. returning nothing.");
634 :     return;
635 : sh002i 3741 }
636 :     } else {
637 : sh002i 4045 debug("found no cookie for this course. returning nothing.");
638 :     return;
639 : sh002i 3741 }
640 :     }
641 :    
642 :     sub sendCookie {
643 :     my ($self, $userID, $key) = @_;
644 :     my $r = $self->{r};
645 :     my $ce = $r->ce;
646 :    
647 :     my $courseID = $r->urlpath->arg("courseID");
648 :    
649 :     my $expires = time2str("%a, %d-%h-%Y %H:%M:%S %Z", time+COOKIE_LIFESPAN, "GMT");
650 : sh002i 4192 my $cookie = WeBWorK::Cookie->new($r,
651 : sh002i 3741 -name => "WeBWorKCourseAuthen.$courseID",
652 :     -value => "$userID\t$key",
653 :     -expires => $expires,
654 :     -domain => $r->hostname,
655 :     -path => $ce->{webworkURLRoot},
656 :     -secure => 0,
657 :     );
658 :    
659 : sh002i 4045 debug("about to add Set-Cookie header with this string: '", $cookie->as_string, "'");
660 : sh002i 3741 $r->headers_out->set("Set-Cookie" => $cookie->as_string);
661 :     }
662 :    
663 :     sub killCookie {
664 :     my ($self) = @_;
665 :     my $r = $self->{r};
666 :     my $ce = $r->ce;
667 :    
668 :     my $courseID = $r->urlpath->arg("courseID");
669 :    
670 :     my $expires = time2str("%a, %d-%h-%Y %H:%M:%S %Z", time-60*60*24, "GMT");
671 : sh002i 4192 my $cookie = WeBWorK::Cookie->new($r,
672 : sh002i 3741 -name => "WeBWorKCourseAuthen.$courseID",
673 :     -value => "\t",
674 :     -expires => $expires,
675 :     -domain => $r->hostname,
676 :     -path => $ce->{webworkURLRoot},
677 :     -secure => 0,
678 :     );
679 :    
680 : sh002i 4045 debug("about to add Set-Cookie header with this string: '", $cookie->as_string, "'");
681 : sh002i 3741 $r->headers_out->set("Set-Cookie" => $cookie->as_string);
682 :     }
683 :    
684 :     ################################################################################
685 :     # Utilities
686 :     ################################################################################
687 :    
688 : sh002i 3799 sub write_log_entry {
689 :     my ($self, $message) = @_;
690 : sh002i 3741 my $r = $self->{r};
691 :     my $ce = $r->ce;
692 : sh002i 3799
693 : sh002i 4646 my $user_id = defined $self->{user_id} ? $self->{user_id} : "";
694 :     my $login_type = defined $self->{login_type} ? $self->{login_type} : "";
695 :     my $credential_source = defined $self->{credential_source} ? $self->{credential_source} : "";
696 :    
697 : sh002i 4192 my ($remote_host, $remote_port);
698 :     if (MP2) {
699 :     $remote_host = $r->connection->remote_addr->ip_get || "UNKNOWN";
700 :     $remote_port = $r->connection->remote_addr->port || "UNKNOWN";
701 :     } else {
702 :     ($remote_port, $remote_host) = unpack_sockaddr_in($r->connection->remote_addr);
703 :     $remote_host = defined $remote_host ? inet_ntoa($remote_host) : "UNKNOWN";
704 :     $remote_port = "UNKNOWN" unless defined $remote_port;
705 :     }
706 : sh002i 4238 my $user_agent = $r->headers_in->{"User-Agent"};
707 : sh002i 3799
708 : sh002i 4646 my $log_msg = "$message user_id=$user_id login_type=$login_type credential_source=$credential_source host=$remote_host port=$remote_port UA=$user_agent";
709 : sh002i 4132 debug("Writing to login log: '$log_msg'.\n");
710 :     writeCourseLog($ce, "login_log", $log_msg);
711 : sh002i 3741 }
712 :    
713 : malsyned 305 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9