(CVE-2013-0786) [SECURITY] build_subselect() leaks the existence of products and...
[WebKit-https.git] / Websites / bugs.webkit.org / Bugzilla / Auth.pm
1 # -*- Mode: perl; indent-tabs-mode: nil -*-
2 #
3 # The contents of this file are subject to the Mozilla Public
4 # License Version 1.1 (the "License"); you may not use this file
5 # except in compliance with the License. You may obtain a copy of
6 # the License at http://www.mozilla.org/MPL/
7 #
8 # Software distributed under the License is distributed on an "AS
9 # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10 # implied. See the License for the specific language governing
11 # rights and limitations under the License.
12 #
13 # The Original Code is the Bugzilla Bug Tracking System.
14 #
15 # The Initial Developer of the Original Code is Netscape Communications
16 # Corporation. Portions created by Netscape are
17 # Copyright (C) 1998 Netscape Communications Corporation. All
18 # Rights Reserved.
19 #
20 # Contributor(s): Bradley Baetz <bbaetz@acm.org>
21 #                 Erik Stambaugh <erik@dasbistro.com>
22 #                 Max Kanat-Alexander <mkanat@bugzilla.org>
23
24 package Bugzilla::Auth;
25
26 use strict;
27 use fields qw(
28     _info_getter
29     _verifier
30     _persister
31 );
32
33 use Bugzilla::Constants;
34 use Bugzilla::Error;
35 use Bugzilla::Auth::Login::Stack;
36 use Bugzilla::Auth::Verify::Stack;
37 use Bugzilla::Auth::Persist::Cookie;
38
39 sub new {
40     my ($class, $params) = @_;
41     my $self = fields::new($class);
42
43     $params            ||= {};
44     $params->{Login}   ||= Bugzilla->params->{'user_info_class'} . ',Cookie';
45     $params->{Verify}  ||= Bugzilla->params->{'user_verify_class'};
46
47     $self->{_info_getter} = new Bugzilla::Auth::Login::Stack($params->{Login});
48     $self->{_verifier} = new Bugzilla::Auth::Verify::Stack($params->{Verify});
49     # If we ever have any other login persistence methods besides cookies,
50     # this could become more configurable.
51     $self->{_persister} = new Bugzilla::Auth::Persist::Cookie();
52
53     return $self;
54 }
55
56 sub login {
57     my ($self, $type) = @_;
58     my $dbh = Bugzilla->dbh;
59
60     # Get login info from the cookie, form, environment variables, etc.
61     my $login_info = $self->{_info_getter}->get_login_info();
62
63     if ($login_info->{failure}) {
64         return $self->_handle_login_result($login_info, $type);
65     }
66
67     # Now verify his username and password against the DB, LDAP, etc.
68     if ($self->{_info_getter}->{successful}->requires_verification) {
69         $login_info = $self->{_verifier}->check_credentials($login_info);
70         if ($login_info->{failure}) {
71             return $self->_handle_login_result($login_info, $type);
72         }
73         $login_info =
74           $self->{_verifier}->{successful}->create_or_update_user($login_info);
75     }
76     else {
77         $login_info = $self->{_verifier}->create_or_update_user($login_info);
78     }
79
80     if ($login_info->{failure}) {
81         return $self->_handle_login_result($login_info, $type);
82     }
83
84     # Make sure the user isn't disabled.
85     my $user = $login_info->{user};
86     if ($user->disabledtext) {
87         return $self->_handle_login_result({ failure => AUTH_DISABLED,
88                                               user    => $user }, $type);
89     }
90     $user->set_authorizer($self);
91
92     return $self->_handle_login_result($login_info, $type);
93 }
94
95 sub can_change_password {
96     my ($self) = @_;
97     my $verifier = $self->{_verifier}->{successful};
98     $verifier  ||= $self->{_verifier};
99     my $getter   = $self->{_info_getter}->{successful};
100     $getter      = $self->{_info_getter} 
101         if (!$getter || $getter->isa('Bugzilla::Auth::Login::Cookie'));
102     return $verifier->can_change_password &&
103            $getter->user_can_create_account;
104 }
105
106 sub can_login {
107     my ($self) = @_;
108     my $getter = $self->{_info_getter}->{successful};
109     $getter    = $self->{_info_getter}
110         if (!$getter || $getter->isa('Bugzilla::Auth::Login::Cookie'));
111     return $getter->can_login;
112 }
113
114 sub can_logout {
115     my ($self) = @_;
116     my $getter = $self->{_info_getter}->{successful};
117     # If there's no successful getter, we're not logged in, so of
118     # course we can't log out!
119     return 0 unless $getter;
120     return $getter->can_logout;
121 }
122
123 sub user_can_create_account {
124     my ($self) = @_;
125     my $verifier = $self->{_verifier}->{successful};
126     $verifier  ||= $self->{_verifier};
127     my $getter   = $self->{_info_getter}->{successful};
128     $getter      = $self->{_info_getter}
129         if (!$getter || $getter->isa('Bugzilla::Auth::Login::Cookie'));
130     return $verifier->user_can_create_account
131            && $getter->user_can_create_account;
132 }
133
134 sub can_change_email {
135     return $_[0]->user_can_create_account;
136 }
137
138 sub _handle_login_result {
139     my ($self, $result, $login_type) = @_;
140     my $dbh = Bugzilla->dbh;
141
142     my $user      = $result->{user};
143     my $fail_code = $result->{failure};
144
145     if (!$fail_code) {
146         if ($self->{_info_getter}->{successful}->requires_persistence) {
147             $self->{_persister}->persist_login($user);
148         }
149     }
150     elsif ($fail_code == AUTH_ERROR) {
151         ThrowCodeError($result->{error}, $result->{details});
152     }
153     elsif ($fail_code == AUTH_NODATA) {
154         $self->{_info_getter}->fail_nodata($self) 
155             if $login_type == LOGIN_REQUIRED;
156
157         # If we're not LOGIN_REQUIRED, we just return the default user.
158         $user = Bugzilla->user;
159     }
160     # The username/password may be wrong
161     # Don't let the user know whether the username exists or whether
162     # the password was just wrong. (This makes it harder for a cracker
163     # to find account names by brute force)
164     elsif ($fail_code == AUTH_LOGINFAILED or $fail_code == AUTH_NO_SUCH_USER) {
165         ThrowUserError("invalid_username_or_password");
166     }
167     # The account may be disabled
168     elsif ($fail_code == AUTH_DISABLED) {
169         $self->{_persister}->logout();
170         # XXX This is NOT a good way to do this, architecturally.
171         $self->{_persister}->clear_browser_cookies();
172         # and throw a user error
173         ThrowUserError("account_disabled",
174             {'disabled_reason' => $result->{user}->disabledtext});
175     }
176     # If we get here, then we've run out of options, which shouldn't happen.
177     else {
178         ThrowCodeError("authres_unhandled", { value => $fail_code });
179     }
180
181     return $user;
182 }
183
184 1;
185
186 __END__
187
188 =head1 NAME
189
190 Bugzilla::Auth - An object that authenticates the login credentials for
191                  a user.
192
193 =head1 DESCRIPTION
194
195 Handles authentication for Bugzilla users.
196
197 Authentication from Bugzilla involves two sets of modules. One set is
198 used to obtain the username/password (from CGI, email, etc), and the 
199 other set uses this data to authenticate against the datasource 
200 (the Bugzilla DB, LDAP, PAM, etc.).
201
202 Modules for obtaining the username/password are subclasses of 
203 L<Bugzilla::Auth::Login>, and modules for authenticating are subclasses
204 of L<Bugzilla::Auth::Verify>.
205
206 =head1 AUTHENTICATION ERROR CODES
207
208 Whenever a method in the C<Bugzilla::Auth> family fails in some way,
209 it will return a hashref containing at least a single key called C<failure>.
210 C<failure> will point to an integer error code, and depending on the error
211 code the hashref may contain more data.
212
213 The error codes are explained here below.
214
215 =head2 C<AUTH_NODATA>
216
217 Insufficient login data was provided by the user. This may happen in several
218 cases, such as cookie authentication when the cookie is not present.
219
220 =head2 C<AUTH_ERROR>
221
222 An error occurred when trying to use the login mechanism.
223
224 The hashref will also contain an C<error> element, which is the name
225 of an error from C<template/en/default/global/code-error.html> --
226 the same type of error that would be thrown by 
227 L<Bugzilla::Error::ThrowCodeError>.
228
229 The hashref *may* contain an element called C<details>, which is a hashref
230 that should be passed to L<Bugzilla::Error::ThrowCodeError> as the 
231 various fields to be used in the error message.
232
233 =head2 C<AUTH_LOGINFAILED>
234
235 An incorrect username or password was given.
236
237 =head2 C<AUTH_NO_SUCH_USER>
238
239 This is an optional more-specific version of C<AUTH_LOGINFAILED>.
240 Modules should throw this error when they discover that the
241 requested user account actually does not exist, according to them.
242
243 That is, for example, L<Bugzilla::Auth::Verify::LDAP> would throw
244 this if the user didn't exist in LDAP.
245
246 The difference between C<AUTH_NO_SUCH_USER> and C<AUTH_LOGINFAILED>
247 should never be communicated to the user, for security reasons.
248
249 =head2 C<AUTH_DISABLED>
250
251 The user successfully logged in, but their account has been disabled.
252 Usually this is throw only by C<Bugzilla::Auth::login>.
253
254 =head1 LOGIN TYPES
255
256 The C<login> function (below) can do different types of login, depending
257 on what constant you pass into it:
258
259 =head2 C<LOGIN_OPTIONAL>
260
261 A login is never required to access this data. Attempting to login is
262 still useful, because this allows the page to be personalised. Note that
263 an incorrect login will still trigger an error, even though the lack of
264 a login will be OK.
265
266 =head2 C<LOGIN_NORMAL>
267
268 A login may or may not be required, depending on the setting of the
269 I<requirelogin> parameter. This is the default if you don't specify a
270 type.
271
272 =head2 C<LOGIN_REQUIRED>
273
274 A login is always required to access this data.
275
276 =head1 METHODS
277
278 These are methods that can be called on a C<Bugzilla::Auth> object 
279 itself.
280
281 =head2 Login
282
283 =over 4
284
285 =item C<login($type)>
286
287 Description: Logs a user in. For more details on how this works
288              internally, see the section entitled "STRUCTURE."
289 Params:      $type - One of the Login Types from above.
290 Returns:     An authenticated C<Bugzilla::User>. Or, if the type was
291              not C<LOGIN_REQUIRED>, then we return an
292              empty C<Bugzilla::User> if no login data was passed in.
293
294 =back
295
296 =head2 Info Methods
297
298 These are methods that give information about the Bugzilla::Auth object.
299
300 =over 4
301
302 =item C<can_change_password>
303
304 Description: Tells you whether or not the current login system allows
305              changing passwords.
306 Params:      None
307 Returns:     C<true> if users and administrators should be allowed to
308              change passwords, C<false> otherwise.
309
310 =item C<can_login>
311
312 Description: Tells you whether or not the current login system allows
313              users to log in through the web interface.
314 Params:      None
315 Returns:     C<true> if users can log in through the web interface,
316              C<false> otherwise.
317
318 =item C<can_logout>
319
320 Description: Tells you whether or not the current login system allows
321              users to log themselves out.
322 Params:      None
323 Returns:     C<true> if users can log themselves out, C<false> otherwise.
324              If a user isn't logged in, we always return C<false>.
325
326 =item C<user_can_create_account>
327
328 Description: Tells you whether or not users are allowed to manually create
329              their own accounts, based on the current login system in use.
330              Note that this doesn't check the C<createemailregexp>
331              parameter--you have to do that by yourself in your code.
332 Params:      None
333 Returns:     C<true> if users are allowed to create new Bugzilla accounts,
334              C<false> otherwise.
335
336 =item C<can_change_email>
337
338 Description: Whether or not the current login system allows users to
339              change their own email address.
340 Params:      None
341 Returns:     C<true> if users can change their own email address,
342              C<false> otherwise.
343
344 =back
345
346 =head1 STRUCTURE
347
348 This section is mostly interesting to developers who want to implement
349 a new authentication type. It describes the general structure of the
350 Bugzilla::Auth family, and how the C<login> function works.
351
352 A C<Bugzilla::Auth> object is essentially a collection of a few other
353 objects: the "Info Getter," the "Verifier," and the "Persistence 
354 Mechanism."
355
356 They are used inside the C<login> function in the following order:
357
358 =head2 The Info Getter
359
360 This is a C<Bugzilla::Auth::Login> object. Basically, it gets the
361 username and password from the user, somehow. Or, it just gets enough
362 information to uniquely identify a user, and passes that on down the line.
363 (For example, a C<user_id> is enough to uniquely identify a user,
364 even without a username and password.)
365
366 Some Info Getters don't require any verification. For example, if we got
367 the C<user_id> from a Cookie, we don't need to check the username and 
368 password.
369
370 If an Info Getter returns only a C<user_id> and no username/password,
371 then it MUST NOT require verification. If an Info Getter requires
372 verfication, then it MUST return at least a C<username>.
373
374 =head2 The Verifier
375
376 This verifies that the username and password are valid.
377
378 It's possible that some methods of verification don't require a password.
379
380 =head2 The Persistence Mechanism
381
382 This makes it so that the user doesn't have to log in on every page.
383 Normally this object just sends a cookie to the user's web browser,
384 as that's the most common method of "login persistence."
385
386 =head2 Other Things We Do
387
388 After we verify the username and password, sometimes we automatically
389 create an account in the Bugzilla database, for certain authentication
390 types. We use the "Account Source" to get data about the user, and
391 create them in the database. (Or, if their data has changed since the
392 last time they logged in, their data gets updated.)
393
394 =head2 The C<$login_data> Hash
395
396 All of the C<Bugzilla::Auth::Login> and C<Bugzilla::Auth::Verify>
397 methods take an argument called C<$login_data>. This is basically
398 a hash that becomes more and more populated as we go through the
399 C<login> function.
400
401 All C<Bugzilla::Auth::Login> and C<Bugzilla::Auth::Verify> methods
402 also *return* the C<$login_data> structure, when they succeed. They
403 may have added new data to it.
404
405 For all C<Bugzilla::Auth::Login> and C<Bugzilla::Auth::Verify> methods,
406 the rule is "you must return the same hashref you were passed in." You can
407 modify the hashref all you want, but you can't create a new one. The only
408 time you can return a new one is if you're returning some error code
409 instead of the C<$login_data> structure.
410
411 Each C<Bugzilla::Auth::Login> or C<Bugzilla::Auth::Verify> method
412 explains in its documentation which C<$login_data> elements are
413 required by it, and which are set by it.
414
415 Here are all of the elements that *may* be in C<$login_data>:
416
417 =over 4
418
419 =item C<user_id>
420
421 A Bugzilla C<user_id> that uniquely identifies a user.
422
423 =item C<username>
424
425 The username that was provided by the user.
426
427 =item C<bz_username>
428
429 The username of this user inside of Bugzilla. Sometimes this differs from
430 C<username>.
431
432 =item C<password>
433
434 The password provided by the user.
435
436 =item C<realname>
437
438 The real name of the user.
439
440 =item C<extern_id>
441
442 Some string that uniquely identifies the user in an external account 
443 source. If this C<extern_id> already exists in the database with
444 a different username, the username will be *changed* to be the
445 username specified in this C<$login_data>.
446
447 That is, let's my extern_id is C<mkanat>. I already have an account
448 in Bugzilla with the username of C<mkanat@foo.com>. But this time,
449 when I log in, I have an extern_id of C<mkanat> and a C<username>
450 of C<mkanat@bar.org>. So now, Bugzilla will automatically change my 
451 username to C<mkanat@bar.org> instead of C<mkanat@foo.com>.
452
453 =item C<user>
454
455 A L<Bugzilla::User> object representing the authenticated user. 
456 Note that C<Bugzilla::Auth::login> may modify this object at various points.
457
458 =back
459
460