[system] / branches / rel-2-4-patches / webwork2 / lib / WeBWorK / Authen.pm Repository:
ViewVC logotype

View of /branches/rel-2-4-patches/webwork2/lib/WeBWorK/Authen.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1683 - (download) (as text) (annotate)
Tue Dec 23 06:03:33 2003 UTC (9 years, 5 months ago) by sh002i
Original Path: trunk/webwork2/lib/WeBWorK/Authen.pm
File size: 8712 byte(s)
added cookie support for authenticaion. the "WeBWorKAuthentication"
cookie is used to retrieve the "user" and "key" parameters. the cookie
is sent if a "send_cookie" parameter is present, and persists for 30
days unless canceled by an explicit "logout".

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader: webwork-modperl/lib/WeBWorK/Authen.pm,v 1.20 2003/12/09 01:12:30 sh002i Exp $
    5 #
    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 ################################################################################
   16 
   17 package WeBWorK::Authen;
   18 
   19 =head1 NAME
   20 
   21 WeBWorK::Authen - Check user identity, manage session keys.
   22 
   23 =cut
   24 
   25 use strict;
   26 use warnings;
   27 use Apache::Cookie;
   28 use Data::Dumper;
   29 
   30 sub new($$$) {
   31   my $invocant = shift;
   32   my $class = ref($invocant) || $invocant;
   33   my $self = {};
   34   ($self->{r}, $self->{ce}, $self->{db}) = @_;
   35   bless $self, $class;
   36   return $self;
   37 }
   38 
   39 sub checkPassword($$$) {
   40   my ($self, $userID, $possibleClearPassword) = @_;
   41   my $Password = $self->{db}->getPassword($userID); # checked
   42   return 0 unless defined $Password;
   43   my $possibleCryptPassword = crypt($possibleClearPassword, $Password->password());
   44   return $possibleCryptPassword eq $Password->password();
   45 }
   46 
   47 sub generateKey($$) {
   48   my ($self, $userID) = @_;
   49   my @chars = @{ $self->{ce}->{sessionKeyChars} };
   50   my $length = $self->{ce}->{sessionKeyLength};
   51   srand;
   52   my $key = join ("", @chars[map rand(@chars), 1 .. $length]);
   53   return WeBWorK::DB::Record::Key->new(user_id=>$userID, key=>$key, timestamp=>time);
   54 }
   55 
   56 sub checkKey($$$) {
   57   my ($self, $userID, $possibleKey) = @_;
   58   my $Key = $self->{db}->getKey($userID); # checked
   59   return 0 unless defined $Key;
   60   if (time <= $Key->timestamp()+$self->{ce}->{sessionKeyTimeout}) {
   61     if ($possibleKey eq $Key->key()) {
   62       # unexpired and matches -- update timestamp
   63       $Key->timestamp(time);
   64       $self->{db}->putKey($Key);
   65       return 1;
   66     } else {
   67       # unexpired but doesn't match -- leave timestamp alone
   68       # we do this to keep an attacker from keeping someone's session
   69       # alive. (yeah, we don't match IPs.)
   70       return 0;
   71     }
   72   } else {
   73     # expired -- delete key
   74     $self->{db}->deleteKey($userID);
   75     return 0;
   76   }
   77 }
   78 
   79 sub unexpiredKeyExists($$) {
   80   my ($self, $userID) = @_;
   81   my $Key = $self->{db}->getKey($userID); # checked
   82   return 0 unless defined $Key;
   83   if (time <= $Key->timestamp()+$self->{ce}->{sessionKeyTimeout}) {
   84     # unexpired, but leave timestamp alone
   85     return 1;
   86   } else {
   87     # expired -- delete key
   88     $self->{db}->deleteKey($userID);
   89     return 0;
   90   }
   91 }
   92 
   93 sub checkCookie {
   94   my ($self, $user, $key) = @_;
   95   my $r = $self->{r};
   96   my %cookies = Apache::Cookie->fetch;
   97   my $cookie = $cookies{WeBWorKAuthentication};
   98   if ($cookie) {
   99     my ($user, $key) = $cookie->value =~ m/^user=([^&]*)&key=([^&]*)$/;
  100     return $user, $key;
  101   }
  102 }
  103 
  104 sub sendCookie {
  105   my ($self, $user, $key) = @_;
  106   my $r = $self->{r};
  107   my $ce = $self->{ce};
  108   my $cookie = Apache::Cookie->new($r,
  109     -name => "WeBWorKAuthentication",
  110     -value => "user=$user&key=$key",
  111     -expires => "+30D",
  112     -domain => $r->hostname,
  113     -path => $ce->{webworkURLRoot},
  114     -secure => 0,
  115   );
  116   $r->headers_out->set("Set-Cookie" => $cookie->as_string);
  117 }
  118 
  119 # verify will return 1 if the person is who they say the are. If the
  120 # verification failed because of of invalid authentication data, a note will be
  121 # written in the request explaining why it failed. If the request failed because
  122 # no authentication data was provided, however, no note will be written, as this
  123 # is expected to happen whenever someone types in a URL manually, and is not
  124 # considered an error condition.
  125 sub verify($) {
  126   my $self = shift;
  127   my $r = $self->{r};
  128   my $ce = $self->{ce};
  129   my $db = $self->{db};
  130 
  131   my $practiceUserPrefix = $ce->{practiceUserPrefix};
  132   my $debugPracticeUser = $ce->{debugPracticeUser};
  133 
  134   my $user = $r->param('user');
  135   my $passwd = $r->param('passwd');
  136   my $key = $r->param('key');
  137   my $force_passwd_authen = $r->param('force_passwd_authen');
  138 
  139   my $error;
  140   my $failWithoutError = 0;
  141 
  142   VERIFY: {
  143     # This block is here so we can "last" out of it when we've
  144     # decided whether we're going to succeed or fail.
  145 
  146     # no authentication data was given. this is OK.
  147     unless (defined $user or defined $passwd or defined $key) {
  148       # check to see if a cookie was sent by the browser. if so, use the
  149       # user and key from the cookie for authentication. note that the
  150       # cookie is only used if no credentials are sent as parameters.
  151       my ($cookieUser, $cookieKey) = $self->checkCookie;
  152       if ($cookieUser and $cookieKey) {
  153         $r->param("user", $cookieUser);
  154         $r->param("key", $cookieKey);
  155         $user = $cookieUser;
  156         $key = $cookieKey;
  157       } else {
  158         $failWithoutError = 1;
  159         last VERIFY;
  160       }
  161     }
  162 
  163     if (defined $user and $force_passwd_authen) {
  164       $failWithoutError = 1;
  165       last VERIFY;
  166     }
  167 
  168     # no user was supplied.  somebody's building their own GET
  169     unless ($user) {
  170       $error = "You must specify a username.";
  171       last VERIFY;
  172     }
  173 
  174     # it's a practice user.
  175     if ($practiceUserPrefix and $user =~ /^$practiceUserPrefix/) {
  176       # we're not interested in a practice user's password
  177       $r->param("passwd", "");
  178 
  179       # it's a practice user that doesn't exist.
  180       unless (defined $db->getUser($user)) { # checked
  181         $error = "That practice account does not exist.";
  182         last VERIFY;
  183       }
  184 
  185       # we've got a key.
  186       if ($key) {
  187         if ($self->checkKey($user, $key)) {
  188           # they key was valid.
  189           last VERIFY;
  190         } else {
  191           # the key was invalid.
  192           $error = "Your session has timed out due to inactivity. You must login again.";
  193           last VERIFY;
  194         }
  195       }
  196 
  197       # -- here we know that a key was not supplied. --
  198 
  199       # it's the debug user.
  200       if ($debugPracticeUser and $user eq $debugPracticeUser) {
  201         # clobber any existing session, valid or not.
  202         my $Key = $self->generateKey($user);
  203         eval { $db->deleteKey($user) };
  204         $db->addKey($Key);
  205         $r->param("key", $Key->key());
  206         last VERIFY;
  207       }
  208 
  209       # an unexpired key exists -- the account is in use.
  210       if ($self->unexpiredKeyExists($user)) {
  211         $error = "That practice account is in use.";
  212         last VERIFY;
  213       }
  214 
  215       # here we know the account is not in use, so we
  216       # generate a new  session key (unexpiredKeyExists
  217       # deleted any expired key) and succeed!
  218       my $Key = $self->generateKey($user);
  219       $db->addKey($Key);
  220       $r->param("key", $Key->key());
  221       last VERIFY;
  222     }
  223 
  224     # -- here we know it's a regular user. --
  225 
  226     # a key was supplied.
  227     if ($key) {
  228       # we're not interested in a user's password if they're
  229       # supplying a key
  230       $r->param("passwd", "");
  231 
  232       if ($self->checkKey($user, $key)) {
  233         # valid key, so succeed.
  234         last VERIFY;
  235       } else {
  236         # invalid key. the login page doesn't propogate the key,
  237         # so we know this is an expired session.
  238         $error = "Your session has timed out due to inactivity. You must login again.";
  239         last VERIFY;
  240       }
  241     }
  242 
  243     # a password was supplied.
  244     if ($passwd) {
  245       if ($self->checkPassword($user, $passwd)) {
  246         # valid password, so create a new session. (we don't want
  247         # to reuse an old one, duh.)
  248         my $Key = $self->generateKey($user);
  249         eval { $db->deleteKey($user) };
  250         $db->addKey($Key);
  251         $r->param("key", $Key->key());
  252         # also delete the password
  253         $r->param("passwd", "");
  254         last VERIFY;
  255       } else {
  256         # incorrect password. fail.
  257         $error = "Incorrect username or password.";
  258         last VERIFY;
  259       }
  260     }
  261 
  262     # neither a key or a password were supplied.
  263     $error = "You must enter a password."
  264   }
  265 
  266   if (defined $error) {
  267     # authentication failed, in a bad way
  268     $r->notes("authen_error",$error);
  269     return 0;
  270   } elsif ($failWithoutError) {
  271     # authentication failed, but not in a bad way
  272     return 0;
  273   } else {
  274     # autentication succeeded!
  275     # send a cookie with the user and key that were accepted.
  276     if ($r->param("send_cookie")) {
  277       $self->sendCookie($r->param("user"), $r->param("key"));
  278     }
  279     return 1;
  280   }
  281 
  282   # Whatever you do, don't delete this!
  283   critical($r);
  284   # One time, I deleted it, and my mother broke her back, my cat died, and
  285   # the Pope got a tummy ache. When I replaced the line, I received eternal
  286   # salvation and a check for USD 500.
  287 }
  288 
  289 1;
  290 
  291 __END__
  292 
  293 =head1 AUTHOR
  294 
  295 Written by Dennis Lambe Jr., malsyned (at) math.rochester.edu, and Sam
  296 Hathaway, sh002i (at) math.rochester.edu.
  297 
  298 =cut

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9