From: Dylan William Hardison Date: Thu, 18 Apr 2019 14:26:08 +0000 (-0400) Subject: Bug 1541555 - Add facility for requiring an API Key to always come from the same... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1d7edc53aebdb84bace923f02f86a75bf43cb0fa;p=thirdparty%2Fbugzilla.git Bug 1541555 - Add facility for requiring an API Key to always come from the same IP address --- diff --git a/Bugzilla/Auth/Login/APIKey.pm b/Bugzilla/Auth/Login/APIKey.pm index 43032c584..f6d2dea38 100644 --- a/Bugzilla/Auth/Login/APIKey.pm +++ b/Bugzilla/Auth/Login/APIKey.pm @@ -47,18 +47,25 @@ sub get_login_info { return {failure => AUTH_NODATA}; } - my $api_key = Bugzilla::User::APIKey->new({name => $api_key_text}); + my $api_key = Bugzilla::User::APIKey->new({name => $api_key_text}); + my $remote_ip = remote_ip(); 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->sticky + && $api_key->last_used_ip + && $api_key->last_used_ip ne $remote_ip) + { + ThrowUserError("api_key_not_valid"); + } elsif ($api_key->revoked) { ThrowUserError('api_key_revoked'); } - $api_key->update_last_used(); + $api_key->update_last_used($remote_ip); $self->set_app_id($api_key->app_id); return {user_id => $api_key->user_id}; diff --git a/Bugzilla/User/APIKey.pm b/Bugzilla/User/APIKey.pm index a30c6718f..86f7ba2db 100644 --- a/Bugzilla/User/APIKey.pm +++ b/Bugzilla/User/APIKey.pm @@ -14,7 +14,7 @@ use warnings; use base qw(Bugzilla::Object); use Bugzilla::User; -use Bugzilla::Util qw(generate_random_password trim remote_ip); +use Bugzilla::Util qw(generate_random_password trim); use Bugzilla::Error; ##################################################################### @@ -31,14 +31,16 @@ use constant DB_COLUMNS => qw( revoked last_used last_used_ip + sticky ); -use constant UPDATE_COLUMNS => qw(description revoked last_used last_used_ip); +use constant UPDATE_COLUMNS => qw(description revoked last_used last_used_ip sticky); use constant VALIDATORS => { api_key => \&_check_api_key, app_id => \&_check_app_id, description => \&_check_description, revoked => \&Bugzilla::Object::check_boolean, + sticky => \&Bugzilla::Object::check_boolean, }; use constant LIST_ORDER => 'id'; use constant NAME_FIELD => 'api_key'; @@ -60,6 +62,7 @@ sub description { return $_[0]->{description} } sub revoked { return $_[0]->{revoked} } sub last_used { return $_[0]->{last_used} } sub last_used_ip { return $_[0]->{last_used_ip} } +sub sticky { return $_[0]->{sticky} } # Helpers sub user { @@ -69,17 +72,17 @@ sub user { } sub update_last_used { - my $self = shift; - my $timestamp - = shift || Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); + my ($self, $remote_ip) = @_; + my $timestamp = Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); $self->set('last_used', $timestamp); - $self->set('last_used_ip', remote_ip()); + $self->set('last_used_ip', $remote_ip); $self->update; } # Setters sub set_description { $_[0]->set('description', $_[1]); } sub set_revoked { $_[0]->set('revoked', $_[1]); } +sub set_sticky { $_[0]->set('sticky', $_[1]); } # Validators sub _check_api_key { return generate_random_password(40); } diff --git a/docs/en/rst/using/preferences.rst b/docs/en/rst/using/preferences.rst index d61dc4f8a..cfce75d6d 100644 --- a/docs/en/rst/using/preferences.rst +++ b/docs/en/rst/using/preferences.rst @@ -132,10 +132,15 @@ change your password everywhere. You can create more than one API key if required. Each API key has an optional description which can help you record what it is used for. -On this page, you can unrevoke, revoke and change the description of existing +On this page, you can unrevoke, revoke, make sticky, and change the description of existing API keys for your login. A revoked key means that it cannot be used. The description is optional and purely for your information. +Sticky API keys may only be used from one IP address, which reduces the risk +of the key being leaked. The IP address is the one the key was last used +from. The expected workflow is that the sticky bit will be set once your application +(or script) is setup. The sticky attribute may only be set, it can't ever be unset. + You can also create a new API key by selecting the checkbox under the 'New API key' section of the page. diff --git a/template/en/default/account/prefs/apikey.html.tmpl b/template/en/default/account/prefs/apikey.html.tmpl index 19cec468f..3a27b0a9c 100644 --- a/template/en/default/account/prefs/apikey.html.tmpl +++ b/template/en/default/account/prefs/apikey.html.tmpl @@ -14,8 +14,9 @@

API keys are used to authenticate WebService API 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.
-
+ you record what each key is used for. +

+

Documentation on how to log in is available here. @@ -24,13 +25,14 @@

Existing API keys

You can update the description, and revoke or unrevoke existing API keys -here.

+here. Sticky keys may only be used from the last IP that used the API key, and cannot be unset.

+ @@ -51,6 +53,12 @@ here.

[% ELSE %] [% END %] +
API key Description (optional) Last usedSticky Revoked
never used + + param('description_' . $api_key->id); my $revoked = !!$cgi->param('revoked_' . $api_key->id); + my $sticky = !!$cgi->param('sticky_' . $api_key->id); - if ($description ne $api_key->description || $revoked != $api_key->revoked) { + if ( $description ne $api_key->description + || $revoked != $api_key->revoked + || $sticky != $api_key->sticky) + { if ($user->mfa && !$revoked && $api_key->revoked) { push @mfa_events, { @@ -862,7 +866,9 @@ sub SaveApiKey { }; } else { - $api_key->set_all({description => $description, revoked => $revoked,}); + $sticky = 1 if $api_key->sticky; + $api_key->set_all( + {description => $description, revoked => $revoked, sticky => $sticky}); $api_key->update(); if ($revoked) { Bugzilla->log_user_request(undef, undef, 'api-key-revoke');