1
1
require 'sinatra'
2
2
require 'octokit'
3
+ require 'dotenv/load' # Manages environment variables
4
+ require 'git'
3
5
require 'json'
4
- require 'openssl' # Used to verify the webhook signature
5
- require 'jwt' # Used to authenticate a GitHub App
6
- require 'time' # Used to get ISO 8601 representation of a Time object
7
- require 'logger'
6
+ require 'openssl' # Verifies the webhook signature
7
+ require 'jwt' # Authenticates a GitHub App
8
+ require 'time' # Gets ISO 8601 representation of a Time object
9
+ require 'logger' # Logs debug statements
8
10
9
11
set :port , 3000
12
+ set :bind , '0.0.0.0'
10
13
11
14
12
15
# This is template code to create a GitHub App server.
13
16
# You can read more about GitHub Apps here: # https://developer.github.com/apps/
14
17
#
15
18
# On its own, this app does absolutely nothing, except that it can be installed.
16
- # It's up to you to add fun functionality!
19
+ # It's up to you to add functionality!
17
20
# You can check out one example in advanced_server.rb.
18
21
#
19
22
# This code is a Sinatra app, for two reasons:
20
23
# 1. Because the app will require a landing page for installation.
21
24
# 2. To easily handle webhook events.
22
25
#
23
- #
24
26
# Of course, not all apps need to receive and process events!
25
27
# Feel free to rip out the event handling code if you don't need it.
26
28
#
29
31
30
32
class GHAapp < Sinatra ::Application
31
33
32
- # !!! DO NOT EVER USE HARD-CODED VALUES IN A REAL APP !!!
33
- # Instead, set and read app tokens or other secrets in your code
34
- # in a runtime source, like an environment variable like below
35
-
36
- # Expects that the private key has been set as an environment variable in
37
- # PEM format using the following command to replace newlines with the
38
- # literal `\n`:
39
- # export GITHUB_PRIVATE_KEY=`awk '{printf "%s\\n", $0}' private-key.pem`
40
- #
41
- # Converts the newlines
34
+ # Expects that the private key in PEM format. Converts the newlines
42
35
PRIVATE_KEY = OpenSSL ::PKey ::RSA . new ( ENV [ 'GITHUB_PRIVATE_KEY' ] . gsub ( '\n' , "\n " ) )
43
36
44
37
# Your registered app must have a secret set. The secret is used to verify
@@ -59,26 +52,25 @@ class GHAapp < Sinatra::Application
59
52
get_payload_request ( request )
60
53
verify_webhook_signature
61
54
authenticate_app
62
- # Authenticate each installation of the app in order to run API operations
55
+ # Authenticate the app installation in order to run API operations
63
56
authenticate_installation ( @payload )
64
57
end
65
58
66
59
67
60
post '/event_handler' do
68
61
69
- # # # # # # # # # # # # # # # # # # #
70
- # ADD YOUR CODE HERE #
71
- # # # # # # # # # # # # # # # # # # #
62
+ # # # # # # # # # # # #
63
+ # ADD YOUR CODE HERE #
64
+ # # # # # # # # # # # #
72
65
73
- 'ok' # We've got to return _something_. ;)
74
66
end
75
67
76
68
77
69
helpers do
78
70
79
- # # # # # # # # # # # # # # # # # # #
80
- # ADD YOUR HELPERS METHODS HERE #
81
- # # # # # # # # # # # # # # # # # # #
71
+ # # # # # # # # # # # # # # # # #
72
+ # ADD YOUR HELPER METHODS HERE #
73
+ # # # # # # # # # # # # # # # # #
82
74
83
75
# Saves the raw payload and converts the payload to JSON format
84
76
def get_payload_request ( request )
@@ -95,7 +87,7 @@ def get_payload_request(request)
95
87
end
96
88
97
89
# Instantiate an Octokit client authenticated as a GitHub App.
98
- # GitHub App authentication equires that we construct a
90
+ # GitHub App authentication requires that you construct a
99
91
# JWT (https://jwt.io/introduction/) signed with the app's private key,
100
92
# so GitHub can be sure that it came from the app an not altererd by
101
93
# a malicious third party.
@@ -111,15 +103,15 @@ def authenticate_app
111
103
iss : APP_IDENTIFIER
112
104
}
113
105
114
- # Cryptographically sign the JWT
106
+ # Cryptographically sign the JWT.
115
107
jwt = JWT . encode ( payload , PRIVATE_KEY , 'RS256' )
116
108
117
109
# Create the Octokit client, using the JWT as the auth token.
118
110
@app_client ||= Octokit ::Client . new ( bearer_token : jwt )
119
111
end
120
112
121
- # Instantiate an Octokit client authenticated as an installation of a
122
- # GitHub App to run API operations.
113
+ # Instantiate an Octokit client, authenticated as an installation of a
114
+ # GitHub App, to run API operations.
123
115
def authenticate_installation ( payload )
124
116
installation_id = payload [ 'installation' ] [ 'id' ]
125
117
installation_token = @app_client . create_app_installation_access_token ( installation_id ) [ :token ]
@@ -129,14 +121,14 @@ def authenticate_installation(payload)
129
121
# Check X-Hub-Signature to confirm that this webhook was generated by
130
122
# GitHub, and not a malicious third party.
131
123
#
132
- # GitHub will the WEBHOOK_SECRET, registered
133
- # to the GitHub App, to create a hash signature sent in each webhook payload
134
- # in the `X-HUB-Signature` header . This code computes the expected hash
135
- # signature and compares it to the signature sent in the `X-HUB-Signature`
136
- # header. If they don't match, this request is an attack, and we should
137
- # reject it. GitHub uses the HMAC hexdigest to compute the signature. The
138
- # `X-HUB-Signature` looks something like this: "sha1=123456"
139
- # See https://developer.github.com/webhooks/securing/ for details
124
+ # GitHub uses the WEBHOOK_SECRET, registered to the GitHub App, to
125
+ # create the hash signature sent in the `X-HUB-Signature` header of each
126
+ # webhook . This code computes the expected hash signature and compares it to
127
+ # the signature sent in the `X-HUB-Signature` header. If they don't match,
128
+ # this request is an attack, and you should reject it. GitHub uses the HMAC
129
+ # hexdigest to compute the signature. The `X-HUB-Signature` looks something
130
+ # like this: "sha1=123456".
131
+ # See https://developer.github.com/webhooks/securing/ for details.
140
132
def verify_webhook_signature
141
133
their_signature_header = request . env [ 'HTTP_X_HUB_SIGNATURE' ] || 'sha1='
142
134
method , their_digest = their_signature_header . split ( '=' )
@@ -150,12 +142,5 @@ def verify_webhook_signature
150
142
end
151
143
152
144
end
153
-
154
-
155
- # Finally some logic to let us run this server directly from the commandline, or with Rack
156
- # Don't worry too much about this code ;) But, for the curious:
157
- # $0 is the executed file
158
- # __FILE__ is the current file
159
- # If they are the same—that is, we are running this file directly, call the Sinatra run method
160
145
run! if __FILE__ == $0
161
146
end
0 commit comments