aboutsummaryrefslogtreecommitdiffstats
path: root/Bugzilla/Auth/Verify.pm
blob: 52cebb5ea4c4b69ec70b3623ec3ac0e38dc9acd5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>

package Bugzilla::Auth::Verify;

use strict;
use fields qw();

use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::User;
use Bugzilla::Util;

use constant user_can_create_account => 1;

sub new {
    my ($class, $login_type) = @_;
    my $self = fields::new($class);
    return $self;
}

sub can_change_password {
    return $_[0]->can('change_password');
}

sub create_or_update_user {
    my ($self, $params) = @_;
    my $dbh = Bugzilla->dbh;

    my $extern_id = $params->{extern_id};
    my $username  = $params->{bz_username} || $params->{username};
    my $password  = $params->{password} || '*';
    my $real_name = $params->{realname} || '';
    my $user_id   = $params->{user_id};

    # A passed-in user_id always overrides anything else, for determining
    # what account we should return.
    if (!$user_id) {
        my $username_user_id = login_to_id($username || '');
        my $extern_user_id;
        if ($extern_id) {
            trick_taint($extern_id);
            $extern_user_id = $dbh->selectrow_array('SELECT userid
                 FROM profiles WHERE extern_id = ?', undef, $extern_id);
        }

        # If we have both a valid extern_id and a valid username, and they are
        # not the same id, then we have a conflict.
        if ($username_user_id && $extern_user_id
            && $username_user_id ne $extern_user_id)
        {
            my $extern_name = Bugzilla::User->new($extern_user_id)->login;
            return { failure => AUTH_ERROR, error => "extern_id_conflict",
                     details => {extern_id   => $extern_id,
                                 extern_user => $extern_name,
                                 username    => $username} };
        }

        # If we have a valid username, but no valid id,
        # then we have to create the user. This happens when we're
        # passed only a username, and that username doesn't exist already.
        if ($username && !$username_user_id && !$extern_user_id) {
            validate_email_syntax($username)
              || return { failure => AUTH_ERROR, 
                          error   => 'auth_invalid_email',
                          details => {addr => $username} };
            # XXX Theoretically this could fail with an error, but the fix for
            # that is too involved to be done right now.
            my $user = Bugzilla::User->create({ 
                login_name    => $username, 
                cryptpassword => $password,
                realname      => $real_name});
            $username_user_id = $user->id;
        }

        # If we have a valid username id and an extern_id, but no valid
        # extern_user_id, then we have to set the user's extern_id.
        if ($extern_id && $username_user_id && !$extern_user_id) {
            $dbh->do('UPDATE profiles SET extern_id = ? WHERE userid = ?',
                     undef, $extern_id, $username_user_id);
        }

        # Finally, at this point, one of these will give us a valid user id.
        $user_id = $extern_user_id || $username_user_id;
    }

    # If we still don't have a valid user_id, then we weren't passed
    # enough information in $params, and we should die right here.
    ThrowCodeError('bad_arg', {argument => 'params', function =>
        'Bugzilla::Auth::Verify::create_or_update_user'})
        unless $user_id;

    my $user = new Bugzilla::User($user_id);

    # Now that we have a valid User, we need to see if any data has to be
    # updated.
    if ($username && lc($user->login) ne lc($username)) {
        validate_email_syntax($username)
          || return { failure => AUTH_ERROR, error => 'auth_invalid_email',
                      details => {addr => $username} };
        # Username is more than likely tainted, but we only use it in a
        # placeholder, and we've already validated it, so it's safe.
        trick_taint($username);
        $dbh->do('UPDATE profiles SET login_name = ? WHERE userid = ?',
                 undef, $username, $user->id);
    }
    if ($real_name && $user->name ne $real_name) {
        # $real_name is more than likely tainted, but we only use it
        # in a placeholder and we never use it after this.
        trick_taint($real_name);
        $dbh->do('UPDATE profiles SET realname = ? WHERE userid = ?',
                 undef, $real_name, $user->id);
    }

    return { user => $user };
}

1;

__END__

=head1 NAME

Bugzilla::Auth::Verify - An object that verifies usernames and passwords.

=head1 DESCRIPTION

Bugzilla::Auth::Verify provides the "Verifier" part of the Bugzilla 
login process. (For details, see the "STRUCTURE" section of 
L<Bugzilla::Auth>.)

It is mostly an abstract class, requiring subclasses to implement
most methods.

Note that callers outside of the C<Bugzilla::Auth> package should never
create this object directly. Just create a C<Bugzilla::Auth> object
and call C<login> on it.

=head1 VERIFICATION METHODS

These are the methods that have to do with the actual verification.

Subclasses MUST implement these methods.

=over 4

=item C<check_credentials($login_data)>

Description: Checks whether or not a username is valid.
Params:      $login_data - A C<$login_data> hashref, as described in
                           L<Bugzilla::Auth>.
                           This C<$login_data> hashref MUST contain
                           C<username>, and SHOULD also contain
                           C<password>.
Returns:     A C<$login_data> hashref with C<bz_username> set. This
             method may also set C<realname>. It must avoid changing
             anything that is already set.

=back

=head1 MODIFICATION METHODS

These are methods that change data in the actual authentication backend.

These methods are optional, they do not have to be implemented by
subclasses.

=over 4

=item C<create_or_update_user($login_data)>

Description: Automatically creates a user account in the database
             if it doesn't already exist, or updates the account
             data if C<$login_data> contains newer information.

Params:      $login_data - A C<$login_data> hashref, as described in
                           L<Bugzilla::Auth>.
                           This C<$login_data> hashref MUST contain
                           either C<user_id>, C<bz_username>, or
                           C<username>. If both C<username> and C<bz_username>
                           are specified, C<bz_username> is used as the
                           login name of the user to create in the database.
                           It MAY also contain C<extern_id>, in which
                           case it still MUST contain C<bz_username> or
                           C<username>.
                           It MAY contain C<password> and C<realname>.

Returns:     A hashref with one element, C<user>, which is a
             L<Bugzilla::User> object. May also return a login error
             as described in L<Bugzilla::Auth>.

Note:        This method is not abstract, it is actually implemented
             and creates accounts in the Bugzilla database. Subclasses
             should probably all call the C<Bugzilla::Auth::Verify>
             version of this function at the end of their own
             C<create_or_update_user>.

=item C<change_password($user, $password)>

Description: Modifies the user's password in the authentication backend.
Params:      $user - A L<Bugzilla::User> object representing the user
                     whose password we want to change.
             $password - The user's new password.
Returns:     Nothing.

=back

=head1 INFO METHODS

These are methods that describe the capabilities of this object.
These are all no-parameter methods that return either C<true> or 
C<false>.

=over 4

=item C<user_can_create_account>

Whether or not users can manually create accounts in this type of
account source. Defaults to C<true>.

=back