Commit fd29ee56 authored by Simon Green's avatar Simon Green

Bug 726696 - All authenticated WebServices methods should require username/pass,…

Bug 726696 - All authenticated WebServices methods should require username/pass, token or a valid API key for authentication r=dkl, a=sgreen
parent 9f0f44b7
......@@ -30,7 +30,7 @@ sub new {
my $self = fields::new($class);
$params ||= {};
$params->{Login} ||= Bugzilla->params->{'user_info_class'} . ',Cookie';
$params->{Login} ||= Bugzilla->params->{'user_info_class'} . ',Cookie,APIKey';
$params->{Verify} ||= Bugzilla->params->{'user_verify_class'};
$self->{_info_getter} = new Bugzilla::Auth::Login::Stack($params->{Login});
......
# 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::Login::APIKey;
use 5.10.1;
use strict;
use base qw(Bugzilla::Auth::Login);
use Bugzilla::Constants;
use Bugzilla::User::APIKey;
use Bugzilla::Util;
use Bugzilla::Error;
use constant requires_persistence => 0;
use constant requires_verification => 0;
use constant can_login => 0;
use constant can_logout => 0;
# This method is only available to web services. An API key can never
# be used to authenticate a Web request.
sub get_login_info {
my ($self) = @_;
my $params = Bugzilla->input_params;
my ($user_id, $login_cookie);
my $api_key_text = trim(delete $params->{'Bugzilla_api_key'});
if (!i_am_webservice() || !$api_key_text) {
return { failure => AUTH_NODATA };
}
my $api_key = Bugzilla::User::APIKey->new({ name => $api_key_text });
if (!$api_key or $api_key->api_key ne $api_key_text) {
# The second part checks the correct capitalisation. Silly MySQL
ThrowUserError("api_key_not_valid");
}
elsif ($api_key->revoked) {
ThrowUserError('api_key_revoked');
}
$api_key->update_last_used();
return { user_id => $api_key->user_id };
}
1;
......@@ -14,8 +14,9 @@ use base qw(Bugzilla::Auth::Login);
use fields qw(_login_token);
use Bugzilla::Constants;
use Bugzilla::Util;
use Bugzilla::Error;
use Bugzilla::Token;
use Bugzilla::Util;
use List::Util qw(first);
......@@ -49,6 +50,17 @@ sub get_login_info {
@{$cgi->{'Bugzilla_cookie_list'}};
$user_id = $cookie->value if $cookie;
}
# If the call is for a web service, and an api token is provided, check
# it is valid.
if (i_am_webservice() && Bugzilla->input_params->{Bugzilla_api_token}) {
my $api_token = Bugzilla->input_params->{Bugzilla_api_token};
my ($token_user_id, undef, undef, $token_type)
= Bugzilla::Token::GetTokenData($api_token);
if ($token_type ne 'api_token' || $user_id != $token_user_id) {
ThrowUserError('auth_invalid_token', { token => $api_token });
}
}
}
# If no cookies were provided, we also look for a login token
......
......@@ -1177,7 +1177,7 @@ use constant ABSTRACT_SCHEMA => {
issuedate => {TYPE => 'DATETIME', NOTNULL => 1} ,
token => {TYPE => 'varchar(16)', NOTNULL => 1,
PRIMARYKEY => 1},
tokentype => {TYPE => 'varchar(8)', NOTNULL => 1} ,
tokentype => {TYPE => 'varchar(16)', NOTNULL => 1} ,
eventdata => {TYPE => 'TINYTEXT'},
],
INDEXES => [
......@@ -1733,6 +1733,26 @@ use constant ABSTRACT_SCHEMA => {
bug_user_last_visit_last_visit_ts_idx => ['last_visit_ts'],
],
},
user_api_keys => {
FIELDS => [
id => {TYPE => 'INTSERIAL', NOTNULL => 1,
PRIMARYKEY => 1},
user_id => {TYPE => 'INT3', NOTNULL => 1,
REFERENCES => {TABLE => 'profiles',
COLUMN => 'userid',
DELETE => 'CASCADE'}},
api_key => {TYPE => 'VARCHAR(40)', NOTNULL => 1},
description => {TYPE => 'VARCHAR(255)'},
revoked => {TYPE => 'BOOLEAN', NOTNULL => 1,
DEFAULT => 'FALSE'},
last_used => {TYPE => 'DATETIME'},
],
INDEXES => [
user_api_keys_key => {FIELDS => ['api_key'], TYPE => 'UNIQUE'},
user_api_keys_user_id => {FIELDS => ['user_id']},
],
},
};
# Foreign Keys are added in Bugzilla::DB::bz_add_field_tables
......
......@@ -719,6 +719,10 @@ sub update_table_definitions {
'bug_user_last_visit_last_visit_ts_idx',
['last_visit_ts']);
# 2014-07-14 sgreen@redhat.com - Bug 726696
$dbh->bz_alter_column('tokens', 'tokentype',
{TYPE => 'varchar(16)', NOTNULL => 1});
################################################################
# New --TABLE-- changes should go *** A B O V E *** this point #
################################################################
......
......@@ -1001,6 +1001,12 @@ sub create {
return $cookie ? issue_hash_token(['login_request', $cookie]) : '';
},
'get_api_token' => sub {
return '' unless Bugzilla->user->id;
my $cache = Bugzilla->request_cache;
return $cache->{api_token} //= issue_api_token();
},
# A way for all templates to get at Field data, cached.
'bug_fields' => sub {
my $cache = Bugzilla->request_cache;
......
......@@ -23,13 +23,21 @@ use Digest::SHA qw(hmac_sha256_base64);
use parent qw(Exporter);
@Bugzilla::Token::EXPORT = qw(issue_session_token check_token_data delete_token
@Bugzilla::Token::EXPORT = qw(issue_api_token issue_session_token
check_token_data delete_token
issue_hash_token check_hash_token);
################################################################################
# Public Functions
################################################################################
# Create a token used for internal API authentication
sub issue_api_token {
# Generates a random token, adds it to the tokens table, and returns
# the token to the caller.
return _create_token(Bugzilla->user->id, 'api_token', '');
}
# Creates and sends a token to create a new user account.
# It assumes that the login has the correct format and is not already in use.
sub issue_new_user_account_token {
......@@ -466,6 +474,14 @@ Bugzilla::Token - Provides different routines to manage tokens.
=over
=item C<issue_api_token($login_name)>
Description: Creates a token that can be used for API calls on the web page.
Params: None.
Returns: The token.
=item C<issue_new_user_account_token($login_name)>
Description: Creates and sends a token per email to the email address
......
# 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::User::APIKey;
use 5.10.1;
use strict;
use parent qw(Bugzilla::Object);
use Bugzilla::User;
use Bugzilla::Util qw(generate_random_password trim);
#####################################################################
# Overriden Constants that are used as methods
#####################################################################
use constant DB_TABLE => 'user_api_keys';
use constant DB_COLUMNS => qw(
id
user_id
api_key
description
revoked
last_used
);
use constant UPDATE_COLUMNS => qw(description revoked last_used);
use constant VALIDATORS => {
api_key => \&_check_api_key,
description => \&_check_description,
revoked => \&Bugzilla::Object::check_boolean,
};
use constant LIST_ORDER => 'id';
use constant NAME_FIELD => 'api_key';
# turn off auditing and exclude these objects from memcached
use constant { AUDIT_CREATES => 0,
AUDIT_UPDATES => 0,
AUDIT_REMOVES => 0,
USE_MEMCACHED => 0 };
# Accessors
sub id { return $_[0]->{id} }
sub user_id { return $_[0]->{user_id} }
sub api_key { return $_[0]->{api_key} }
sub description { return $_[0]->{description} }
sub revoked { return $_[0]->{revoked} }
sub last_used { return $_[0]->{last_used} }
# Helpers
sub user {
my $self = shift;
$self->{user} //= Bugzilla::User->new({name => $self->user_id, cache => 1});
return $self->{user};
}
sub update_last_used {
my $self = shift;
my $timestamp = shift
|| Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
$self->set('last_used', $timestamp);
$self->update;
}
# Setters
sub set_description { $_[0]->set('description', $_[1]); }
sub set_revoked { $_[0]->set('revoked', $_[1]); }
# Validators
sub _check_api_key { return generate_random_password(40); }
sub _check_description { return trim($_[1]) || ''; }
1;
__END__
=head1 NAME
Bugzilla::User::APIKey - Model for an api key belonging to a user.
=head1 SYNOPSIS
use Bugzilla::User::APIKey;
my $api_key = Bugzilla::User::APIKey->new($id);
my $api_key = Bugzilla::User::APIKey->new({ name => $api_key });
# Class Functions
$user_api_key = Bugzilla::User::APIKey->create({
description => $description,
});
=head1 DESCRIPTION
This package handles Bugzilla User::APIKey.
C<Bugzilla::User::APIKey> is an implementation of L<Bugzilla::Object>, and
thus provides all the methods of L<Bugzilla::Object> in addition to the methods
listed below.
=head1 METHODS
=head2 Accessor Methods
=over
=item C<id>
The internal id of the api key.
=item C<user>
The Bugzilla::User object that this api key belongs to.
=item C<user_id>
The user id that this api key belongs to.
=item C<api_key>
The API key, which is a random string.
=item C<description>
An optional string that lets the user describe what a key is used for.
For example: "Dashboard key", "Application X key".
=item C<revoked>
If true, this api key cannot be used.
=item C<last_used>
The date that this key was last used. undef if never used.
=item C<update_last_used>
Updates the last used value to the current timestamp. This is updated even
if the RPC call resulted in an error. It is not updated when the description
or the revoked flag is changed.
=item C<set_description>
Sets the new description
=item C<set_revoked>
Sets the revoked flag
=back
......@@ -134,14 +134,22 @@ how this is implemented for those frontends.
=head1 LOGGING IN
There are various ways to log in:
Some methods do not require you to log in. An example of this is Bug.get.
However, authenticating yourself allows you to see non public information. For
example, a bug that is not publicly visible.
There are two ways to authenticate yourself:
=over
=item C<User.login>
=item C<Bugzilla_api_key>
You can use L<Bugzilla::WebService::User/login> to log in as a Bugzilla
user. This issues a token that you must then use in future calls.
B<Added in Bugzilla 5.0>
You can specify C<Bugzilla_api_key> as an argument to any WebService method, and
you will be logged in as that user if the key is correct, and has not been
revoked. You can set up an API key by using the 'API Key' tab in the
Preferences pages.
=item C<Bugzilla_login> and C<Bugzilla_password>
......@@ -164,15 +172,29 @@ then your login will only be valid for your IP address.
=back
The C<Bugzilla_restrictlogin> option is only used when you have also
specified C<Bugzilla_login> and C<Bugzilla_password>.
specified C<Bugzilla_login> and C<Bugzilla_password>. This value will be
deprecated in the release after Bugzilla 5.0 and you will be required to
pass the Bugzilla_login and Bugzilla_password for every call.
For REST, you may also use the C<login> and C<password> variable
names instead of C<Bugzilla_login> and C<Bugzilla_password> as a
convenience. You may also use C<token> instead of C<Bugzilla_token>.
=back
There are also two deprecreated methods of authentications. This will be
removed in the version after Bugzilla 5.0.
=over
=item C<User.login>
You can use L<Bugzilla::WebService::User/login> to log in as a Bugzilla
user. This issues a token that you must then use in future calls.
=item C<Bugzilla_token>
B<Added in Bugzilla 5.0>
B<Added in Bugzilla 4.4.3>
You can specify C<Bugzilla_token> as argument to any WebService method,
and you will be logged in as that user if the token is correct. This is
......
......@@ -142,6 +142,9 @@ use constant WS_ERROR_CODE => {
extern_id_conflict => -303,
auth_failure => 304,
password_current_too_short => 305,
api_key_not_valid => 306,
api_key_revoked => 306,
auth_invalid_token => 307,
# Except, historically, AUTH_NODATA, which is 410.
login_required => 410,
......
......@@ -432,9 +432,13 @@ where applicable.
=head1 Logging In and Out
These method are now deprecated, and will be removed in the release after
Bugzilla 5.0. The correct way of use these REST and RPC calls is noted in
L<Bugzilla::WebService>
=head2 login
B<STABLE>
B<DEPRECATED>
=over
......@@ -499,7 +503,9 @@ creates a login cookie.
=item C<restrict_login> was added in Bugzilla B<5.0>.
=item C<token> was added in Bugzilla B<5.0>.
=item C<token> was added in Bugzilla B<4.4.3>.
=item This function will be removed in the release after Bugzilla 5.0, in favour of API keys.
=back
......@@ -507,7 +513,7 @@ creates a login cookie.
=head2 logout
B<STABLE>
B<DEPRECATED>
=over
......@@ -525,7 +531,7 @@ Log out the user. Does nothing if there is no user logged in.
=head2 valid_login
B<UNSTABLE>
B<DEPRECATED>
=over
......@@ -563,6 +569,8 @@ for the provided username.
=item Added in Bugzilla B<5.0>.
=item This function will be removed in the release after Bugzilla 5.0, in favour of API keys.
=back
=back
......
......@@ -268,6 +268,11 @@ sub fix_credentials {
$params->{'Bugzilla_login'} = $params->{'login'};
$params->{'Bugzilla_password'} = $params->{'password'};
}
# Allow user to pass api_key=12345678 as a convenience which becomes
# "Bugzilla_api_key" which is what the auth code looks for.
if (exists $params->{api_key}) {
$params->{Bugzilla_api_key} = delete $params->{api_key};
}
# Allow user to pass token=12345678 as a convenience which becomes
# "Bugzilla_token" which is what the auth code looks for.
if (exists $params->{'token'}) {
......
......@@ -1005,6 +1005,22 @@ If you attempt to change your email address, a confirmation
email is sent to both the old and new addresses, with a link to use to
confirm the change. This helps to prevent account hijacking.
.. _apikey:
API Keys
========
API keys are used to authenticate REST calls. You can create more than one
API key if required. Each API key has an optional description which can help
you record what each key is used for.
On this page, you can unrevoke, revoke and change the description of existing
API keys for your login. A revoked key means that it cannot be used. The
description for purely for your information, and is optional.
You can also create a new API key by selecting the check box under the 'New
API key' section of the page.
.. _permissionsettings:
Permissions
......
......@@ -22,6 +22,7 @@ YAHOO.bugzilla.dupTable = {
method : "Bug.possible_duplicates",
id : YAHOO.bugzilla.dupTable.counter,
params : {
Bugzilla_api_token: BUGZILLA.api_token,
product : product_name,
summary : summary_field.value,
limit : 7,
......@@ -199,7 +200,10 @@ function set_assign_to(use_qa_contact) {
var args = JSON.stringify({
version: "1.1",
method: 'BugUserLastVisit.update',
params: { ids: bug_id },
params: {
Bugzilla_api_token: BUGZILLA.api_token,
ids: bug_id
},
});
var callbacks = {
failure: function(res) {
......@@ -218,7 +222,9 @@ function set_assign_to(use_qa_contact) {
var args = JSON.stringify({
version: "1.1",
method: 'BugUserLastVisit.get',
params: { },
params: {
Bugzilla_api_token: BUGZILLA.api_token
},
});
var callbacks = {
success: function(res) { done(JSON.parse(res.responseText)) },
......
......@@ -50,7 +50,11 @@ YAHOO.bugzilla.commentTagging = {
return YAHOO.lang.JSON.stringify({
method : "Bug.search_comment_tags",
id : YAHOO.bugzilla.commentTagging.counter,
params : [ { query : query, limit : 10 } ]
params : {
Bugzilla_api_token: BUGZILLA.api_token,
query : query,
limit : 10
}
});
};
ac.minQueryLength = this.min_len;
......@@ -327,6 +331,7 @@ YAHOO.bugzilla.commentTagging = {
version: "1.1",
method: 'Bug.comments',
params: {
Bugzilla_api_token: BUGZILLA.api_token,
comment_ids: [ comment_id ],
include_fields: [ 'tags' ]
}
......@@ -359,6 +364,7 @@ YAHOO.bugzilla.commentTagging = {
version: "1.1",
method: 'Bug.update_comment_tags',
params: {
Bugzilla_api_token: BUGZILLA.api_token,
comment_id: comment_id,
add: add,
remove: remove
......
......@@ -825,6 +825,7 @@ YAHOO.bugzilla.userAutocomplete = {
method : "User.get",
id : YAHOO.bugzilla.userAutocomplete.counter,
params : [ {
Bugzilla_api_token: BUGZILLA.api_token,
match : [ decodeURIComponent(enteredText) ],
include_fields : [ "name", "real_name" ]
} ]
......@@ -1047,6 +1048,7 @@ function show_comment_preview(bug_id) {
version: "1.1",
method: 'Bug.render_comment',
params: {
Bugzilla_api_token: BUGZILLA.api_token,
id: bug_id,
text: comment.value
}
......
[%# 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.
#%]
[%# INTERFACE:
# api_keys: array. Array of api keys this user has.
# any_revoked: boolean. True is any keys have been revoked.
#%]
<p>
API keys are used to authenticate REST calls. You can create more than one
API key if required. Each API key has an optional description which can help
you record what each key is used for. Documentation on how to log in is
available from
<a href="docs/en/html/api/Bugzilla/WebService.html#LOGGING_IN">here</a>.
</p>
<h3>Existing API keys</h3>
<p>You can update the description, and revoke or unrevoke existing API keys
here.</p>
<table id="email_prefs">
<tr class="column_header">
<th>API key</th>
<th>Description (optional)</th>
<th>Last used</th>
<th>Revoked?</th>
</tr>
[% FOREACH api_key IN api_keys %]
<tr[% IF api_key.revoked %] class="apikey_revoked"[% END %]>
<td>[% api_key.api_key FILTER html %]</td>
<td>
<input name="description_[% api_key.id FILTER html %]"
id="description_[% api_key.id FILTER html %]"
value="[% api_key.description FILTER html %]">
</td>
[% IF api_key.last_used %]
<td>[% api_key.last_used FILTER time %]</td>
[% ELSE %]
<td class="center"><i>never used</i></td>
[% END %]
<td class="center">
<input type="checkbox" value="1"
name="revoked_[% api_key.id FILTER html %]"
id="revoked_[% api_key.id FILTER html %]"
[% IF api_key.revoked %] checked="checked" [% END %]>
</td>
</tr>
[% END %]
[% UNLESS api_keys.size %]
<tr><td colspan="4">You don't have any API keys.</td></tr>
[% END %]
</table>
[% IF any_revoked %]
<a id="apikey_revoked_controller" class="bz_default_hidden"
href="javascript:TUI_toggle_class('apikey_revoked')">Hide Revoked Keys</a>
[%# Show the link if the browser supports JS %]
<script type="text/javascript">
TUI_hide_default('apikey_revoked');
TUI_alternates['apikey_revoked'] = 'Show Revoked Keys';
YAHOO.util.Dom.removeClass('apikey_revoked_controller',
'bz_default_hidden');
</script>
[% END %]
<h3>New API key</h3>
<p>You can generate a new API key by ticking the check box below and optionally
providing a description for the API key. The API key will be randomly
generated for you.</p>
<p>
<input type="checkbox" name="new_key" id="new_key">
Generate a new API key with optional description
<input name="new_description" id="new_description">
</p>
......@@ -36,6 +36,9 @@
{ name => "account", label => "Account Information",
link => "userprefs.cgi?tab=account", saveable => "1",
doc_section => "using.html#account-information" },
{ name => "apikey", label => "API Keys",
link => "userprefs.cgi?tab=apikey", saveable => "1",
doc_section => "using.html#apikey" },
{ name => "permissions", label => "Permissions",
link => "userprefs.cgi?tab=permissions", saveable => "0",
doc_section => "using.html#permissions" } ] %]
......@@ -53,7 +56,7 @@
title = current_tab.label
subheader = filtered_login
style_urls = ['skins/standard/admin.css']
javascript_urls = ['js/util.js', 'js/field.js']
javascript_urls = ['js/util.js', 'js/field.js', 'js/TUI.js']
doc_section = current_tab.doc_section
yui = ['autocomplete']
%]
......
[%# 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.
#%]
[%# INTERFACE:
# user: The Bugzilla::User object of the user being created
# new_key: The API key created
#%]
From: [% Param('mailfrom') %]
To: [% user.email %]
Subject: [% terms.Bugzilla %]: New API key created
X-Bugzilla-Type: admin
[This e-mail has been automatically generated]
A new [% terms.Bugzilla %] API key[% IF new_key.description %], with the
description '[% new_key.description %]'[% END %] has been created. You can view
or update the key at the following URL:
[%+ urlbase %]userprefs.cgi?tab=apikey
IMPORTANT: If you did not request a new key, your [% terms.Bugzilla %] account
may have been compromised. In this case, please disable the key at the above
URL, and change your password immediately.
For security reasons, we have not included your new key in this e-mail.
If you have any issues regarding your account, please contact [% Param('maintainer') %].
......@@ -166,6 +166,9 @@
version_required:
'You must select a Version for this [% terms.bug %].'
}
[% IF javascript_urls.containsany(['js/bug.js', 'js/field.js', 'js/comment-tagging.js']) %]
, api_token: '[% get_api_token FILTER js FILTER html %]'
[% END %]
};
[% FOREACH yui_name = yui %]
......
......@@ -97,6 +97,15 @@
[% terms.Bug %] aliases cannot be longer than 20 characters.
Please choose a shorter alias.
[% ELSIF error == "api_key_not_valid" %]
[% title = "Invalid API key" %]
The API key you specified is invalid. Please check that you typed it
correctly.
[% ELSIF error == "api_key_revoked" %]
[% title = "Invalid API key" %]
The API key you specified has been revoked by the user that created it.
[% ELSIF error == "attachment_bug_id_mismatch" %]
[% title = "Invalid Attachments" %]
You tried to perform an action on attachments from different [% terms.bugs %].
......@@ -219,6 +228,11 @@
[% Hook.process("auth_failure") %]
[% ELSIF error == "auth_invalid_token" %]
[% title = 'A token error occurred' %]
The token '[% token FILTER html %]' is not valid. It could be because
you loaded this page more than 3 days ago.
[% ELSIF error == "auth_untrusted_request" %]
[% title = "Untrusted Authentication Request" %]
You tried to log in using the <em>[% login FILTER html %]</em> account,
......
......@@ -13,10 +13,12 @@ use lib qw(. lib);
use Bugzilla;
use Bugzilla::BugMail;
use Bugzilla::Constants;
use Bugzilla::Mailer;
use Bugzilla::Search;
use Bugzilla::Util;
use Bugzilla::Error;
use Bugzilla::User;
use Bugzilla::User::APIKey;
use Bugzilla::Token;
my $template = Bugzilla->template;
......@@ -501,6 +503,65 @@ sub SaveSavedSearches {
}
sub DoApiKey {
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;
}
sub SaveApiKey {
my $cgi = Bugzilla->cgi;
my $dbh = Bugzilla->dbh;
my $user = Bugzilla->user;
# 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)
{
$api_key->set_all({
description => $description,
revoked => $revoked,
});
$api_key->update();
}
}
# Was a new api key requested
if ($cgi->param('new_key')) {
my $new_key = Bugzilla::User::APIKey->create({
user_id => $user->id,
description => $cgi->param('new_description'),
});
# As a security precaution, we always sent out an e-mail when
# an API key is created
my $lang = $user->setting('lang')
// Bugzilla::User->new()->setting('lang');
my $template = Bugzilla->template_inner($lang);
my $message;
$template->process(
'email/new-api-key.txt.tmpl',
{ user => $user, new_key => $new_key },
\$message
) || ThrowTemplateError($template->error());
MessageToMTA($message);
}
$dbh->bz_commit_transaction;
}
###############################################################################
# Live code (not subroutine definitions) starts here
###############################################################################
......@@ -570,6 +631,11 @@ SWITCH: for ($current_tab_name) {
DoSavedSearches();
last SWITCH;
};
/^apikey$/ && do {
SaveApiKey() if $save_changes;
DoApiKey();
last SWITCH;
};
ThrowUserError("unknown_tab",
{ current_tab_name => $current_tab_name });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment