diff options
author | Dylan William Hardison <dylan@hardison.net> | 2015-03-04 04:27:15 +0100 |
---|---|---|
committer | Dylan William Hardison <dylan@hardison.net> | 2015-04-03 00:21:08 +0200 |
commit | c0d00a57eebb31d3e2cdef0ebb9219ebe2fe2bab (patch) | |
tree | e6c6eb07c7348e086f1ed6de54de829e806dda05 /extensions/GitHubAuth/lib/Login.pm | |
parent | 1317be9bb70dbb945fa82d0d1bd8548c83c86ebe (diff) | |
download | bugzilla-c0d00a57eebb31d3e2cdef0ebb9219ebe2fe2bab.tar.gz bugzilla-c0d00a57eebb31d3e2cdef0ebb9219ebe2fe2bab.tar.xz |
Github Auth Extension
Diffstat (limited to 'extensions/GitHubAuth/lib/Login.pm')
-rw-r--r-- | extensions/GitHubAuth/lib/Login.pm | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/extensions/GitHubAuth/lib/Login.pm b/extensions/GitHubAuth/lib/Login.pm new file mode 100644 index 000000000..c83456274 --- /dev/null +++ b/extensions/GitHubAuth/lib/Login.pm @@ -0,0 +1,173 @@ +# 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::Extension::GitHubAuth::Login; +use strict; +use warnings; +use base qw(Bugzilla::Auth::Login); +use fields qw(github_failure); + +use Scalar::Util qw(blessed); + +use Bugzilla::Constants qw(AUTH_NODATA AUTH_ERROR USAGE_MODE_BROWSER ); +use Bugzilla::Util qw(trick_taint correct_urlbase); +use Bugzilla::Extension::GitHubAuth::Client; +use Bugzilla::Extension::GitHubAuth::Client::Error (); +use Bugzilla::Extension::GitHubAuth::Util qw(target_uri); +use Bugzilla::Error; + +use constant { requires_verification => 1, + is_automatic => 1, + user_can_create_account => 1 }; + +sub get_login_info { + my ($self) = @_; + my $cgi = Bugzilla->cgi; + my $github_login = $cgi->param('github_login'); + my $github_email = $cgi->param('github_email'); + my $github_email_key = $cgi->param('github_email_key'); + + return { failure => AUTH_NODATA } unless $github_login; + + if ($github_email_key && $github_email) { + trick_taint($github_email); + trick_taint($github_email_key); + return $self->_get_login_info_from_email($github_email, $github_email_key); + } + else { + return $self->_get_login_info_from_github(); + } +} + +sub _get_login_info_from_github { + my ($self) = @_; + my $cgi = Bugzilla->cgi; + my $template = Bugzilla->template; + my $state = $cgi->param('state'); + my $code = $cgi->param('code'); + + return { failure => AUTH_ERROR, error => 'github_missing_code' } unless $code; + return { failure => AUTH_ERROR, error => 'github_invalid_state' } unless $state; + + trick_taint($code); + trick_taint($state); + + my $target = target_uri(); + my $client = Bugzilla::Extension::GitHubAuth::Client->new; + if ($state ne $client->get_state($target)) { + return { failure => AUTH_ERROR, error => 'github_invalid_state' }; + } + + my ($access_token, $emails); + eval { + # The following variable lets us catch and return (rather than throw) errors + # from our github client code, as required by the Auth API. + local $Bugzilla::Extension::GitHubAuth::Client::Error::USE_EXCEPTION_OBJECTS = 1; + $access_token = $client->get_access_token($code); + $emails = $client->get_user_emails($access_token); + }; + my $e = $@; + if (blessed $e && $e->isa('Bugzilla::Extension::GitHubAuth::Client::Error')) { + my $key = $e->type eq 'user' ? 'user_error' : 'error'; + return { failure => AUTH_ERROR, $key => $e->error, details => $e->vars }; + } + elsif ($e) { + die $e; + } + + my @emails = map { $_->{email} } grep { $_->{verified} } @$emails; + + my $choose_email = sub { + my ($email) = @_; + my $uri = $target->clone; + my $key = Bugzilla::Extension::GitHubAuth::Client->get_email_key($email); + $uri->query_param(github_email => $email); + $uri->query_param(github_email_key => $key); + return $uri; + }; + + my @bugzilla_users; + my @github_emails; + foreach my $email (@emails) { + my $user = Bugzilla::User->new({name => $email, cache => 1}); + if ($user) { + push @bugzilla_users, $user; + } + else { + push @github_emails, $email; + } + } + my @allowed_bugzilla_users = grep { not $_->in_group('no-github-auth') } @bugzilla_users; + + if (@allowed_bugzilla_users == 1) { + my ($user) = @allowed_bugzilla_users; + return { username => $user->login, user_id => $user->id, github_auth => 1 }; + } + elsif (@allowed_bugzilla_users > 1) { + $self->{github_failure} = { + template => 'account/auth/github-verify-account.html.tmpl', + vars => { + bugzilla_users => \@allowed_bugzilla_users, + choose_email => $choose_email, + }, + }; + return { failure => AUTH_NODATA }; + } + elsif (@allowed_bugzilla_users == 0 && @bugzilla_users > 0 && @github_emails == 0) { + return { failure => AUTH_ERROR, + user_error => 'github_auth_account_too_powerful' }; + } + elsif (@github_emails) { + $self->{github_failure} = { + template => 'account/auth/github-verify-account.html.tmpl', + vars => { + github_emails => \@github_emails, + choose_email => $choose_email, + }, + }; + return { failure => AUTH_NODATA }; + } + else { + return { failure => AUTH_ERROR, user_error => 'github_no_emails' }; + } +} + +sub _get_login_info_from_email { + my ($self, $github_email, $github_email_key) = @_; + my $cgi = Bugzilla->cgi; + + my $key = Bugzilla::Extension::GitHubAuth::Client->get_email_key($github_email); + unless ($github_email_key eq $key) { + return { failure => AUTH_ERROR, + user_error => 'github_invalid_email', + { email => $github_email }}; + } + + my $user = Bugzilla::User->new({name => $github_email, cache => 1}); + return { failure => AUTH_ERROR, + user_error => 'github_auth_account_too_powerful' } if $user && $user->in_group('no-github-auth'); + + return { username => $github_email, github_auth => 1 }; +} + +sub fail_nodata { + my ($self) = @_; + my $cgi = Bugzilla->cgi; + my $template = Bugzilla->template; + + ThrowUserError('login_required') if Bugzilla->usage_mode != USAGE_MODE_BROWSER; + + my $file = $self->{github_failure}{template} // "account/auth/login.html.tmpl"; + my $vars = $self->{github_failure}{vars} // { target => $cgi->url(-relative=>1) }; + + print $cgi->header(); + $template->process($file, $vars) or ThrowTemplateError($template->error()); + exit; +} + + +1; |