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

Diff of /branches/rel-2-3-dev/webwork2/lib/WeBWorK/Authen.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 816 Revision 817
11 11
12=cut 12=cut
13 13
14use strict; 14use strict;
15use warnings; 15use warnings;
16use WeBWorK::DB::Auth;
17 16
18sub new($$$) { 17sub new($$$) {
19 my $invocant = shift; 18 my $invocant = shift;
20 my $class = ref($invocant) || $invocant; 19 my $class = ref($invocant) || $invocant;
21 my $self = {}; 20 my $self = {};
22 ($self->{r}, $self->{ce}) = @_; 21 ($self->{r}, $self->{ce}, $self->{db}) = @_;
23 bless $self, $class; 22 bless $self, $class;
24 return $self; 23 return $self;
25} 24}
26 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
34sub 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
27sub generate_key { 42sub generateKey($$) {
28 # Package constants. These should never be changed in other places ever 43 my ($self, $userID) = @_;
29 my $key_length = 40; # number of chars in each key 44 my @chars = @{ $self->{ce}->{sessionKeyChars} };
30 my @key_chars = ('A'..'Z', 'a'..'z', '0'..'9', '.', '^', '/', '!', '*'); 45 my $length = $self->{ce}->{sessionKeyLength};
31
32 my $i = $key_length;
33 my $key = '';
34 srand; 46 srand;
35 while($i) { 47 my $key = join ("", @chars[map rand(@chars), 1 .. $length]);
36 $key .= $key_chars[rand(@key_chars)]; 48 return WeBWorK::DB::Record::Key->new(user_id=>$userID, key=>$key, timestamp=>time);
37 $i--; 49}
50
51sub 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;
38 } 66 }
67 } else {
68 # expired -- delete key
69 $self->{db}->deleteKey($userID);
39 return $key; 70 return 0;
71 }
72}
73
74sub 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 }
40} 86}
41 87
42# verify will return 1 if the person is who they say the are. 88# verify will return 1 if the person is who they say the are.
43# If the verification failed because of of invalid authentication data, 89# If the verification failed because of of invalid authentication data,
44# a note will be written in the request explaining why it failed. 90# a note will be written in the request explaining why it failed.
46# no note will be written, as this is expected to happen whenever someone 92# no note will be written, as this is expected to happen whenever someone
47# types in a URL manually, and is not considered an error condition. 93# types in a URL manually, and is not considered an error condition.
48sub verify($) { 94sub verify($) {
49 my $self = shift; 95 my $self = shift;
50 my $r = $self->{r}; 96 my $r = $self->{r};
51 my $course_env = $self->{ce}; 97 my $ce = $self->{ce};
98 my $db = $self->{db};
99
100 my $practiceUserPrefix = $ce->{practiceUserPrefix};
101 my $debugPracticeUser = $ce->{debugPracticeUser};
52 102
53 my $user = $r->param('user'); 103 my $user = $r->param('user');
54 my $passwd = $r->param('passwd'); 104 my $passwd = $r->param('passwd');
55 my $key = $r->param('key'); 105 my $key = $r->param('key');
56 my $time = time;
57
58 # I wanted to get rid of that passwd up here for security reasons,
59 # but usability dictates that we not clear out invalid passwords.
60 #$r->param('passwd',undef);
61 106
62 my $error; 107 my $error;
63 my $return; 108 my $failWithoutError = 0;
64 109
65 my $auth = WeBWorK::DB::Auth->new($course_env); 110 VERIFY: {
66 111 # This block is here so we can "last" out of it when we've
67 # The first part of this big conditional checks to make that we have 112 # decided whether we're going to succeed or fail.
68 # all of the form info that we need. It's pretty boring. The kooky 113
69 # authen stuff comes after that. 114 # no authentication data was given. this is OK.
70 if (!defined $user && !defined $passwd && !defined $key) { 115 unless (defined $user or defined $passwd or defined $key) {
71 # The user hasn't even had a chance to say who he is, so we 116 $failWithoutError = 1;
72 # can't hold it against him that we don't know. 117 last VERIFY;
73 undef $error; 118 }
74 $return = 0; 119
75 } elsif (!$user) { 120 # no user was supplied.
121 unless ($user) {
76 $error = "You must specify a username"; 122 $error = "You must specify a username.";
77 $return = 0; 123 last VERIFY;
78 } elsif (!$passwd && !$key) {
79 $error = "You must enter a password";
80 $return = 0;
81 } 124 }
82 # OK, we're done with the trivia. Now lets authenticate. 125
83 elsif ($passwd) { 126 # it's a practice user.
84 # A bit of extra logic for practice users
85 # Practice users are different because:
86 # - They aren't allowed to log in if an active key exists
87 # (except for $debugPracticeUser)
88 # - They are allowed to log in with any password
89 my $practiceUserPrefix = $course_env->{"practiceUserPrefix"};
90 my $debugPracticeUser = $course_env->{"debugPracticeUser"};
91 if ($practiceUserPrefix and $user =~ /^$practiceUserPrefix/) { 127 if ($practiceUserPrefix and $user =~ /^$practiceUserPrefix/) {
92 if (!$auth->getPassword($user)) { # the only way DB::Auth provides for checking the existence of a user 128 # we're not interested in a practice user's password
129 $r->param("passwd", "");
130
131 # it's a practice user that doesn't exist.
132 unless ($db->getUser($user)) {
93 $error = "That practice account does not exist"; 133 $error = "That practice account does not exist.";
94 $return = 0; 134 last VERIFY;
95 } elsif ($auth->getKey($user) and $user ne $debugPracticeUser) { 135 }
136
137 # we've got a key.
138 if ($key) {
139 if ($self->checkKey($user, $key)) {
140 # they key was valid.
141 last VERIFY;
142 } else {
143 # the key was invalid.
144 $error = "Your session has expired. You must login again.";
145 last VERIFY;
146 }
147 }
148
149 # -- here we know that a key was not supplied. --
150
151 # it's the debug user.
152 if ($debugPracticeUser and $user eq $debugPracticeUser) {
153 # clobber any existing session, valid or not.
154 my $Key = $self->generateKey($user);
155 $db->deleteKey($user);
156 $db->addKey($Key);
157 $r->param("key", $Key->key());
158 last VERIFY;
159 }
160
161 # an unexpired key exists -- the account is in use.
162 if ($self->unexpiredKeyExists($user)) {
96 $error = "That practice account is in use"; 163 $error = "That practice account is in use.";
97 $return = 0; 164 last VERIFY;
165 }
166
167 # here we know the account is not in use, so we
168 # generate a new session key (unexpiredKeyExists
169 # deleted any expired key) and succeed!
170 my $Key = $self->generateKey($user);
171 $db->addKey($Key);
172 $r->param("key", $Key->key());
173 last VERIFY;
174 }
175
176 # -- here we know it's a regular user. --
177
178 # a key was supplied.
179 if ($key) {
180 # we're not interested in a user's password if they're
181 # supplying a key
182 $r->param("passwd", "");
183
184 if ($self->checkKey($user, $key)) {
185 # valid key, so succeed.
186 last VERIFY;
98 } else { 187 } else {
99 $key = generate_key; 188 # invalid key. the login page doesn't propogate the key,
100 $auth->setKey($user, $key); 189 # so we know this is an expired session.
101 $r->param('key',$key); 190 $error = "Your session has expired. You must login again.";
102 $return = 1; 191 last VERIFY;
103 }
104 } 192 }
105 # Not a practice user. Do normal authentication. 193 }
106 elsif ($auth->verifyPassword($user, $passwd)) { 194
107 # Remove the passwd field from subsequent requests. 195 # a password was supplied.
196 if ($passwd) {
197 if ($self->checkPassword($user, $passwd)) {
198 # valid password, so create a new session. (we don't want
199 # to reuse an old one, duh.)
200 my $Key = $self->generateKey($user);
201 $db->deleteKey($user);
202 $db->addKey($Key);
203 $r->param("key", $Key->key());
204 # also delete the password
108 $r->param('passwd',""); 205 $r->param("passwd", "");
109 $key = $auth->getKey($user) || generate_key; 206 last VERIFY;
110 $auth->setKey($user, $key);
111 $r->param('key',$key);
112 $return = 1;
113 } else { 207 } else {
208 # incorrect password. fail.
114 $error = "Incorrect username or password"; 209 $error = "Incorrect username or password.";
115 $return = 0; 210 last VERIFY;
116 } 211 }
117 } elsif ($key) { 212 }
118 # The timestamp gets updated by verifyKey 213
119 if ($auth->verifyKey($user, $key)) { 214 # neither a key or a password were supplied.
120 $return = 1; 215 $error = "You must enter a password."
121 } else { 216 }
122 $error = "Your session has expired. You must login again"; 217
218 if (defined $error) {
219 $r->notes("authen_error",$error);
123 $return = 0; 220 return 0;
124 }
125 } else { 221 } else {
126 $error = "Unexpected authentication error!"; 222 return not $failWithoutError;
127 $return = 0;
128 } 223 }
129
130 $r->notes("authen_error",$error) if defined($error);
131 return $return;
132 224
133 # Whatever you do, don't delete this! 225 # Whatever you do, don't delete this!
134 critical($r); 226 critical($r);
135} 227}
136 228
138 230
139__END__ 231__END__
140 232
141=head1 AUTHOR 233=head1 AUTHOR
142 234
143Written by Dennis Lambe Jr., malsyned (at) math.rochester.edu 235Written by Dennis Lambe Jr., malsyned (at) math.rochester.edu, and Sam Hathaway, sh002i (at) math.rochester.edu.
144 236
145=cut 237=cut

Legend:
Removed from v.816  
changed lines
  Added in v.817

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9