Parent Directory
|
Revision Log
Fixed BUG #16: "Logging out from a practice user and logging back in doesn't work right" http://webwork3/bugzilla/show_bug.cgi?id=16 --Dennis
1 ################################################################################ 2 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project 3 # $Id$ 4 ################################################################################ 5 6 package WeBWorK::Authen; 7 8 =head1 NAME 9 10 WeBWorK::Authen - Check user identity, manage session keys. 11 12 =cut 13 14 use strict; 15 use warnings; 16 17 sub new($$$) { 18 my $invocant = shift; 19 my $class = ref($invocant) || $invocant; 20 my $self = {}; 21 ($self->{r}, $self->{ce}, $self->{db}) = @_; 22 bless $self, $class; 23 return $self; 24 } 25 26 # um, this isn't used. move it to Utils? 27 #sub generatePassword($$$) { 28 # my ($self, $userID, $clearPassword) = @_; 29 # my $salt = join("", ('.','/','0'..'9','A'..'Z','a'..'z')[rand 64, rand 64]); 30 # my $cryptPassword = crypt($clearPassword, $salt); 31 # return WeBWorK::DB::Record::Password->new(user_id=>$userID, password=>$password); 32 #} 33 34 sub checkPassword($$$) { 35 my ($self, $userID, $possibleClearPassword) = @_; 36 my $Password = $self->{db}->getPassword($userID); 37 return 0 unless $Password; 38 my $possibleCryptPassword = crypt($possibleClearPassword, $Password->password()); 39 return $possibleCryptPassword eq $Password->password(); 40 } 41 42 sub generateKey($$) { 43 my ($self, $userID) = @_; 44 my @chars = @{ $self->{ce}->{sessionKeyChars} }; 45 my $length = $self->{ce}->{sessionKeyLength}; 46 srand; 47 my $key = join ("", @chars[map rand(@chars), 1 .. $length]); 48 return WeBWorK::DB::Record::Key->new(user_id=>$userID, key=>$key, timestamp=>time); 49 } 50 51 sub checkKey($$$) { 52 my ($self, $userID, $possibleKey) = @_; 53 my $Key = $self->{db}->getKey($userID); 54 return 0 unless $Key; 55 if (time <= $Key->timestamp()+$self->{ce}->{sessionKeyTimeout}) { 56 if ($possibleKey eq $Key->key()) { 57 # unexpired and matches -- update timestamp 58 $Key->timestamp(time); 59 $self->{db}->putKey($Key); 60 return 1; 61 } else { 62 # unexpired but doesn't match -- leave timestamp alone 63 # we do this to keep an attacker from keeping someone's session 64 # alive. (yeah, we don't match IPs.) 65 return 0; 66 } 67 } else { 68 # expired -- delete key 69 $self->{db}->deleteKey($userID); 70 return 0; 71 } 72 } 73 74 sub unexpiredKeyExists($$) { 75 my ($self, $userID) = @_; 76 my $Key = $self->{db}->getKey($userID); 77 return 0 unless $Key; 78 if (time <= $Key->timestamp()+$self->{ce}->{sessionKeyTimeout}) { 79 # unexpired, but leave timestamp alone 80 return 1; 81 } else { 82 # expired -- delete key 83 $self->{db}->deleteKey($userID); 84 return 0; 85 } 86 } 87 88 # verify will return 1 if the person is who they say the are. 89 # If the verification failed because of of invalid authentication data, 90 # a note will be written in the request explaining why it failed. 91 # If the request failed because no authentication data was provided, however, 92 # no note will be written, as this is expected to happen whenever someone 93 # types in a URL manually, and is not considered an error condition. 94 sub verify($) { 95 my $self = shift; 96 my $r = $self->{r}; 97 my $ce = $self->{ce}; 98 my $db = $self->{db}; 99 100 my $practiceUserPrefix = $ce->{practiceUserPrefix}; 101 my $debugPracticeUser = $ce->{debugPracticeUser}; 102 103 my $user = $r->param('user'); 104 my $passwd = $r->param('passwd'); 105 my $key = $r->param('key'); 106 my $force_passwd_authen = $r->param('force_passwd_authen'); 107 108 my $error; 109 my $failWithoutError = 0; 110 111 VERIFY: { 112 # This block is here so we can "last" out of it when we've 113 # decided whether we're going to succeed or fail. 114 115 # no authentication data was given. this is OK. 116 unless (defined $user or defined $passwd or defined $key) { 117 $failWithoutError = 1; 118 last VERIFY; 119 } 120 121 if (defined $user and $force_passwd_authen) { 122 $failWithoutError = 1; 123 last VERIFY; 124 } 125 126 # no user was supplied. somebody's building their own GET 127 unless ($user) { 128 $error = "You must specify a username."; 129 last VERIFY; 130 } 131 132 # it's a practice user. 133 if ($practiceUserPrefix and $user =~ /^$practiceUserPrefix/) { 134 # we're not interested in a practice user's password 135 $r->param("passwd", ""); 136 137 # it's a practice user that doesn't exist. 138 unless ($db->getUser($user)) { 139 $error = "That practice account does not exist."; 140 last VERIFY; 141 } 142 143 # we've got a key. 144 if ($key) { 145 if ($self->checkKey($user, $key)) { 146 # they key was valid. 147 last VERIFY; 148 } else { 149 # the key was invalid. 150 $error = "Your session has expired. You must login again."; 151 last VERIFY; 152 } 153 } 154 155 # -- here we know that a key was not supplied. -- 156 157 # it's the debug user. 158 if ($debugPracticeUser and $user eq $debugPracticeUser) { 159 # clobber any existing session, valid or not. 160 my $Key = $self->generateKey($user); 161 $db->deleteKey($user); 162 $db->addKey($Key); 163 $r->param("key", $Key->key()); 164 last VERIFY; 165 } 166 167 # an unexpired key exists -- the account is in use. 168 if ($self->unexpiredKeyExists($user)) { 169 $error = "That practice account is in use."; 170 last VERIFY; 171 } 172 173 # here we know the account is not in use, so we 174 # generate a new session key (unexpiredKeyExists 175 # deleted any expired key) and succeed! 176 my $Key = $self->generateKey($user); 177 $db->addKey($Key); 178 $r->param("key", $Key->key()); 179 last VERIFY; 180 } 181 182 # -- here we know it's a regular user. -- 183 184 # a key was supplied. 185 if ($key) { 186 # we're not interested in a user's password if they're 187 # supplying a key 188 $r->param("passwd", ""); 189 190 if ($self->checkKey($user, $key)) { 191 # valid key, so succeed. 192 last VERIFY; 193 } else { 194 # invalid key. the login page doesn't propogate the key, 195 # so we know this is an expired session. 196 $error = "Your session has expired. You must login again."; 197 last VERIFY; 198 } 199 } 200 201 # a password was supplied. 202 if ($passwd) { 203 if ($self->checkPassword($user, $passwd)) { 204 # valid password, so create a new session. (we don't want 205 # to reuse an old one, duh.) 206 my $Key = $self->generateKey($user); 207 $db->deleteKey($user); 208 $db->addKey($Key); 209 $r->param("key", $Key->key()); 210 # also delete the password 211 $r->param("passwd", ""); 212 last VERIFY; 213 } else { 214 # incorrect password. fail. 215 $error = "Incorrect username or password."; 216 last VERIFY; 217 } 218 } 219 220 # neither a key or a password were supplied. 221 $error = "You must enter a password." 222 } 223 224 if (defined $error) { 225 $r->notes("authen_error",$error); 226 return 0; 227 } else { 228 return not $failWithoutError; 229 } 230 231 # Whatever you do, don't delete this! 232 critical($r); 233 } 234 235 1; 236 237 __END__ 238 239 =head1 AUTHOR 240 241 Written by Dennis Lambe Jr., malsyned (at) math.rochester.edu, and Sam Hathaway, sh002i (at) math.rochester.edu. 242 243 =cut
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |