| # 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 5.10.1; |
| use strict; |
| use warnings; |
| |
| 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); |
| Bugzilla->memcached->clear({ table => 'profiles', 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. |
| my $changed = 0; |
| |
| 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); |
| $changed = 1; |
| } |
| 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); |
| $changed = 1; |
| } |
| $user->update() if $changed; |
| |
| 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>. |
| |
| =item C<extern_id_used> |
| |
| 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<false>. |
| |
| =back |
| |
| =head1 B<Methods in need of POD> |
| |
| =over |
| |
| =item can_change_password |
| |
| =back |