forked from bugzilla/bugzilla
-
Notifications
You must be signed in to change notification settings - Fork 0
/
github.cgi
executable file
·128 lines (106 loc) · 4.14 KB
/
github.cgi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/env perl
# 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(remote_ip);
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 = Bugzilla->localconfig->{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'});
if ($github_secret ne $github_secret2) {
ThrowCodeError("github_invalid_request", {reason => 'invalid secret'});
}
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|\bt=|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));
}
else {
# 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 the homepage.
unless ($state_param
&& $state_cookie
&& ($cgi->param('code') || $cgi->param('email')))
{
$cgi->base_redirect();
}
my $invalid_request = $state_param ne $state_cookie;
my $state_data;
unless ($invalid_request) {
$state_data = get_token_extra_data($state_param);
$invalid_request
= !($state_data
&& $state_data->{type}
&& $state_data->{type} =~ /^github_(?:login|email)$/);
}
if ($invalid_request) {
ThrowCodeError("github_invalid_request", {reason => 'invalid state param'});
}
$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};
}
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('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'));
}
$cgi->base_redirect($attachment_uri->as_string);
}
else {
print $cgi->redirect($target_uri);
}
}