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