# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # # This Source Code Form is "Incompatible With Secondary Licenses", as # defined by the Mozilla Public License, v. 2.0. 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; use constant extern_id_used => 0; 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} }; # Usually we'd call validate_password, but external authentication # systems might follow different standards than ours. So in this # place here, we call trick_taint without checks. trick_taint($password); # 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} }; $user->set_login($username); } 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); $user->set_name($real_name); } $user->update(); 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.) It is mostly an abstract class, requiring subclasses to implement most methods. Note that callers outside of the C package should never create this object directly. Just create a C object and call C 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 Description: Checks whether or not a username is valid. Params: $login_data - A C<$login_data> hashref, as described in L. This C<$login_data> hashref MUST contain C, and SHOULD also contain C. Returns: A C<$login_data> hashref with C set. This method may also set C. 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 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. This C<$login_data> hashref MUST contain either C, C, or C. If both C and C are specified, C is used as the login name of the user to create in the database. It MAY also contain C, in which case it still MUST contain C or C. It MAY contain C and C. Returns: A hashref with one element, C, which is a L object. May also return a login error as described in L. Note: This method is not abstract, it is actually implemented and creates accounts in the Bugzilla database. Subclasses should probably all call the C version of this function at the end of their own C. =item C Description: Modifies the user's password in the authentication backend. Params: $user - A L 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 or C. =over 4 =item C Whether or not users can manually create accounts in this type of account source. Defaults to C. =item C Whether or not this verifier method uses the extern_id field. If used, users with editusers permission will be be allowed to edit the extern_id for all users. The default value is C. =back