#!/usr/bin/perl -T # 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 local/lib/perl5); use Bugzilla; use Bugzilla::Util qw( correct_urlbase ); use Bugzilla::Error; use Bugzilla::Constants; use Bugzilla::Token qw( issue_short_lived_session_token set_token_extra_data get_token_extra_data delete_token ); use URI; use URI::QueryParam; BEGIN { Bugzilla->extensions } use Bugzilla::Extension::GitHubAuth::Client; my $cgi = Bugzilla->cgi; my $urlbase = correct_urlbase(); if (lc($cgi->request_method) eq 'post') { # POST requests come from Bugzilla itself and begin the GitHub login process # by redirecting the user to GitHub's authentication endpoint. my $user = Bugzilla->login(LOGIN_OPTIONAL); my $target_uri = $cgi->param('target_uri') or ThrowCodeError("github_invalid_target"); my $github_secret = $cgi->param('github_secret') or ThrowCodeError("github_invalid_request", { reason => 'invalid secret' }); my $github_secret2 = Bugzilla->github_secret or ThrowCodeError("github_invalid_request", { reason => 'invalid secret' }); ThrowCodeError("github_invalid_request", { reason => 'invalid secret' }) unless $github_secret eq $github_secret2; ThrowCodeError("github_invalid_target", { target_uri => $target_uri }) unless $target_uri =~ /^\Q$urlbase\E/; ThrowCodeError("github_insecure_referer", { target_uri => $target_uri }) if $cgi->referer && $cgi->referer =~ /(reset_password\.cgi|token\.cgi|t=|token=|api_key=)/; if ($user->id) { print $cgi->redirect($target_uri); exit; } my $state = issue_short_lived_session_token("github_state"); set_token_extra_data($state, { type => 'github_login', target_uri => $target_uri }); $cgi->send_cookie(-name => 'github_state', -value => $state, -httponly => 1); print $cgi->redirect(Bugzilla::Extension::GitHubAuth::Client->authorize_uri($state)); } elsif (lc($cgi->request_method) eq 'get') { # GET requests come from GitHub, with this script acting as the OAuth2 callback. my $state_param = $cgi->param('state'); my $state_cookie = $cgi->cookie('github_state'); # If the state or params are missing, or the github_state cookie is missing # we just redirect to index.cgi. unless ($state_param && $state_cookie && ($cgi->param('code') || $cgi->param('email'))) { print $cgi->redirect($urlbase . "index.cgi"); exit; } ThrowCodeError("github_invalid_request", { reason => 'invalid state param' }) unless $state_param eq $state_cookie; my $state_data = get_token_extra_data($state_param); ThrowCodeError("github_invalid_request", { reason => 'invalid state param' } ) unless $state_data && $state_data->{type}; $cgi->remove_cookie('github_state'); delete_token($state_param); if ($state_data->{type} eq 'github_login') { Bugzilla->request_cache->{github_action} = 'login'; Bugzilla->request_cache->{github_target_uri} = $state_data->{target_uri}; } elsif ($state_data->{type} eq 'github_email') { Bugzilla->request_cache->{github_action} = 'email'; Bugzilla->request_cache->{github_emails} = $state_data->{emails}; } else { ThrowCodeError("github_invalid_request", { reason => "invalid state param" }) } my $user = Bugzilla->login(LOGIN_REQUIRED); my $target_uri = URI->new($state_data->{target_uri}); # It makes very little sense to login to a page with the logout parameter. # doing so would be a no-op, so we ignore the logout param here. $target_uri->query_param_delete('logout'); if ($target_uri->path =~ /attachment\.cgi/) { my $attachment_uri = URI->new($urlbase . "attachment.cgi"); $attachment_uri->query_param(id => scalar $target_uri->query_param('id')); if ($target_uri->query_param('action')) { $attachment_uri->query_param(action => scalar $target_uri->query_param('action')); } print $cgi->redirect($attachment_uri); } else { print $cgi->redirect($target_uri); } }