diff options
-rw-r--r-- | Bugzilla/Config/Auth.pm | 5 | ||||
-rw-r--r-- | Bugzilla/Token.pm | 50 | ||||
-rwxr-xr-x | auth.cgi | 88 | ||||
-rw-r--r-- | template/en/default/account/auth/delegation.html.tmpl | 37 | ||||
-rw-r--r-- | template/en/default/admin/params/auth.html.tmpl | 4 | ||||
-rw-r--r-- | template/en/default/global/user-error.html.tmpl | 23 |
6 files changed, 207 insertions, 0 deletions
diff --git a/Bugzilla/Config/Auth.pm b/Bugzilla/Config/Auth.pm index f87126713..217805bea 100644 --- a/Bugzilla/Config/Auth.pm +++ b/Bugzilla/Config/Auth.pm @@ -143,6 +143,11 @@ sub get_param_list { type => 'b', default => '1' }, + { + name => 'auth_delegation', + type => 'b', + default => 0, + }, ); return @param_list; } diff --git a/Bugzilla/Token.pm b/Bugzilla/Token.pm index 750c36435..7edcd4226 100644 --- a/Bugzilla/Token.pm +++ b/Bugzilla/Token.pm @@ -40,10 +40,12 @@ use Date::Format; use Date::Parse; use File::Basename; use Digest::MD5 qw(md5_hex); +use Digest::SHA qw(hmac_sha256_base64); use base qw(Exporter); @Bugzilla::Token::EXPORT = qw(issue_api_token issue_session_token + issue_auth_delegation_token check_auth_delegation_token check_token_data delete_token issue_hash_token check_hash_token); @@ -65,6 +67,37 @@ sub issue_api_token { return $token // _create_token($user->id, 'api_token', ''); } +sub issue_auth_delegation_token { + my ($uri) = @_; + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + my $checksum = hmac_sha256_base64($user->id, $uri, Bugzilla->localconfig->{'site_wide_secret'}); + + return _create_token($user->id, 'auth_delegation', $checksum); +} + +sub check_auth_delegation_token { + my ($token, $uri) = @_; + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + + my ($eventdata) = $dbh->selectrow_array(" + SELECT eventdata FROM tokens + WHERE token = ? AND tokentype = 'auth_delegation' + AND (" . $dbh->sql_date_math('issuedate', '+', (MAX_TOKEN_AGE * 24 - 12), 'HOUR') . ") > NOW()", + undef, $token); + + if ($eventdata) { + my $checksum = hmac_sha256_base64($user->id, $uri, Bugzilla->localconfig->{'site_wide_secret'}); + if ($eventdata eq $checksum) { + delete_token($token); + return 1; + } + } + + return 0; +} + # 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 { @@ -628,6 +661,23 @@ although they can be used separately. Returns: A unique token. +=item C<issue_auth_delegation_token($uri)> + + Description: Creates and returns a token used to validate auth delegation confirmations. + + Params: $uri - The uri that auth will be delegated to. + + Returns: A unique token. + +=item C<check_auth_delegation_token($token, $uri)> + + Description: Checks if a token $token is a confirmation token for $uri. + + Params: $token - The token returned by issue_auth_delegation_token() + $uri - The uri that auth will be delegated to. + + Returns: a boolean value + =item C<check_token_data($token, $event)> Description: Makes sure the $token has been created by the currently logged in diff --git a/auth.cgi b/auth.cgi new file mode 100755 index 000000000..ad9017048 --- /dev/null +++ b/auth.cgi @@ -0,0 +1,88 @@ +#!/usr/bin/perl -wT +# 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. + +use 5.10.1; +use strict; +use warnings; + +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Hook; +use Bugzilla::Util qw(trick_taint); +use Bugzilla::Token qw(issue_auth_delegation_token check_auth_delegation_token); +use Bugzilla::Mailer qw(MessageToMTA); + +use URI; +use URI::QueryParam; + +Bugzilla->login(LOGIN_REQUIRED); + +ThrowUserError('auth_delegation_disabled') unless Bugzilla->params->{auth_delegation}; + +my $cgi = Bugzilla->cgi; +my $template = Bugzilla->template; +my $user = Bugzilla->user; +my $callback = $cgi->param('callback') or ThrowUserError("auth_delegation_missing_callback"); +my $description = $cgi->param('description') or ThrowUserError("auth_delegation_missing_description"); + +trick_taint($callback); +trick_taint($description); + +my $callback_uri = URI->new($callback); +my $callback_base = $callback_uri->clone; +$callback_base->query(undef); + +my $skip_confirmation = 0; +my %args = ( skip_confirmation => \$skip_confirmation, + callback => $callback_uri, + description => $description, + callback_base => $callback_base ); + +Bugzilla::Hook::process('auth_delegation_confirm', \%args); + +my $confirmed = lc($cgi->request_method) eq 'post' && $cgi->param('confirm'); + +if ($confirmed || $skip_confirmation) { + my $token = $cgi->param('token'); + unless ($skip_confirmation) { + ThrowUserError("auth_delegation_missing_token") unless $token; + trick_taint($token); + + unless (check_auth_delegation_token($token, $callback)) { + ThrowUserError('auth_delegation_invalid_token', + { token => $token, callback => $callback }); + } + } + + my $new_key = Bugzilla::User::APIKey->create({ + user_id => $user->id, + description => $description, + }); + my $template = Bugzilla->template_inner($user->setting('lang')); + my $vars = { user => $user, new_key => $new_key }; + my $message; + $template->process('email/new-api-key.txt.tmpl', $vars, \$message) + or ThrowTemplateError($template->error()); + + MessageToMTA($message); + + $callback_uri->query_param(client_api_key => $new_key->api_key); + $callback_uri->query_param(client_api_login => $user->login); + + print $cgi->redirect($callback_uri); +} +else { + $args{token} = issue_auth_delegation_token($callback); + + print $cgi->header(); + $template->process("account/auth/delegation.html.tmpl", \%args) + or ThrowTemplateError($template->error()); +} diff --git a/template/en/default/account/auth/delegation.html.tmpl b/template/en/default/account/auth/delegation.html.tmpl new file mode 100644 index 000000000..2afdf1dc7 --- /dev/null +++ b/template/en/default/account/auth/delegation.html.tmpl @@ -0,0 +1,37 @@ +[%# 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. + #%] + +[% PROCESS global/header.html.tmpl + title = "Auth Delegation Request" %] + +<h1>[% title FILTER html %] </h1> +<p> + A third-party website (<a href="[% callback_base FILTER html %]">[% callback_base FILTER html %]</a>) + would like to have <strong>complete</strong> access to your [% terms.Bugzilla %] account. +</p> + +<p>The description of the site reads: + <blockquote> + [% description FILTER html %] + </blockquote> +</p> + +<p>Do you want this website to have <strong>complete</strong> access to your [% terms.Bugzilla %] + account?</p> + +<div> + <form action="auth.cgi" method="post"> + <input type="hidden" name="confirm" value="1"> + <input type="hidden" name="callback" value="[% callback FILTER html %]"> + <input type="hidden" name="description" value="[% description FILTER html %]"> + <input type="hidden" name="token" value="[% token FILTER html %]"> + <input type="submit" name="submit" value="Accept"> + </form> +</div> + +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/admin/params/auth.html.tmpl b/template/en/default/admin/params/auth.html.tmpl index 85d707706..fea4239b3 100644 --- a/template/en/default/admin/params/auth.html.tmpl +++ b/template/en/default/admin/params/auth.html.tmpl @@ -150,4 +150,8 @@ "complexity rules and minimum length requirements when the user logs " _ "into the $terms.Bugzilla web interface. If it doesn't, the user would " _ "not be able to log in, and recieve a message to reset their password." + + auth_delegation => + "If set, $terms.Bugzilla will allow third party applications " _ + "to request API keys for users." %] diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index 5b2f50116..8c7958649 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -129,6 +129,29 @@ account creation. Please contact an administrator to get a new account created. + [% ELSIF error == "auth_delegation_disabled" %] + [% title = "Can't use auth delegation" %] + This site does not have auth delegation enabled. + Please contact an administrator if you require this functionality. + + [% ELSIF error == "auth_delegation_missing_callback" %] + [% title = "Auth delegation impossible without callback URI" %] + It looks like auth delegation was attempted, but no callback URI was passed. + You were sent here by some other site; please contact them for support. + + [% ELSIF error == "auth_delegation_missing_description" %] + [% title = "Auth delegation impossible without description" %] + It looks like auth delegation was attempted, but no description was passed. + You were sent here by some other site; please contact them for support. + + [% ELSIF error == "auth_delegation_missing_token" %] + [% title = "Auth delegation can't be confirmed" %] + Auth delegation cannot be confirmed due to missing or invalid token. + + [% ELSIF error == "auth_delegation_invalid_token" %] + [% title = "Auth delegation can't be confirmed" %] + Auth delegation cannot be confirmed due to missing or invalid token. + [% ELSIF error == "auth_failure" %] [% title = "Authorization Required" %] [% admindocslinks = {'groups.html' => 'Group Security'} %] |