diff options
Diffstat (limited to 'userprefs.cgi')
-rwxr-xr-x | userprefs.cgi | 1611 |
1 files changed, 820 insertions, 791 deletions
diff --git a/userprefs.cgi b/userprefs.cgi index 830c49eed..45a1c5194 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -30,7 +30,7 @@ use DateTime; use constant SESSION_MAX => 20; local our $template = Bugzilla->template; -local our $vars = {}; +local our $vars = {}; ############################################################################### # Each panel has two functions - panel Foo has a DoFoo, to get the data @@ -39,898 +39,922 @@ local our $vars = {}; # SaveFoo may be called before DoFoo. ############################################################################### sub DoAccount { - my $dbh = Bugzilla->dbh; - my $user = Bugzilla->user; - - ($vars->{'realname'}) = $dbh->selectrow_array( - "SELECT realname FROM profiles WHERE userid = ?", undef, $user->id); - - if(Bugzilla->params->{'allowemailchange'} - && Bugzilla->user->authorizer->can_change_email) { - # First delete old tokens. - Bugzilla::Token::CleanTokenTable(); - - my @token = $dbh->selectrow_array( - "SELECT tokentype, " . - $dbh->sql_date_math('issuedate', '+', MAX_TOKEN_AGE, 'DAY') - . ", eventdata + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + + ($vars->{'realname'}) + = $dbh->selectrow_array("SELECT realname FROM profiles WHERE userid = ?", + undef, $user->id); + + if ( Bugzilla->params->{'allowemailchange'} + && Bugzilla->user->authorizer->can_change_email) + { + # First delete old tokens. + Bugzilla::Token::CleanTokenTable(); + + my @token = $dbh->selectrow_array( + "SELECT tokentype, " + . $dbh->sql_date_math('issuedate', '+', MAX_TOKEN_AGE, 'DAY') + . ", eventdata FROM tokens WHERE userid = ? AND tokentype LIKE 'email%' - ORDER BY tokentype ASC " . $dbh->sql_limit(1), undef, $user->id); - if (scalar(@token) > 0) { - my ($tokentype, $change_date, $eventdata) = @token; - $vars->{'login_change_date'} = $change_date; - - if($tokentype eq 'emailnew') { - my ($oldemail,$newemail) = split(/:/,$eventdata); - $vars->{'new_login_name'} = $newemail; - } - } + ORDER BY tokentype ASC " . $dbh->sql_limit(1), undef, $user->id + ); + if (scalar(@token) > 0) { + my ($tokentype, $change_date, $eventdata) = @token; + $vars->{'login_change_date'} = $change_date; + + if ($tokentype eq 'emailnew') { + my ($oldemail, $newemail) = split(/:/, $eventdata); + $vars->{'new_login_name'} = $newemail; + } } + } } sub SaveAccount { - my $cgi = Bugzilla->cgi; - my $dbh = Bugzilla->dbh; + my $cgi = Bugzilla->cgi; + my $dbh = Bugzilla->dbh; - $dbh->bz_start_transaction; + $dbh->bz_start_transaction; - my $user = Bugzilla->user; + my $user = Bugzilla->user; - my $oldpassword = $cgi->param('old_password'); - my $pwd1 = $cgi->param('new_password1'); - my $pwd2 = $cgi->param('new_password2'); - my $new_login_name = trim($cgi->param('new_login_name')); - my @mfa_events; + my $oldpassword = $cgi->param('old_password'); + my $pwd1 = $cgi->param('new_password1'); + my $pwd2 = $cgi->param('new_password2'); + my $new_login_name = trim($cgi->param('new_login_name')); + my @mfa_events; - if ($user->authorizer->can_change_password - && ($oldpassword ne "" || $pwd1 ne "" || $pwd2 ne "")) - { - my $oldcryptedpwd = $user->cryptpassword; - $oldcryptedpwd || ThrowCodeError("unable_to_retrieve_password"); + if ($user->authorizer->can_change_password + && ($oldpassword ne "" || $pwd1 ne "" || $pwd2 ne "")) + { + my $oldcryptedpwd = $user->cryptpassword; + $oldcryptedpwd || ThrowCodeError("unable_to_retrieve_password"); - if (bz_crypt($oldpassword, $oldcryptedpwd) ne $oldcryptedpwd) { - ThrowUserError("old_password_incorrect"); - } + if (bz_crypt($oldpassword, $oldcryptedpwd) ne $oldcryptedpwd) { + ThrowUserError("old_password_incorrect"); + } + + if ($pwd1 ne "" || $pwd2 ne "") { + ThrowUserError("new_password_missing") unless $pwd1; + Bugzilla->assert_password_is_secure($pwd1); + Bugzilla->assert_passwords_match($pwd1, $pwd2); - if ($pwd1 ne "" || $pwd2 ne "") { - ThrowUserError("new_password_missing") unless $pwd1; - Bugzilla->assert_password_is_secure($pwd1); - Bugzilla->assert_passwords_match($pwd1, $pwd2); - - if ($oldpassword ne $pwd1) { - if ($user->mfa) { - push @mfa_events, { - type => 'set_password', - reason => 'changing your password', - password => $pwd1, - }; - } - else { - $user->set_password($pwd1); - # Invalidate all logins except for the current one - Bugzilla->logout(LOGOUT_KEEP_CURRENT); - } - } + if ($oldpassword ne $pwd1) { + if ($user->mfa) { + push @mfa_events, + { + type => 'set_password', + reason => 'changing your password', + password => $pwd1, + }; } - } + else { + $user->set_password($pwd1); - if ($user->authorizer->can_change_email - && Bugzilla->params->{"allowemailchange"} - && $new_login_name) - { - if ($user->login ne $new_login_name) { - $oldpassword || ThrowUserError("old_password_required"); - - # Block multiple email changes for the same user. - if (Bugzilla::Token::HasEmailChangeToken($user->id)) { - ThrowUserError("email_change_in_progress"); - } - - # Before changing an email address, confirm one does not exist. - validate_email_syntax($new_login_name) - || ThrowUserError('illegal_email_address', {addr => $new_login_name}); - is_available_username($new_login_name) - || ThrowUserError("account_exists", {email => $new_login_name}); - - if ($user->mfa) { - push @mfa_events, { - type => 'set_login', - reason => 'changing your email address', - login => $new_login_name, - }; - } - else { - Bugzilla::Token::IssueEmailChangeToken($user, $new_login_name); - $vars->{email_changes_saved} = 1; - } + # Invalidate all logins except for the current one + Bugzilla->logout(LOGOUT_KEEP_CURRENT); } + } } + } + + if ( $user->authorizer->can_change_email + && Bugzilla->params->{"allowemailchange"} + && $new_login_name) + { + if ($user->login ne $new_login_name) { + $oldpassword || ThrowUserError("old_password_required"); + + # Block multiple email changes for the same user. + if (Bugzilla::Token::HasEmailChangeToken($user->id)) { + ThrowUserError("email_change_in_progress"); + } + + # Before changing an email address, confirm one does not exist. + validate_email_syntax($new_login_name) + || ThrowUserError('illegal_email_address', {addr => $new_login_name}); + is_available_username($new_login_name) + || ThrowUserError("account_exists", {email => $new_login_name}); + + if ($user->mfa) { + push @mfa_events, + { + type => 'set_login', + reason => 'changing your email address', + login => $new_login_name, + }; + } + else { + Bugzilla::Token::IssueEmailChangeToken($user, $new_login_name); + $vars->{email_changes_saved} = 1; + } + } + } - $user->set_name($cgi->param('realname')); - $user->update({ keep_session => 1, keep_tokens => 1 }); - $dbh->bz_commit_transaction; + $user->set_name($cgi->param('realname')); + $user->update({keep_session => 1, keep_tokens => 1}); + $dbh->bz_commit_transaction; - if (@mfa_events) { - # build the fields for the postback - my $mfa_event = { - postback => { - action => 'userprefs.cgi', - fields => { - tab => 'account', - }, - }, - reason => ucfirst(join(' and ', map { $_->{reason} } @mfa_events)), - actions => \@mfa_events, - }; - # display 2fa verification - $user->mfa_provider->verify_prompt($mfa_event); - } + if (@mfa_events) { + + # build the fields for the postback + my $mfa_event = { + postback => {action => 'userprefs.cgi', fields => {tab => 'account',},}, + reason => ucfirst(join(' and ', map { $_->{reason} } @mfa_events)), + actions => \@mfa_events, + }; + + # display 2fa verification + $user->mfa_provider->verify_prompt($mfa_event); + } } sub MfaAccount { - my $cgi = Bugzilla->cgi; - my $user = Bugzilla->user; - my $dbh = Bugzilla->dbh; - return unless $user->mfa; + my $cgi = Bugzilla->cgi; + my $user = Bugzilla->user; + my $dbh = Bugzilla->dbh; + return unless $user->mfa; - my $event = $user->mfa_provider->verify_token($cgi->param('mfa_token')); + my $event = $user->mfa_provider->verify_token($cgi->param('mfa_token')); - foreach my $action (@{ $event->{actions} }) { - if ($action->{type} eq 'set_login') { - Bugzilla::Token::IssueEmailChangeToken($user, $action->{login}); - $vars->{email_changes_saved} = 1; - } + foreach my $action (@{$event->{actions}}) { + if ($action->{type} eq 'set_login') { + Bugzilla::Token::IssueEmailChangeToken($user, $action->{login}); + $vars->{email_changes_saved} = 1; + } - elsif ($action->{type} eq 'set_password') { - $dbh->bz_start_transaction; - $user->set_password($action->{password}); - Bugzilla->logout(LOGOUT_KEEP_CURRENT); - $user->update({ keep_session => 1, keep_tokens => 1 }); - $dbh->bz_commit_transaction; - } + elsif ($action->{type} eq 'set_password') { + $dbh->bz_start_transaction; + $user->set_password($action->{password}); + Bugzilla->logout(LOGOUT_KEEP_CURRENT); + $user->update({keep_session => 1, keep_tokens => 1}); + $dbh->bz_commit_transaction; } + } } sub DisableAccount { - my $user = Bugzilla->user; + my $user = Bugzilla->user; - my $new_login = 'u' . $user->id . '@disabled.tld'; + my $new_login = 'u' . $user->id . '@disabled.tld'; - Bugzilla->audit(sprintf('<%s> self-disabled %s (now %s)', remote_ip(), $user->login, $new_login)); + Bugzilla->audit(sprintf( + '<%s> self-disabled %s (now %s)', + remote_ip(), $user->login, $new_login + )); - $user->set_login($new_login); - $user->set_name(''); - $user->set_disabledtext('Disabled by account owner.'); - $user->set_disable_mail(1); - $user->set_password('*'); - $user->update(); + $user->set_login($new_login); + $user->set_name(''); + $user->set_disabledtext('Disabled by account owner.'); + $user->set_disable_mail(1); + $user->set_password('*'); + $user->update(); - Bugzilla->logout(); - print Bugzilla->cgi->redirect(Bugzilla->localconfig->{urlbase}); - exit; + Bugzilla->logout(); + print Bugzilla->cgi->redirect(Bugzilla->localconfig->{urlbase}); + exit; } sub DoSettings { - my $user = Bugzilla->user; - - my %settings; - my $has_settings_enabled = 0; - foreach my $name (sort keys %{ $user->settings }) { - my $setting = $user->settings->{$name}; - next if !$setting->{is_enabled}; - my $category = $setting->{category}; - $settings{$category} ||= []; - push(@{ $settings{$category} }, $setting); - $has_settings_enabled = 1 if $setting->{is_enabled}; - } - - $vars->{settings} = \%settings; - $vars->{has_settings_enabled} = $has_settings_enabled; - $vars->{dont_show_button} = !$has_settings_enabled; + my $user = Bugzilla->user; + + my %settings; + my $has_settings_enabled = 0; + foreach my $name (sort keys %{$user->settings}) { + my $setting = $user->settings->{$name}; + next if !$setting->{is_enabled}; + my $category = $setting->{category}; + $settings{$category} ||= []; + push(@{$settings{$category}}, $setting); + $has_settings_enabled = 1 if $setting->{is_enabled}; + } + + $vars->{settings} = \%settings; + $vars->{has_settings_enabled} = $has_settings_enabled; + $vars->{dont_show_button} = !$has_settings_enabled; } sub SaveSettings { - my $cgi = Bugzilla->cgi; - my $user = Bugzilla->user; - - my $settings = $user->settings; - my @setting_list = keys %$settings; - my $mfa_event = undef; - - foreach my $name (@setting_list) { - next if ! ($settings->{$name}->{'is_enabled'}); - my $value = $cgi->param($name); - next unless defined $value; - my $setting = new Bugzilla::User::Setting($name); - - if ($name eq 'api_key_only' && $user->mfa - && ($value eq 'off' - || ($value eq 'api_key_only-isdefault' && $setting->{default_value} eq 'off')) - ) { - $mfa_event = {}; - } + my $cgi = Bugzilla->cgi; + my $user = Bugzilla->user; + + my $settings = $user->settings; + my @setting_list = keys %$settings; + my $mfa_event = undef; + + foreach my $name (@setting_list) { + next if !($settings->{$name}->{'is_enabled'}); + my $value = $cgi->param($name); + next unless defined $value; + my $setting = new Bugzilla::User::Setting($name); + + if ( + $name eq 'api_key_only' + && $user->mfa + && ($value eq 'off' + || ($value eq 'api_key_only-isdefault' && $setting->{default_value} eq 'off')) + ) + { + $mfa_event = {}; + } - if ($value eq "${name}-isdefault" ) { - if (! $settings->{$name}->{'is_default'}) { - if ($mfa_event) { - $mfa_event->{reset} = 1; - } - else { - $settings->{$name}->reset_to_default; - } - } + if ($value eq "${name}-isdefault") { + if (!$settings->{$name}->{'is_default'}) { + if ($mfa_event) { + $mfa_event->{reset} = 1; } else { - $setting->validate_value($value); - if ($name eq 'api_key_only' && $mfa_event) { - $mfa_event->{set} = $value; - } - else { - $settings->{$name}->set($value); - } + $settings->{$name}->reset_to_default; } + } } + else { + $setting->validate_value($value); + if ($name eq 'api_key_only' && $mfa_event) { + $mfa_event->{set} = $value; + } + else { + $settings->{$name}->set($value); + } + } + } - Bugzilla::Hook::process('settings_after_update'); + Bugzilla::Hook::process('settings_after_update'); - $vars->{'settings'} = $user->settings(1); - clear_settings_cache($user->id); + $vars->{'settings'} = $user->settings(1); + clear_settings_cache($user->id); - if ($mfa_event) { - $mfa_event->{reason} = 'Disabling API key authentication requirements'; - $mfa_event->{postback} = { - action => 'userprefs.cgi', - fields => { - tab => 'settings', - }, - }; - $user->mfa_provider->verify_prompt($mfa_event); - } + if ($mfa_event) { + $mfa_event->{reason} = 'Disabling API key authentication requirements'; + $mfa_event->{postback} + = {action => 'userprefs.cgi', fields => {tab => 'settings',},}; + $user->mfa_provider->verify_prompt($mfa_event); + } } sub MfaSettings { - my $cgi = Bugzilla->cgi; - my $user = Bugzilla->user; - return unless $user->mfa; - - my $event = $user->mfa_provider->verify_token($cgi->param('mfa_token')); - - my $settings = $user->settings; - if ($event->{reset}) { - $settings->{api_key_only}->reset_to_default(); - } - elsif (my $value = $event->{set}) { - $settings->{api_key_only}->set($value); - } - - $vars->{settings} = $user->settings(1); - clear_settings_cache($user->id); + my $cgi = Bugzilla->cgi; + my $user = Bugzilla->user; + return unless $user->mfa; + + my $event = $user->mfa_provider->verify_token($cgi->param('mfa_token')); + + my $settings = $user->settings; + if ($event->{reset}) { + $settings->{api_key_only}->reset_to_default(); + } + elsif (my $value = $event->{set}) { + $settings->{api_key_only}->set($value); + } + + $vars->{settings} = $user->settings(1); + clear_settings_cache($user->id); } sub DoEmail { - my $dbh = Bugzilla->dbh; - my $user = Bugzilla->user; - - ########################################################################### - # User watching - ########################################################################### - my $watched_ref = $dbh->selectcol_arrayref( - "SELECT profiles.login_name FROM watch INNER JOIN profiles" . - " ON watch.watched = profiles.userid" . - " WHERE watcher = ?" . - " ORDER BY profiles.login_name", - undef, $user->id); - $vars->{'watchedusers'} = $watched_ref; - - my $watcher_ids = $dbh->selectcol_arrayref( - "SELECT watcher FROM watch WHERE watched = ?", - undef, $user->id); - - my @watchers; - foreach my $watcher_id (@$watcher_ids) { - my $watcher = new Bugzilla::User($watcher_id); - push(@watchers, Bugzilla::User::identity($watcher)); - } - - @watchers = sort { lc($a) cmp lc($b) } @watchers; - $vars->{'watchers'} = \@watchers; + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + + ########################################################################### + # User watching + ########################################################################### + my $watched_ref = $dbh->selectcol_arrayref( + "SELECT profiles.login_name FROM watch INNER JOIN profiles" + . " ON watch.watched = profiles.userid" + . " WHERE watcher = ?" + . " ORDER BY profiles.login_name", + undef, $user->id + ); + $vars->{'watchedusers'} = $watched_ref; + + my $watcher_ids + = $dbh->selectcol_arrayref("SELECT watcher FROM watch WHERE watched = ?", + undef, $user->id); + + my @watchers; + foreach my $watcher_id (@$watcher_ids) { + my $watcher = new Bugzilla::User($watcher_id); + push(@watchers, Bugzilla::User::identity($watcher)); + } + + @watchers = sort { lc($a) cmp lc($b) } @watchers; + $vars->{'watchers'} = \@watchers; } sub SaveEmail { - my $dbh = Bugzilla->dbh; - my $cgi = Bugzilla->cgi; - my $user = Bugzilla->user; - - Bugzilla::User::match_field({ 'new_watchedusers' => {'type' => 'multi'} }); - - ########################################################################### - # Role-based preferences - ########################################################################### - $dbh->bz_start_transaction(); - - my $sth_insert = $dbh->prepare('INSERT INTO email_setting - (user_id, relationship, event) VALUES (?, ?, ?)'); - - my $sth_delete = $dbh->prepare('DELETE FROM email_setting - WHERE user_id = ? AND relationship = ? AND event = ?'); - # Load current email preferences into memory before updating them. - my $settings = $user->mail_settings; - - # Update the table - first, with normal events in the - # relationship/event matrix. - my %relationships = Bugzilla::BugMail::relationships(); - foreach my $rel (keys %relationships) { - next if ($rel == REL_QA && !Bugzilla->params->{'useqacontact'}); - # Positive events: a ticked box means "send me mail." - foreach my $event (POS_EVENTS) { - my $is_set = $cgi->param("email-$rel-$event"); - if ($is_set xor $settings->{$rel}{$event}) { - if ($is_set) { - $sth_insert->execute($user->id, $rel, $event); - } - else { - $sth_delete->execute($user->id, $rel, $event); - } - } + my $dbh = Bugzilla->dbh; + my $cgi = Bugzilla->cgi; + my $user = Bugzilla->user; + + Bugzilla::User::match_field({'new_watchedusers' => {'type' => 'multi'}}); + + ########################################################################### + # Role-based preferences + ########################################################################### + $dbh->bz_start_transaction(); + + my $sth_insert = $dbh->prepare( + 'INSERT INTO email_setting + (user_id, relationship, event) VALUES (?, ?, ?)' + ); + + my $sth_delete = $dbh->prepare( + 'DELETE FROM email_setting + WHERE user_id = ? AND relationship = ? AND event = ?' + ); + + # Load current email preferences into memory before updating them. + my $settings = $user->mail_settings; + + # Update the table - first, with normal events in the + # relationship/event matrix. + my %relationships = Bugzilla::BugMail::relationships(); + foreach my $rel (keys %relationships) { + next if ($rel == REL_QA && !Bugzilla->params->{'useqacontact'}); + + # Positive events: a ticked box means "send me mail." + foreach my $event (POS_EVENTS) { + my $is_set = $cgi->param("email-$rel-$event"); + if ($is_set xor $settings->{$rel}{$event}) { + if ($is_set) { + $sth_insert->execute($user->id, $rel, $event); } - - # Negative events: a ticked box means "don't send me mail." - foreach my $event (NEG_EVENTS) { - my $is_set = $cgi->param("neg-email-$rel-$event"); - if (!$is_set xor $settings->{$rel}{$event}) { - if (!$is_set) { - $sth_insert->execute($user->id, $rel, $event); - } - else { - $sth_delete->execute($user->id, $rel, $event); - } - } + else { + $sth_delete->execute($user->id, $rel, $event); } + } } - # Global positive events: a ticked box means "send me mail." - foreach my $event (GLOBAL_EVENTS) { - my $is_set = $cgi->param("email-" . REL_ANY . "-$event"); - if ($is_set xor $settings->{+REL_ANY}{$event}) { - if ($is_set) { - $sth_insert->execute($user->id, REL_ANY, $event); - } - else { - $sth_delete->execute($user->id, REL_ANY, $event); - } + # Negative events: a ticked box means "don't send me mail." + foreach my $event (NEG_EVENTS) { + my $is_set = $cgi->param("neg-email-$rel-$event"); + if (!$is_set xor $settings->{$rel}{$event}) { + if (!$is_set) { + $sth_insert->execute($user->id, $rel, $event); } + else { + $sth_delete->execute($user->id, $rel, $event); + } + } } + } + + # Global positive events: a ticked box means "send me mail." + foreach my $event (GLOBAL_EVENTS) { + my $is_set = $cgi->param("email-" . REL_ANY . "-$event"); + if ($is_set xor $settings->{+REL_ANY}{$event}) { + if ($is_set) { + $sth_insert->execute($user->id, REL_ANY, $event); + } + else { + $sth_delete->execute($user->id, REL_ANY, $event); + } + } + } - $dbh->bz_commit_transaction(); - - # We have to clear the cache about email preferences. - delete $user->{'mail_settings'}; - - ########################################################################### - # User watching - ########################################################################### - if (defined $cgi->param('new_watchedusers') - || defined $cgi->param('remove_watched_users')) - { - $dbh->bz_start_transaction(); - - # Use this to protect error messages on duplicate submissions - my $old_watch_ids = - $dbh->selectcol_arrayref("SELECT watched FROM watch" - . " WHERE watcher = ?", undef, $user->id); + $dbh->bz_commit_transaction(); - # The new information given to us by the user. - my $new_watched_users = join(',', $cgi->param('new_watchedusers')) || ''; - my @new_watch_names = split(/[,\s]+/, $new_watched_users); - my %new_watch_ids; + # We have to clear the cache about email preferences. + delete $user->{'mail_settings'}; - foreach my $username (@new_watch_names) { - my $watched_userid = login_to_id(trim($username), THROW_ERROR); - $new_watch_ids{$watched_userid} = 1; - } + ########################################################################### + # User watching + ########################################################################### + if ( defined $cgi->param('new_watchedusers') + || defined $cgi->param('remove_watched_users')) + { + $dbh->bz_start_transaction(); - # Add people who were added. - my $insert_sth = $dbh->prepare('INSERT INTO watch (watched, watcher)' - . ' VALUES (?, ?)'); - foreach my $add_me (keys(%new_watch_ids)) { - next if grep($_ == $add_me, @$old_watch_ids); - $insert_sth->execute($add_me, $user->id); - } + # Use this to protect error messages on duplicate submissions + my $old_watch_ids + = $dbh->selectcol_arrayref("SELECT watched FROM watch" . " WHERE watcher = ?", + undef, $user->id); - if (defined $cgi->param('remove_watched_users')) { - my @removed = $cgi->param('watched_by_you'); - # Remove people who were removed. - my $delete_sth = $dbh->prepare('DELETE FROM watch WHERE watched = ?' - . ' AND watcher = ?'); - - my %remove_watch_ids; - foreach my $username (@removed) { - my $watched_userid = login_to_id(trim($username), THROW_ERROR); - $remove_watch_ids{$watched_userid} = 1; - } - foreach my $remove_me (keys(%remove_watch_ids)) { - $delete_sth->execute($remove_me, $user->id); - } - } + # The new information given to us by the user. + my $new_watched_users = join(',', $cgi->param('new_watchedusers')) || ''; + my @new_watch_names = split(/[,\s]+/, $new_watched_users); + my %new_watch_ids; - $dbh->bz_commit_transaction(); + foreach my $username (@new_watch_names) { + my $watched_userid = login_to_id(trim($username), THROW_ERROR); + $new_watch_ids{$watched_userid} = 1; } - ########################################################################### - # Ignore Bugs - ########################################################################### - my %ignored_bugs = map { $_->{'id'} => 1 } @{$user->bugs_ignored}; - - # Validate the new bugs to ignore by checking that they exist and also - # if the user gave an alias - my @add_ignored = split(/[\s,]+/, $cgi->param('add_ignored_bugs')); - @add_ignored = map { Bugzilla::Bug->check($_)->id } @add_ignored; - map { $ignored_bugs{$_} = 1 } @add_ignored; - - # Remove any bug ids the user no longer wants to ignore - foreach my $key (grep(/^remove_ignored_bug_/, $cgi->param)) { - my ($bug_id) = $key =~ /(\d+)$/; - delete $ignored_bugs{$bug_id}; + # Add people who were added. + my $insert_sth + = $dbh->prepare('INSERT INTO watch (watched, watcher)' . ' VALUES (?, ?)'); + foreach my $add_me (keys(%new_watch_ids)) { + next if grep($_ == $add_me, @$old_watch_ids); + $insert_sth->execute($add_me, $user->id); } - # Update the database with any changes made - my ($removed, $added) = diff_arrays([ map { $_->{'id'} } @{$user->bugs_ignored} ], - [ keys %ignored_bugs ]); + if (defined $cgi->param('remove_watched_users')) { + my @removed = $cgi->param('watched_by_you'); + + # Remove people who were removed. + my $delete_sth + = $dbh->prepare('DELETE FROM watch WHERE watched = ?' . ' AND watcher = ?'); + + my %remove_watch_ids; + foreach my $username (@removed) { + my $watched_userid = login_to_id(trim($username), THROW_ERROR); + $remove_watch_ids{$watched_userid} = 1; + } + foreach my $remove_me (keys(%remove_watch_ids)) { + $delete_sth->execute($remove_me, $user->id); + } + } - if (scalar @$removed || scalar @$added) { - $dbh->bz_start_transaction(); + $dbh->bz_commit_transaction(); + } + + ########################################################################### + # Ignore Bugs + ########################################################################### + my %ignored_bugs = map { $_->{'id'} => 1 } @{$user->bugs_ignored}; + + # Validate the new bugs to ignore by checking that they exist and also + # if the user gave an alias + my @add_ignored = split(/[\s,]+/, $cgi->param('add_ignored_bugs')); + @add_ignored = map { Bugzilla::Bug->check($_)->id } @add_ignored; + map { $ignored_bugs{$_} = 1 } @add_ignored; + + # Remove any bug ids the user no longer wants to ignore + foreach my $key (grep(/^remove_ignored_bug_/, $cgi->param)) { + my ($bug_id) = $key =~ /(\d+)$/; + delete $ignored_bugs{$bug_id}; + } + + # Update the database with any changes made + my ($removed, $added) + = diff_arrays([map { $_->{'id'} } @{$user->bugs_ignored}], + [keys %ignored_bugs]); + + if (scalar @$removed || scalar @$added) { + $dbh->bz_start_transaction(); - if (scalar @$removed) { - $dbh->do('DELETE FROM email_bug_ignore WHERE user_id = ? AND ' . - $dbh->sql_in('bug_id', $removed), - undef, $user->id); - } - if (scalar @$added) { - my $sth = $dbh->prepare('INSERT INTO email_bug_ignore - (user_id, bug_id) VALUES (?, ?)'); - $sth->execute($user->id, $_) foreach @$added; - } + if (scalar @$removed) { + $dbh->do( + 'DELETE FROM email_bug_ignore WHERE user_id = ? AND ' + . $dbh->sql_in('bug_id', $removed), + undef, $user->id + ); + } + if (scalar @$added) { + my $sth = $dbh->prepare( + 'INSERT INTO email_bug_ignore + (user_id, bug_id) VALUES (?, ?)' + ); + $sth->execute($user->id, $_) foreach @$added; + } - # Reset the cache of ignored bugs if the list changed. - delete $user->{bugs_ignored}; + # Reset the cache of ignored bugs if the list changed. + delete $user->{bugs_ignored}; - $dbh->bz_commit_transaction(); - } + $dbh->bz_commit_transaction(); + } } sub DoPermissions { - my $dbh = Bugzilla->dbh; - my $user = Bugzilla->user; - my (@has_bits, @set_bits); - - my $groups = $dbh->selectall_arrayref( - "SELECT DISTINCT name, description FROM groups WHERE id IN (" . - $user->groups_as_string . ") ORDER BY name"); - foreach my $group (@$groups) { - my ($nam, $desc) = @$group; - push(@has_bits, {"desc" => $desc, "name" => $nam}); - } - $groups = $dbh->selectall_arrayref('SELECT DISTINCT id, name, description + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + my (@has_bits, @set_bits); + + my $groups + = $dbh->selectall_arrayref( + "SELECT DISTINCT name, description FROM groups WHERE id IN (" + . $user->groups_as_string + . ") ORDER BY name"); + foreach my $group (@$groups) { + my ($nam, $desc) = @$group; + push(@has_bits, {"desc" => $desc, "name" => $nam}); + } + $groups = $dbh->selectall_arrayref( + 'SELECT DISTINCT id, name, description FROM groups - ORDER BY name'); - foreach my $group (@$groups) { - my ($group_id, $nam, $desc) = @$group; - if ($user->can_bless($group_id)) { - push(@set_bits, {"desc" => $desc, "name" => $nam}); - } + ORDER BY name' + ); + foreach my $group (@$groups) { + my ($group_id, $nam, $desc) = @$group; + if ($user->can_bless($group_id)) { + push(@set_bits, {"desc" => $desc, "name" => $nam}); } + } - # If the user has product specific privileges, inform him about that. - foreach my $privs (PER_PRODUCT_PRIVILEGES) { - next if $user->in_group($privs); - $vars->{"local_$privs"} = $user->get_products_by_permission($privs); - } + # If the user has product specific privileges, inform him about that. + foreach my $privs (PER_PRODUCT_PRIVILEGES) { + next if $user->in_group($privs); + $vars->{"local_$privs"} = $user->get_products_by_permission($privs); + } - $vars->{'has_bits'} = \@has_bits; - $vars->{'set_bits'} = \@set_bits; + $vars->{'has_bits'} = \@has_bits; + $vars->{'set_bits'} = \@set_bits; } # No SavePermissions() because this panel has no changeable fields. sub DoSavedSearches { - my $dbh = Bugzilla->dbh; - my $user = Bugzilla->user; - - if ($user->queryshare_groups_as_string) { - $vars->{'queryshare_groups'} = - Bugzilla::Group->new_from_list($user->queryshare_groups); - } - $vars->{'bless_group_ids'} = [map { $_->id } @{$user->bless_groups}]; + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + + if ($user->queryshare_groups_as_string) { + $vars->{'queryshare_groups'} + = Bugzilla::Group->new_from_list($user->queryshare_groups); + } + $vars->{'bless_group_ids'} = [map { $_->id } @{$user->bless_groups}]; } sub SaveSavedSearches { - my $cgi = Bugzilla->cgi; - my $dbh = Bugzilla->dbh; - my $user = Bugzilla->user; + my $cgi = Bugzilla->cgi; + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; - # We'll need this in a loop, so do the call once. - my $user_id = $user->id; + # We'll need this in a loop, so do the call once. + my $user_id = $user->id; - my $sth_insert_nl = $dbh->prepare('INSERT INTO namedqueries_link_in_footer + my $sth_insert_nl = $dbh->prepare( + 'INSERT INTO namedqueries_link_in_footer (namedquery_id, user_id) - VALUES (?, ?)'); - my $sth_delete_nl = $dbh->prepare('DELETE FROM namedqueries_link_in_footer + VALUES (?, ?)' + ); + my $sth_delete_nl = $dbh->prepare( + 'DELETE FROM namedqueries_link_in_footer WHERE namedquery_id = ? - AND user_id = ?'); - my $sth_insert_ngm = $dbh->prepare('INSERT INTO namedquery_group_map + AND user_id = ?' + ); + my $sth_insert_ngm = $dbh->prepare( + 'INSERT INTO namedquery_group_map (namedquery_id, group_id) - VALUES (?, ?)'); - my $sth_update_ngm = $dbh->prepare('UPDATE namedquery_group_map + VALUES (?, ?)' + ); + my $sth_update_ngm = $dbh->prepare( + 'UPDATE namedquery_group_map SET group_id = ? - WHERE namedquery_id = ?'); - my $sth_delete_ngm = $dbh->prepare('DELETE FROM namedquery_group_map - WHERE namedquery_id = ?'); - - # Update namedqueries_link_in_footer for this user. - foreach my $q (@{$user->queries}, @{$user->queries_available}) { - if (defined $cgi->param("link_in_footer_" . $q->id)) { - $sth_insert_nl->execute($q->id, $user_id) if !$q->link_in_footer; - } - else { - $sth_delete_nl->execute($q->id, $user_id) if $q->link_in_footer; - } + WHERE namedquery_id = ?' + ); + my $sth_delete_ngm = $dbh->prepare( + 'DELETE FROM namedquery_group_map + WHERE namedquery_id = ?' + ); + + # Update namedqueries_link_in_footer for this user. + foreach my $q (@{$user->queries}, @{$user->queries_available}) { + if (defined $cgi->param("link_in_footer_" . $q->id)) { + $sth_insert_nl->execute($q->id, $user_id) if !$q->link_in_footer; } + else { + $sth_delete_nl->execute($q->id, $user_id) if $q->link_in_footer; + } + } - # For user's own queries, update namedquery_group_map. - foreach my $q (@{$user->queries}) { - my $group_id; + # For user's own queries, update namedquery_group_map. + foreach my $q (@{$user->queries}) { + my $group_id; - if ($user->in_group(Bugzilla->params->{'querysharegroup'})) { - $group_id = $cgi->param("share_" . $q->id) || ''; - } + if ($user->in_group(Bugzilla->params->{'querysharegroup'})) { + $group_id = $cgi->param("share_" . $q->id) || ''; + } - if ($group_id) { - # Don't allow the user to share queries with groups he's not - # allowed to. - next unless grep($_ eq $group_id, @{$user->queryshare_groups}); - - # $group_id is now definitely a valid ID of a group the - # user can share queries with, so we can trick_taint. - detaint_natural($group_id); - if ($q->shared_with_group) { - $sth_update_ngm->execute($group_id, $q->id); - } - else { - $sth_insert_ngm->execute($q->id, $group_id); - } - - # If we're sharing our query with a group we can bless, we - # have the ability to add link to our search to the footer of - # direct group members automatically. - if ($user->can_bless($group_id) && $cgi->param('force_' . $q->id)) { - my $group = new Bugzilla::Group($group_id); - my $members = $group->members_non_inherited; - foreach my $member (@$members) { - next if $member->id == $user->id; - $sth_insert_nl->execute($q->id, $member->id) - if !$q->link_in_footer($member); - } - } - } - else { - # They have unshared that query. - if ($q->shared_with_group) { - $sth_delete_ngm->execute($q->id); - } - - # Don't remove namedqueries_link_in_footer entries for users - # subscribing to the shared query. The idea is that they will - # probably want to be subscribers again should the sharing - # user choose to share the query again. + if ($group_id) { + + # Don't allow the user to share queries with groups he's not + # allowed to. + next unless grep($_ eq $group_id, @{$user->queryshare_groups}); + + # $group_id is now definitely a valid ID of a group the + # user can share queries with, so we can trick_taint. + detaint_natural($group_id); + if ($q->shared_with_group) { + $sth_update_ngm->execute($group_id, $q->id); + } + else { + $sth_insert_ngm->execute($q->id, $group_id); + } + + # If we're sharing our query with a group we can bless, we + # have the ability to add link to our search to the footer of + # direct group members automatically. + if ($user->can_bless($group_id) && $cgi->param('force_' . $q->id)) { + my $group = new Bugzilla::Group($group_id); + my $members = $group->members_non_inherited; + foreach my $member (@$members) { + next if $member->id == $user->id; + $sth_insert_nl->execute($q->id, $member->id) if !$q->link_in_footer($member); } + } + } + else { + # They have unshared that query. + if ($q->shared_with_group) { + $sth_delete_ngm->execute($q->id); + } + + # Don't remove namedqueries_link_in_footer entries for users + # subscribing to the shared query. The idea is that they will + # probably want to be subscribers again should the sharing + # user choose to share the query again. } + } - $user->flush_queries_cache; + $user->flush_queries_cache; - # Update profiles.mybugslink. - my $showmybugslink = defined($cgi->param("showmybugslink")) ? 1 : 0; - $dbh->do("UPDATE profiles SET mybugslink = ? WHERE userid = ?", - undef, ($showmybugslink, $user->id)); - $user->{'showmybugslink'} = $showmybugslink; - Bugzilla->memcached->clear({ table => 'profiles', id => $user->id }); + # Update profiles.mybugslink. + my $showmybugslink = defined($cgi->param("showmybugslink")) ? 1 : 0; + $dbh->do("UPDATE profiles SET mybugslink = ? WHERE userid = ?", + undef, ($showmybugslink, $user->id)); + $user->{'showmybugslink'} = $showmybugslink; + Bugzilla->memcached->clear({table => 'profiles', id => $user->id}); } sub SaveMFA { - my $cgi = Bugzilla->cgi; - my $user = Bugzilla->user; - my $action = $cgi->param('mfa_action') // ''; - my $params = Bugzilla->input_params; - - my $crypt_password = $user->cryptpassword; - if (bz_crypt(delete $params->{password}, $crypt_password) ne $crypt_password) { - ThrowUserError('password_incorrect'); - } + my $cgi = Bugzilla->cgi; + my $user = Bugzilla->user; + my $action = $cgi->param('mfa_action') // ''; + my $params = Bugzilla->input_params; + + my $crypt_password = $user->cryptpassword; + if (bz_crypt(delete $params->{password}, $crypt_password) ne $crypt_password) { + ThrowUserError('password_incorrect'); + } + + my $mfa = $cgi->param('mfa') // $user->mfa; + my $provider = Bugzilla::MFA->new_from($user, $mfa) // return; + + my $reason; + if ($action eq 'enable') { + $provider->enroll(Bugzilla->input_params); + $reason = 'Two-factor enrollment'; + } + elsif ($action eq 'recovery') { + $reason = 'Recovery code generation'; + } + elsif ($action eq 'disable') { + $reason = 'Disabling two-factor authentication'; + } + + if ($provider->can_verify_inline) { + $provider->verify_check($params); + SaveMFAupdate($cgi->param('mfa_action'), $mfa); + } + else { + my $mfa_event = { + postback => + {action => 'userprefs.cgi', fields => {tab => 'mfa', mfa => $mfa,},}, + reason => $reason, + action => $action, + }; + $provider->verify_prompt($mfa_event); + } +} - my $mfa = $cgi->param('mfa') // $user->mfa; - my $provider = Bugzilla::MFA->new_from($user, $mfa) // return; +sub SaveMFAupdate { + my ($action, $mfa) = @_; + my $user = Bugzilla->user; + my $dbh = Bugzilla->dbh; + $action //= ''; - my $reason; - if ($action eq 'enable') { - $provider->enroll(Bugzilla->input_params); - $reason = 'Two-factor enrollment'; - } - elsif ($action eq 'recovery') { - $reason = 'Recovery code generation'; - } - elsif ($action eq 'disable') { - $reason = 'Disabling two-factor authentication'; - } + if ($action eq 'enable') { + $dbh->bz_start_transaction; - if ($provider->can_verify_inline) { - $provider->verify_check($params); - SaveMFAupdate($cgi->param('mfa_action'), $mfa); - } - else { - my $mfa_event = { - postback => { - action => 'userprefs.cgi', - fields => { - tab => 'mfa', - mfa => $mfa, - }, - }, - reason => $reason, - action => $action, - }; - $provider->verify_prompt($mfa_event); - } -} + $user->set_mfa($mfa); + $user->mfa_provider->enrolled(); + Bugzilla->request_cache->{mfa_warning} = 0; + my $settings = Bugzilla->user->settings; + $settings->{api_key_only}->set('on'); + clear_settings_cache(Bugzilla->user->id); -sub SaveMFAupdate { - my ($action, $mfa) = @_; - my $user = Bugzilla->user; - my $dbh = Bugzilla->dbh; - $action //= ''; - - if ($action eq 'enable') { - $dbh->bz_start_transaction; - - $user->set_mfa($mfa); - $user->mfa_provider->enrolled(); - Bugzilla->request_cache->{mfa_warning} = 0; - my $settings = Bugzilla->user->settings; - $settings->{api_key_only}->set('on'); - clear_settings_cache(Bugzilla->user->id); - - $user->update({ keep_session => 1, keep_tokens => 1 }); - $dbh->bz_commit_transaction; - } + $user->update({keep_session => 1, keep_tokens => 1}); + $dbh->bz_commit_transaction; + } - elsif ($action eq 'recovery') { - my $codes = $user->mfa_provider->generate_recovery_codes(); - my $token = issue_short_lived_session_token('mfa-recovery'); - set_token_extra_data($token, $codes); - $vars->{mfa_recovery_token} = $token; + elsif ($action eq 'recovery') { + my $codes = $user->mfa_provider->generate_recovery_codes(); + my $token = issue_short_lived_session_token('mfa-recovery'); + set_token_extra_data($token, $codes); + $vars->{mfa_recovery_token} = $token; - } + } - elsif ($action eq 'disable') { - $user->set_mfa(''); - $user->update({ keep_session => 1, keep_tokens => 1 }); + elsif ($action eq 'disable') { + $user->set_mfa(''); + $user->update({keep_session => 1, keep_tokens => 1}); - } + } } sub SaveMFAcallback { - my $cgi = Bugzilla->cgi; - my $user = Bugzilla->user; + my $cgi = Bugzilla->cgi; + my $user = Bugzilla->user; - my $mfa = $cgi->param('mfa'); - my $provider = Bugzilla::MFA->new_from($user, $mfa) // return; - my $event = $provider->verify_token($cgi->param('mfa_token')); + my $mfa = $cgi->param('mfa'); + my $provider = Bugzilla::MFA->new_from($user, $mfa) // return; + my $event = $provider->verify_token($cgi->param('mfa_token')); - SaveMFAupdate($event->{action}, $mfa); + SaveMFAupdate($event->{action}, $mfa); } sub DoMFA { - my $cgi = Bugzilla->cgi; - return unless my $provider = $cgi->param('frame'); - - print $cgi->header( - -Cache_Control => 'no-cache, no-store, must-revalidate', - -Expires => 'Thu, 01 Dec 1994 16:00:00 GMT', - -Pragma => 'no-cache', - ); - if ($provider eq 'recovery') { - my $token = $cgi->param('t'); - $vars->{codes} = get_token_extra_data($token); - delete_token($token); - $template->process("mfa/recovery.html.tmpl", $vars) - || ThrowTemplateError($template->error()); - } - elsif ($provider =~ /^[a-z]+$/) { - trick_taint($provider); - $template->process("mfa/$provider/enroll.html.tmpl", $vars) - || ThrowTemplateError($template->error()); - } - exit; + my $cgi = Bugzilla->cgi; + return unless my $provider = $cgi->param('frame'); + + print $cgi->header( + -Cache_Control => 'no-cache, no-store, must-revalidate', + -Expires => 'Thu, 01 Dec 1994 16:00:00 GMT', + -Pragma => 'no-cache', + ); + if ($provider eq 'recovery') { + my $token = $cgi->param('t'); + $vars->{codes} = get_token_extra_data($token); + delete_token($token); + $template->process("mfa/recovery.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + elsif ($provider =~ /^[a-z]+$/) { + trick_taint($provider); + $template->process("mfa/$provider/enroll.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + exit; } sub SaveSessions { - my $cgi = Bugzilla->cgi; - my $dbh = Bugzilla->dbh; - my $user = Bugzilla->user; - - # Do it in a transaction. - $dbh->bz_start_transaction; - if ($cgi->param("session_logout_all")) { - my $info_getter = $user->authorizer && $user->authorizer->successful_info_getter(); - if ($info_getter->cookie) { - $dbh->do("DELETE FROM logincookies WHERE userid = ? AND cookie != ?", undef, - $user->id, $info_getter->cookie); - } + my $cgi = Bugzilla->cgi; + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + + # Do it in a transaction. + $dbh->bz_start_transaction; + if ($cgi->param("session_logout_all")) { + my $info_getter + = $user->authorizer && $user->authorizer->successful_info_getter(); + if ($info_getter->cookie) { + $dbh->do("DELETE FROM logincookies WHERE userid = ? AND cookie != ?", + undef, $user->id, $info_getter->cookie); } - else { - my @logout_ids = $cgi->param('session_logout_id'); - my $sessions = Bugzilla::User::Session->new_from_list(\@logout_ids); - foreach my $session (@$sessions) { - $session->remove_from_db if $session->userid == $user->id; - } + } + else { + my @logout_ids = $cgi->param('session_logout_id'); + my $sessions = Bugzilla::User::Session->new_from_list(\@logout_ids); + foreach my $session (@$sessions) { + $session->remove_from_db if $session->userid == $user->id; } + } - $dbh->bz_commit_transaction; + $dbh->bz_commit_transaction; } sub DoSessions { - my $user = Bugzilla->user; - my $dbh = Bugzilla->dbh; - my $sessions = Bugzilla::User::Session->match({ userid => $user->id, LIMIT => SESSION_MAX + 1 }); - my $info_getter = $user->authorizer && $user->authorizer->successful_info_getter(); - - if ($info_getter && $info_getter->can('cookie')) { - foreach my $session (@$sessions) { - $session->{current} = $info_getter->cookie eq $session->{cookie}; - } + my $user = Bugzilla->user; + my $dbh = Bugzilla->dbh; + my $sessions = Bugzilla::User::Session->match( + {userid => $user->id, LIMIT => SESSION_MAX + 1}); + my $info_getter + = $user->authorizer && $user->authorizer->successful_info_getter(); + + if ($info_getter && $info_getter->can('cookie')) { + foreach my $session (@$sessions) { + $session->{current} = $info_getter->cookie eq $session->{cookie}; } - my ($count) = $dbh->selectrow_array("SELECT count(*) FROM logincookies WHERE userid = ?", undef, - $user->id); - - $vars->{too_many_sessions} = @$sessions == SESSION_MAX + 1; - $vars->{sessions} = $sessions; - $vars->{session_count} = $count; - $vars->{session_max} = SESSION_MAX; - pop @$sessions if $vars->{too_many_sessions}; + } + my ($count) + = $dbh->selectrow_array("SELECT count(*) FROM logincookies WHERE userid = ?", + undef, $user->id); + + $vars->{too_many_sessions} = @$sessions == SESSION_MAX + 1; + $vars->{sessions} = $sessions; + $vars->{session_count} = $count; + $vars->{session_max} = SESSION_MAX; + pop @$sessions if $vars->{too_many_sessions}; } sub DoApiKey { - my $user = Bugzilla->user; + my $user = Bugzilla->user; - my $api_keys = Bugzilla::User::APIKey->match({ user_id => $user->id }); - $vars->{api_keys} = $api_keys; - $vars->{any_revoked} = grep { $_->revoked } @$api_keys; + my $api_keys = Bugzilla::User::APIKey->match({user_id => $user->id}); + $vars->{api_keys} = $api_keys; + $vars->{any_revoked} = grep { $_->revoked } @$api_keys; } sub SaveApiKey { - my $cgi = Bugzilla->cgi; - my $dbh = Bugzilla->dbh; - my $user = Bugzilla->user; - my @mfa_events; - - # Do it in a transaction. - $dbh->bz_start_transaction; - - # Update any existing keys - my $api_keys = Bugzilla::User::APIKey->match({ user_id => $user->id }); - foreach my $api_key (@$api_keys) { - my $description = $cgi->param('description_' . $api_key->id); - my $revoked = !!$cgi->param('revoked_' . $api_key->id); - - if ($description ne $api_key->description || $revoked != $api_key->revoked) { - if ($user->mfa && !$revoked && $api_key->revoked) { - push @mfa_events, { - type => 'update', - reason => 'enabling an API key', - id => $api_key->id, - description => $description, - }; - } - else { - $api_key->set_all({ - description => $description, - revoked => $revoked, - }); - $api_key->update(); - if ($revoked) { - Bugzilla->log_user_request(undef, undef, 'api-key-revoke') - } - else { - Bugzilla->log_user_request(undef, undef, 'api-key-unrevoke') - } - } - } - } - - # Create a new API key if requested. - if ($cgi->param('new_key')) { - my $description = $cgi->param('new_description'); - if ($user->mfa) { - push @mfa_events, { - type => 'create', - reason => 'creating an API key', - description => $description, - }; + my $cgi = Bugzilla->cgi; + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + my @mfa_events; + + # Do it in a transaction. + $dbh->bz_start_transaction; + + # Update any existing keys + my $api_keys = Bugzilla::User::APIKey->match({user_id => $user->id}); + foreach my $api_key (@$api_keys) { + my $description = $cgi->param('description_' . $api_key->id); + my $revoked = !!$cgi->param('revoked_' . $api_key->id); + + if ($description ne $api_key->description || $revoked != $api_key->revoked) { + if ($user->mfa && !$revoked && $api_key->revoked) { + push @mfa_events, + { + type => 'update', + reason => 'enabling an API key', + id => $api_key->id, + description => $description, + }; + } + else { + $api_key->set_all({description => $description, revoked => $revoked,}); + $api_key->update(); + if ($revoked) { + Bugzilla->log_user_request(undef, undef, 'api-key-revoke'); } else { - $vars->{new_key} = _create_api_key($description); + Bugzilla->log_user_request(undef, undef, 'api-key-unrevoke'); } + } } - - $dbh->bz_commit_transaction; - - if (@mfa_events) { - # build the fields for the postback - my $mfa_event = { - postback => { - action => 'userprefs.cgi', - fields => { - tab => 'apikey', - }, - }, - reason => ucfirst(join(' and ', map { $_->{reason} } @mfa_events)), - actions => \@mfa_events, + } + + # Create a new API key if requested. + if ($cgi->param('new_key')) { + my $description = $cgi->param('new_description'); + if ($user->mfa) { + push @mfa_events, + { + type => 'create', + reason => 'creating an API key', + description => $description, }; - # display 2fa verification - $user->mfa_provider->verify_prompt($mfa_event); } + else { + $vars->{new_key} = _create_api_key($description); + } + } + + $dbh->bz_commit_transaction; + + if (@mfa_events) { + + # build the fields for the postback + my $mfa_event = { + postback => {action => 'userprefs.cgi', fields => {tab => 'apikey',},}, + reason => ucfirst(join(' and ', map { $_->{reason} } @mfa_events)), + actions => \@mfa_events, + }; + + # display 2fa verification + $user->mfa_provider->verify_prompt($mfa_event); + } } sub MfaApiKey { - my $cgi = Bugzilla->cgi; - my $user = Bugzilla->user; - my $dbh = Bugzilla->dbh; - return unless $user->mfa; + my $cgi = Bugzilla->cgi; + my $user = Bugzilla->user; + my $dbh = Bugzilla->dbh; + return unless $user->mfa; - my $event = $user->mfa_provider->verify_token($cgi->param('mfa_token')); + my $event = $user->mfa_provider->verify_token($cgi->param('mfa_token')); - foreach my $action (@{ $event->{actions} }) { - if ($action->{type} eq 'create') { - $vars->{new_key} = _create_api_key($action->{description}); - } + foreach my $action (@{$event->{actions}}) { + if ($action->{type} eq 'create') { + $vars->{new_key} = _create_api_key($action->{description}); + } - elsif ($action->{type} eq 'update') { - $dbh->bz_start_transaction; - my $api_key = Bugzilla::User::APIKey->check({ id => $action->{id} }); - $api_key->set_all({ - description => $action->{description}, - revoked => 0, - }); - $api_key->update(); - Bugzilla->log_user_request(undef, undef, 'api-key-unrevoke'); - $dbh->bz_commit_transaction; - } + elsif ($action->{type} eq 'update') { + $dbh->bz_start_transaction; + my $api_key = Bugzilla::User::APIKey->check({id => $action->{id}}); + $api_key->set_all({description => $action->{description}, revoked => 0,}); + $api_key->update(); + Bugzilla->log_user_request(undef, undef, 'api-key-unrevoke'); + $dbh->bz_commit_transaction; } + } } sub _create_api_key { - my ($description) = @_; - my $user = Bugzilla->user; + my ($description) = @_; + my $user = Bugzilla->user; - my $key = Bugzilla::User::APIKey->create({ - user_id => $user->id, - description => $description, + my $key + = Bugzilla::User::APIKey->create({ + user_id => $user->id, description => $description, }); - Bugzilla->log_user_request(undef, undef, 'api-key-create'); + Bugzilla->log_user_request(undef, undef, 'api-key-create'); - # As a security precaution, we always sent out an e-mail when - # an API key is created - my $template = Bugzilla->template_inner($user->setting('lang')); - my $message; - $template->process('email/new-api-key.txt.tmpl', $vars, \$message) - || ThrowTemplateError($template->error()); + # As a security precaution, we always sent out an e-mail when + # an API key is created + my $template = Bugzilla->template_inner($user->setting('lang')); + my $message; + $template->process('email/new-api-key.txt.tmpl', $vars, \$message) + || ThrowTemplateError($template->error()); - MessageToMTA($message); + MessageToMTA($message); - return $key; + return $key; } ############################################################################### @@ -947,15 +971,16 @@ $cgi->delete('GoAheadAndLogIn'); Bugzilla->login(LOGIN_OPTIONAL); if (!Bugzilla->user->id) { - # Use credentials given in the form if login cookies are not available. - $cgi->param('Bugzilla_login', $cgi->param('old_login')); - $cgi->param('Bugzilla_password', $cgi->param('old_password')); + + # Use credentials given in the form if login cookies are not available. + $cgi->param('Bugzilla_login', $cgi->param('old_login')); + $cgi->param('Bugzilla_password', $cgi->param('old_password')); } Bugzilla->login(LOGIN_REQUIRED); -my $save_changes = $cgi->param('dosave'); +my $save_changes = $cgi->param('dosave'); my $disable_account = $cgi->param('account_disable'); -my $mfa_token = $cgi->param('mfa_token'); +my $mfa_token = $cgi->param('mfa_token'); $vars->{'changes_saved'} = $save_changes || $mfa_token; my $current_tab_name = $cgi->param('tab') || "account"; @@ -966,72 +991,76 @@ trick_taint($current_tab_name); $vars->{'current_tab_name'} = $current_tab_name; my $token = $cgi->param('token'); -check_token_data($token, 'edit_user_prefs') if $save_changes || $disable_account; +check_token_data($token, 'edit_user_prefs') + if $save_changes || $disable_account; # Do any saving, and then display the current tab. SWITCH: for ($current_tab_name) { - # Extensions must set it to 1 to confirm the tab is valid. - my $handled = 0; - Bugzilla::Hook::process('user_preferences', - { 'vars' => $vars, - save_changes => $save_changes, - current_tab => $current_tab_name, - handled => \$handled }); - last SWITCH if $handled; - - /^account$/ && do { - MfaAccount() if $mfa_token; - DisableAccount() if $disable_account; - SaveAccount() if $save_changes; - DoAccount(); - last SWITCH; - }; - /^settings$/ && do { - MfaSettings() if $mfa_token; - SaveSettings() if $save_changes; - DoSettings(); - last SWITCH; - }; - /^email$/ && do { - SaveEmail() if $save_changes; - DoEmail(); - last SWITCH; - }; - /^permissions$/ && do { - DoPermissions(); - last SWITCH; - }; - /^saved-searches$/ && do { - SaveSavedSearches() if $save_changes; - DoSavedSearches(); - last SWITCH; - }; - /^apikey$/ && do { - MfaApiKey() if $mfa_token; - SaveApiKey() if $save_changes; - DoApiKey(); - last SWITCH; - }; - /^sessions$/ && do { - SaveSessions() if $save_changes; - DoSessions(); - last SWITCH; - }; - /^mfa$/ && do { - SaveMFAcallback() if $mfa_token; - SaveMFA() if $save_changes; - DoMFA(); - last SWITCH; - }; - - ThrowUserError("unknown_tab", - { current_tab_name => $current_tab_name }); + # Extensions must set it to 1 to confirm the tab is valid. + my $handled = 0; + Bugzilla::Hook::process( + 'user_preferences', + { + 'vars' => $vars, + save_changes => $save_changes, + current_tab => $current_tab_name, + handled => \$handled + } + ); + last SWITCH if $handled; + + /^account$/ && do { + MfaAccount() if $mfa_token; + DisableAccount() if $disable_account; + SaveAccount() if $save_changes; + DoAccount(); + last SWITCH; + }; + /^settings$/ && do { + MfaSettings() if $mfa_token; + SaveSettings() if $save_changes; + DoSettings(); + last SWITCH; + }; + /^email$/ && do { + SaveEmail() if $save_changes; + DoEmail(); + last SWITCH; + }; + /^permissions$/ && do { + DoPermissions(); + last SWITCH; + }; + /^saved-searches$/ && do { + SaveSavedSearches() if $save_changes; + DoSavedSearches(); + last SWITCH; + }; + /^apikey$/ && do { + MfaApiKey() if $mfa_token; + SaveApiKey() if $save_changes; + DoApiKey(); + last SWITCH; + }; + /^sessions$/ && do { + SaveSessions() if $save_changes; + DoSessions(); + last SWITCH; + }; + /^mfa$/ && do { + SaveMFAcallback() if $mfa_token; + SaveMFA() if $save_changes; + DoMFA(); + last SWITCH; + }; + + ThrowUserError("unknown_tab", {current_tab_name => $current_tab_name}); } delete_token($token) if $save_changes; if ($current_tab_name ne 'permissions') { - $vars->{'token'} = issue_session_token('edit_user_prefs'); + $vars->{'token'} = issue_session_token('edit_user_prefs'); } # Generate and return the UI (HTML page) from the appropriate template. |