diff options
Diffstat (limited to 'extensions/GitHubAuth/lib')
-rw-r--r-- | extensions/GitHubAuth/lib/Client.pm | 134 | ||||
-rw-r--r-- | extensions/GitHubAuth/lib/Client/Error.pm | 42 | ||||
-rw-r--r-- | extensions/GitHubAuth/lib/Config.pm | 24 | ||||
-rw-r--r-- | extensions/GitHubAuth/lib/Login.pm | 329 | ||||
-rw-r--r-- | extensions/GitHubAuth/lib/Verify.pm | 6 |
5 files changed, 275 insertions, 260 deletions
diff --git a/extensions/GitHubAuth/lib/Client.pm b/extensions/GitHubAuth/lib/Client.pm index 291501961..328bab48f 100644 --- a/extensions/GitHubAuth/lib/Client.pm +++ b/extensions/GitHubAuth/lib/Client.pm @@ -16,7 +16,8 @@ use URI; use URI::QueryParam; use Digest; -use Bugzilla::Extension::GitHubAuth::Client::Error qw(ThrowUserError ThrowCodeError); +use Bugzilla::Extension::GitHubAuth::Client::Error + qw(ThrowUserError ThrowCodeError); use Bugzilla::Util qw(remote_ip); use constant DIGEST_HASH => 'SHA1'; @@ -24,107 +25,108 @@ use constant DIGEST_HASH => 'SHA1'; use fields qw(user_agent); use constant { - GH_ACCESS_TOKEN_URI => 'https://github.com/login/oauth/access_token', - GH_AUTHORIZE_URI => 'https://github.com/login/oauth/authorize', - GH_USER_EMAILS_URI => 'https://api.github.com/user/emails', + GH_ACCESS_TOKEN_URI => 'https://github.com/login/oauth/access_token', + GH_AUTHORIZE_URI => 'https://github.com/login/oauth/authorize', + GH_USER_EMAILS_URI => 'https://api.github.com/user/emails', }; sub new { - my ($class, %init) = @_; - my $self = $class->fields::new(); + my ($class, %init) = @_; + my $self = $class->fields::new(); - return $self; + return $self; } sub login_uri { - my ($class, $target_uri) = @_; + my ($class, $target_uri) = @_; - my $uri = URI->new(Bugzilla->localconfig->{urlbase} . "github.cgi"); - $uri->query_form(target_uri => $target_uri); - return $uri; + my $uri = URI->new(Bugzilla->localconfig->{urlbase} . "github.cgi"); + $uri->query_form(target_uri => $target_uri); + return $uri; } sub authorize_uri { - my ($class, $state) = @_; + my ($class, $state) = @_; - my $uri = URI->new(GH_AUTHORIZE_URI); - $uri->query_form( - client_id => Bugzilla->params->{github_client_id}, - scope => 'user:email', - state => $state, - redirect_uri => Bugzilla->localconfig->{urlbase} . "github.cgi", - ); + my $uri = URI->new(GH_AUTHORIZE_URI); + $uri->query_form( + client_id => Bugzilla->params->{github_client_id}, + scope => 'user:email', + state => $state, + redirect_uri => Bugzilla->localconfig->{urlbase} . "github.cgi", + ); - return $uri; + return $uri; } sub get_email_key { - my ($class, $email) = @_; - - my $cgi = Bugzilla->cgi; - my $digest = Digest->new(DIGEST_HASH); - $digest->add($email); - $digest->add(remote_ip()); - $digest->add($cgi->cookie('Bugzilla_github_token') // Bugzilla->request_cache->{github_token} // ''); - $digest->add(Bugzilla->localconfig->{site_wide_secret}); - return $digest->hexdigest; + my ($class, $email) = @_; + + my $cgi = Bugzilla->cgi; + my $digest = Digest->new(DIGEST_HASH); + $digest->add($email); + $digest->add(remote_ip()); + $digest->add($cgi->cookie('Bugzilla_github_token') + // Bugzilla->request_cache->{github_token} // ''); + $digest->add(Bugzilla->localconfig->{site_wide_secret}); + return $digest->hexdigest; } sub _handle_response { - my ($self, $response) = @_; - my $data = eval { - decode_json($response->content); - }; - if ($@) { - ThrowCodeError("github_bad_response", { message => "Unable to parse json response" }); - } - - unless ($response->is_success) { - ThrowCodeError("github_error", { response => $response }); - } - return $data; + my ($self, $response) = @_; + my $data = eval { decode_json($response->content); }; + if ($@) { + ThrowCodeError("github_bad_response", + {message => "Unable to parse json response"}); + } + + unless ($response->is_success) { + ThrowCodeError("github_error", {response => $response}); + } + return $data; } sub get_access_token { - my ($self, $code) = @_; - - my $response = $self->user_agent->post( - GH_ACCESS_TOKEN_URI, - { client_id => Bugzilla->params->{github_client_id}, - client_secret => Bugzilla->params->{github_client_secret}, - code => $code }, - Accept => 'application/json', - ); - my $data = $self->_handle_response($response); - return $data->{access_token} if exists $data->{access_token}; + my ($self, $code) = @_; + + my $response = $self->user_agent->post(GH_ACCESS_TOKEN_URI, + { + client_id => Bugzilla->params->{github_client_id}, + client_secret => Bugzilla->params->{github_client_secret}, + code => $code + }, + Accept => 'application/json', + ); + my $data = $self->_handle_response($response); + return $data->{access_token} if exists $data->{access_token}; } sub get_user_emails { - my ($self, $access_token) = @_; - my $uri = URI->new(GH_USER_EMAILS_URI); - $uri->query_form(access_token => $access_token); + my ($self, $access_token) = @_; + my $uri = URI->new(GH_USER_EMAILS_URI); + $uri->query_form(access_token => $access_token); - my $response = $self->user_agent->get($uri, Accept => 'application/json'); + my $response = $self->user_agent->get($uri, Accept => 'application/json'); - return $self->_handle_response($response); + return $self->_handle_response($response); } sub user_agent { - my ($self) = @_; - $self->{user_agent} //= $self->_build_user_agent; + my ($self) = @_; + $self->{user_agent} //= $self->_build_user_agent; - return $self->{user_agent}; + return $self->{user_agent}; } sub _build_user_agent { - my ($self) = @_; - my $ua = LWP::UserAgent->new( timeout => 10 ); + my ($self) = @_; + my $ua = LWP::UserAgent->new(timeout => 10); - if (Bugzilla->params->{proxy_url}) { - $ua->proxy('https', Bugzilla->params->{proxy_url}); - } + if (Bugzilla->params->{proxy_url}) { + $ua->proxy('https', Bugzilla->params->{proxy_url}); + } - return $ua; + return $ua; } 1; diff --git a/extensions/GitHubAuth/lib/Client/Error.pm b/extensions/GitHubAuth/lib/Client/Error.pm index adb6ec07b..00e8415d1 100644 --- a/extensions/GitHubAuth/lib/Client/Error.pm +++ b/extensions/GitHubAuth/lib/Client/Error.pm @@ -16,39 +16,39 @@ use Bugzilla::Error (); use base qw(Exporter); use fields qw(type error vars); -our @EXPORT = qw(ThrowUserError ThrowCodeError); +our @EXPORT = qw(ThrowUserError ThrowCodeError); our $USE_EXCEPTION_OBJECTS = 0; sub _new { - my ($class, $type, $error, $vars) = @_; - my $self = $class->fields::new(); - $self->{type} = $type; - $self->{error} = $error; - $self->{vars} = $vars // {}; + my ($class, $type, $error, $vars) = @_; + my $self = $class->fields::new(); + $self->{type} = $type; + $self->{error} = $error; + $self->{vars} = $vars // {}; - return $self; + return $self; } -sub type { $_[0]->{type} } +sub type { $_[0]->{type} } sub error { $_[0]->{error} } -sub vars { $_[0]->{vars} } +sub vars { $_[0]->{vars} } sub ThrowUserError { - if ($USE_EXCEPTION_OBJECTS) { - die __PACKAGE__->_new('user', @_); - } - else { - Bugzilla::Error::ThrowUserError(@_); - } + if ($USE_EXCEPTION_OBJECTS) { + die __PACKAGE__->_new('user', @_); + } + else { + Bugzilla::Error::ThrowUserError(@_); + } } sub ThrowCodeError { - if ($USE_EXCEPTION_OBJECTS) { - die __PACKAGE__->_new('code', @_); - } - else { - Bugzilla::Error::ThrowCodeError(@_); - } + if ($USE_EXCEPTION_OBJECTS) { + die __PACKAGE__->_new('code', @_); + } + else { + Bugzilla::Error::ThrowCodeError(@_); + } } 1; diff --git a/extensions/GitHubAuth/lib/Config.pm b/extensions/GitHubAuth/lib/Config.pm index 0c8874129..b718b4be5 100644 --- a/extensions/GitHubAuth/lib/Config.pm +++ b/extensions/GitHubAuth/lib/Config.pm @@ -16,22 +16,14 @@ use Bugzilla::Config::Common; our $sortkey = 1350; sub get_param_list { - my ($class) = @_; - - my @params = ( - { - name => 'github_client_id', - type => 't', - default => '', - }, - { - name => 'github_client_secret', - type => 't', - default => '', - }, - ); - - return @params; + my ($class) = @_; + + my @params = ( + {name => 'github_client_id', type => 't', default => '',}, + {name => 'github_client_secret', type => 't', default => '',}, + ); + + return @params; } 1; diff --git a/extensions/GitHubAuth/lib/Login.pm b/extensions/GitHubAuth/lib/Login.pm index 073fbfeea..df3195bc7 100644 --- a/extensions/GitHubAuth/lib/Login.pm +++ b/extensions/GitHubAuth/lib/Login.pm @@ -24,187 +24,208 @@ use Bugzilla::Extension::GitHubAuth::Client; use Bugzilla::Extension::GitHubAuth::Client::Error (); use Bugzilla::Error; -use constant { requires_verification => 1, - is_automatic => 1, - user_can_create_account => 1 }; +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_action = Bugzilla->request_cache->{github_action}; - - return { failure => AUTH_NODATA } unless $github_action; - - my $response; - if ($github_action eq 'login') { - $response = $self->_get_login_info_from_github(); - } - elsif ($github_action eq 'email') { - $response = $self->_get_login_info_from_email(); - } - - if (!exists $response->{failure}) { - if (exists $response->{user}) { - # existing account - my $user = $response->{user}; - return { failure => AUTH_ERROR, - user_error => 'github_auth_account_too_powerful' } if $user->in_group('no-github-auth'); - return { failure => AUTH_ERROR, - user_error => 'mfa_prevents_login', - details => { provider => 'GitHub' } } if $user->mfa; - $response = { - username => $user->login, - user_id => $user->id, - github_auth => 1, - }; + my ($self) = @_; + my $cgi = Bugzilla->cgi; + my $github_action = Bugzilla->request_cache->{github_action}; + + return {failure => AUTH_NODATA} unless $github_action; + + my $response; + if ($github_action eq 'login') { + $response = $self->_get_login_info_from_github(); + } + elsif ($github_action eq 'email') { + $response = $self->_get_login_info_from_email(); + } + + if (!exists $response->{failure}) { + if (exists $response->{user}) { + + # existing account + my $user = $response->{user}; + return { + failure => AUTH_ERROR, + user_error => 'github_auth_account_too_powerful' } - else { - # new account - my $email = $response->{email}; - $response = { - username => $email, - github_auth => 1, - }; + if $user->in_group('no-github-auth'); + return { + failure => AUTH_ERROR, + user_error => 'mfa_prevents_login', + details => {provider => 'GitHub'} } + if $user->mfa; + $response = {username => $user->login, user_id => $user->id, github_auth => 1,}; } - return $response; + else { + # new account + my $email = $response->{email}; + $response = {username => $email, github_auth => 1,}; + } + } + return $response; } sub _get_login_info_from_github { - my ($self) = @_; - my $cgi = Bugzilla->cgi; - my $template = Bugzilla->template; - my $code = $cgi->param('code'); - - return { failure => AUTH_ERROR, error => 'github_missing_code' } unless $code; - - trick_taint($code); - - my $client = Bugzilla::Extension::GitHubAuth::Client->new; - - 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} && $_->{email} !~ /\@users\.noreply\.github\.com$/ } @$emails; - - 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 { user => $user }; - } - elsif (@allowed_bugzilla_users > 1) { - $self->{github_failure} = { - template => 'account/auth/github-verify-account.html.tmpl', - vars => { - bugzilla_users => \@allowed_bugzilla_users, - choose_email => _mk_choose_email(\@emails), - }, - }; - 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 => _mk_choose_email(\@emails), - }, - }; - return { failure => AUTH_NODATA }; + my ($self) = @_; + my $cgi = Bugzilla->cgi; + my $template = Bugzilla->template; + my $code = $cgi->param('code'); + + return {failure => AUTH_ERROR, error => 'github_missing_code'} unless $code; + + trick_taint($code); + + my $client = Bugzilla::Extension::GitHubAuth::Client->new; + + 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} && $_->{email} !~ /\@users\.noreply\.github\.com$/ } + @$emails; + + 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 { - return { failure => AUTH_ERROR, user_error => 'github_no_emails' }; + 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 {user => $user}; + } + elsif (@allowed_bugzilla_users > 1) { + $self->{github_failure} = { + template => 'account/auth/github-verify-account.html.tmpl', + vars => { + bugzilla_users => \@allowed_bugzilla_users, + choose_email => _mk_choose_email(\@emails), + }, + }; + 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 => _mk_choose_email(\@emails), + }, + }; + return {failure => AUTH_NODATA}; + } + else { + return {failure => AUTH_ERROR, user_error => 'github_no_emails'}; + } } sub _get_login_info_from_email { - my ($self) = @_; - my $cgi = Bugzilla->cgi; - my $email = $cgi->param('email') or return { failure => AUTH_ERROR, - user_error => 'github_invalid_email', - details => { email => '' } }; - trick_taint($email); - - unless (any { $_ eq $email } @{ Bugzilla->request_cache->{github_emails} }) { - return { failure => AUTH_ERROR, - user_error => 'github_invalid_email', - details => { email => $email }}; - } + my ($self) = @_; + my $cgi = Bugzilla->cgi; + my $email = $cgi->param('email') + or return { + failure => AUTH_ERROR, + user_error => 'github_invalid_email', + details => {email => ''} + }; + trick_taint($email); - my $user = Bugzilla::User->new({name => $email, cache => 1}); - $cgi->remove_cookie('Bugzilla_github_token'); - return $user ? { user => $user } : { email => $email }; + unless (any { $_ eq $email } @{Bugzilla->request_cache->{github_emails}}) { + return { + failure => AUTH_ERROR, + user_error => 'github_invalid_email', + details => {email => $email} + }; + } + + my $user = Bugzilla::User->new({name => $email, cache => 1}); + $cgi->remove_cookie('Bugzilla_github_token'); + return $user ? {user => $user} : {email => $email}; } sub fail_nodata { - my ($self) = @_; - my $cgi = Bugzilla->cgi; - my $template = Bugzilla->template; + my ($self) = @_; + my $cgi = Bugzilla->cgi; + my $template = Bugzilla->template; - ThrowUserError('login_required') if Bugzilla->usage_mode != USAGE_MODE_BROWSER; + 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) }; + 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; + print $cgi->header(); + $template->process($file, $vars) or ThrowTemplateError($template->error()); + exit; } sub _store_emails { - my ($emails) = @_; - my $state = issue_short_lived_session_token("github_email"); - set_token_extra_data($state, { type => 'github_email', - emails => $emails, - target_uri => Bugzilla->request_cache->{github_target_uri} }); - - Bugzilla->cgi->send_cookie(-name => 'github_state', - -value => $state, - -httponly => 1); - return $state; + my ($emails) = @_; + my $state = issue_short_lived_session_token("github_email"); + set_token_extra_data( + $state, + { + type => 'github_email', + emails => $emails, + target_uri => Bugzilla->request_cache->{github_target_uri} + } + ); + + Bugzilla->cgi->send_cookie( + -name => 'github_state', + -value => $state, + -httponly => 1 + ); + return $state; } sub _mk_choose_email { - my ($emails) = @_; - my $state = _store_emails($emails); - - return sub { - my $email = shift; - my $uri = URI->new(Bugzilla->localconfig->{urlbase} . "github.cgi"); - $uri->query_form( state => $state, email => $email ); - return $uri; - }; + my ($emails) = @_; + my $state = _store_emails($emails); + + return sub { + my $email = shift; + my $uri = URI->new(Bugzilla->localconfig->{urlbase} . "github.cgi"); + $uri->query_form(state => $state, email => $email); + return $uri; + }; } 1; diff --git a/extensions/GitHubAuth/lib/Verify.pm b/extensions/GitHubAuth/lib/Verify.pm index f399af02e..078353c80 100644 --- a/extensions/GitHubAuth/lib/Verify.pm +++ b/extensions/GitHubAuth/lib/Verify.pm @@ -16,11 +16,11 @@ use base qw(Bugzilla::Auth::Verify); use Bugzilla::Constants qw( AUTH_NO_SUCH_USER ); sub check_credentials { - my ($self, $login_data) = @_; + my ($self, $login_data) = @_; - return { failure => AUTH_NO_SUCH_USER } unless $login_data->{github_auth}; + return {failure => AUTH_NO_SUCH_USER} unless $login_data->{github_auth}; - return $login_data; + return $login_data; } 1; |