sendgrid-ruby 6.2.1 → 6.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.codeclimate.yml +1 -1
- data/.gitignore +1 -1
- data/.rubocop.yml +4 -0
- data/.rubocop_todo.yml +109 -0
- data/.travis.yml +3 -4
- data/CHANGELOG.md +69 -9
- data/CONTRIBUTING.md +11 -21
- data/FIRST_TIMERS.md +79 -0
- data/Gemfile +0 -1
- data/ISSUE_TEMPLATE.md +5 -1
- data/Makefile +4 -2
- data/PULL_REQUEST_TEMPLATE.md +1 -1
- data/README.md +22 -27
- data/Rakefile +2 -3
- data/TROUBLESHOOTING.md +5 -5
- data/USAGE.md +145 -38
- data/examples/accesssettings/accesssettings.rb +9 -12
- data/examples/alerts/alerts.rb +8 -11
- data/examples/apikeys/apikeys.rb +12 -15
- data/examples/asm/asm.rb +27 -30
- data/examples/browsers/browsers.rb +0 -3
- data/examples/campaigns/campaigns.rb +29 -32
- data/examples/categories/categories.rb +0 -3
- data/examples/clients/clients.rb +1 -4
- data/examples/contactdb/contactdb.rb +63 -66
- data/examples/devices/devices.rb +0 -3
- data/examples/emailactivity/emailactivity.rb +52 -0
- data/examples/geo/geo.rb +0 -3
- data/examples/helpers/eventwebhook/example.rb +16 -0
- data/examples/helpers/mail/example.rb +19 -13
- data/examples/helpers/settings/example.rb +1 -1
- data/examples/helpers/stats/example.rb +4 -4
- data/examples/ips/ips.rb +19 -22
- data/examples/mail/mail.rb +72 -75
- data/examples/mailboxproviders/mailboxproviders.rb +0 -3
- data/examples/mailsettings/mailsettings.rb +21 -24
- data/examples/partnersettings/partnersettings.rb +3 -6
- data/examples/scopes/scopes.rb +7 -9
- data/examples/senderauthentication/senderauthentication.rb +41 -44
- data/examples/senders/senders.rb +28 -31
- data/examples/stats/stats.rb +0 -3
- data/examples/subusers/subusers.rb +17 -20
- data/examples/suppression/suppression.rb +15 -18
- data/examples/templates/templates.rb +29 -31
- data/examples/trackingsettings/trackingsettings.rb +14 -17
- data/examples/user/user.rb +41 -44
- data/lib/rack/sendgrid_webhook_verification.rb +53 -0
- data/lib/sendgrid-ruby.rb +2 -0
- data/lib/sendgrid/base_interface.rb +1 -1
- data/lib/sendgrid/helpers/eventwebhook/eventwebhook.rb +50 -0
- data/lib/sendgrid/helpers/inbound/README.md +5 -5
- data/lib/sendgrid/helpers/inbound/app.rb +1 -1
- data/lib/sendgrid/helpers/inbound/public/index.html +1 -1
- data/lib/sendgrid/helpers/inbound/send.rb +2 -2
- data/lib/sendgrid/helpers/ip_management/ip_management.rb +1 -1
- data/lib/sendgrid/helpers/mail/README.md +3 -3
- data/lib/sendgrid/helpers/mail/asm.rb +6 -18
- data/lib/sendgrid/helpers/mail/attachment.rb +12 -42
- data/lib/sendgrid/helpers/mail/bcc_settings.rb +6 -18
- data/lib/sendgrid/helpers/mail/bypass_list_management.rb +8 -18
- data/lib/sendgrid/helpers/mail/category.rb +2 -2
- data/lib/sendgrid/helpers/mail/click_tracking.rb +6 -18
- data/lib/sendgrid/helpers/mail/content.rb +4 -3
- data/lib/sendgrid/helpers/mail/custom_arg.rb +6 -10
- data/lib/sendgrid/helpers/mail/email.rb +5 -4
- data/lib/sendgrid/helpers/mail/footer.rb +7 -27
- data/lib/sendgrid/helpers/mail/ganalytics.rb +10 -54
- data/lib/sendgrid/helpers/mail/header.rb +6 -10
- data/lib/sendgrid/helpers/mail/mail.rb +30 -48
- data/lib/sendgrid/helpers/mail/mail_settings.rb +9 -25
- data/lib/sendgrid/helpers/mail/open_tracking.rb +6 -18
- data/lib/sendgrid/helpers/mail/personalization.rb +34 -27
- data/lib/sendgrid/helpers/mail/section.rb +6 -10
- data/lib/sendgrid/helpers/mail/spam_check.rb +7 -27
- data/lib/sendgrid/helpers/mail/subscription_tracking.rb +8 -36
- data/lib/sendgrid/helpers/mail/substitution.rb +6 -10
- data/lib/sendgrid/helpers/mail/tracking_settings.rb +7 -20
- data/lib/sendgrid/helpers/permissions/scope.rb +1 -1
- data/lib/sendgrid/helpers/settings/README.md +2 -2
- data/lib/sendgrid/helpers/settings/settings.rb +1 -1
- data/lib/sendgrid/helpers/settings/tracking_settings_dto.rb +3 -5
- data/lib/sendgrid/helpers/stats/metrics.rb +5 -5
- data/lib/sendgrid/sendgrid.rb +1 -1
- data/lib/sendgrid/twilio_email.rb +1 -1
- data/lib/sendgrid/version.rb +1 -1
- data/mail_helper_v3.md +12 -12
- data/sendgrid-ruby.gemspec +8 -7
- data/spec/fixtures/event_webhook.rb +22 -0
- data/spec/rack/sendgrid_webhook_verification_spec.rb +116 -0
- data/spec/sendgrid/helpers/eventwebhook/eventwebhook_spec.rb +105 -0
- data/spec/sendgrid/helpers/settings/mail_settings_dto_spec.rb +1 -1
- data/spec/sendgrid/helpers/settings/partner_settings_dto_spec.rb +1 -1
- data/spec/sendgrid/helpers/settings/settings_spec.rb +2 -2
- data/spec/sendgrid/helpers/settings/tracking_settings_dto_spec.rb +1 -1
- data/spec/sendgrid/helpers/settings/user_settings_dto_spec.rb +1 -1
- data/spec/sendgrid/helpers/stats/email_stats_spec.rb +22 -23
- data/spec/sendgrid/helpers/stats/metrics_spec.rb +19 -20
- data/spec/sendgrid/helpers/stats/stats_response_spec.rb +22 -23
- data/spec/spec_helper.rb +3 -1
- data/static/img/github-fork.png +0 -0
- data/static/img/github-sign-up.png +0 -0
- data/test/sendgrid/helpers/mail/test_attachment.rb +4 -6
- data/test/sendgrid/helpers/mail/test_category.rb +0 -2
- data/test/sendgrid/helpers/mail/test_email.rb +9 -11
- data/test/sendgrid/helpers/mail/test_mail.rb +101 -102
- data/test/sendgrid/helpers/mail/test_personalizations.rb +106 -93
- data/test/sendgrid/permissions/test_scopes.rb +0 -2
- data/test/sendgrid/test_sendgrid-ruby.rb +1948 -1958
- data/twilio_sendgrid_logo.png +0 -0
- data/use-cases/README.md +16 -0
- data/use-cases/domain-authentication.md +5 -0
- data/use-cases/email-statistics.md +52 -0
- data/use-cases/legacy-templates.md +98 -0
- data/use-cases/sms.md +39 -0
- data/use-cases/transactional-templates.md +111 -0
- data/use-cases/twilio-email.md +13 -0
- data/use-cases/twilio-setup.md +54 -0
- metadata +73 -24
- data/USE_CASES.md +0 -405
- data/docker/Dockerfile +0 -12
- data/docker/README.md +0 -30
@@ -1,9 +1,7 @@
|
|
1
1
|
require 'sendgrid-ruby'
|
2
2
|
|
3
|
-
|
4
3
|
sg = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])
|
5
4
|
|
6
|
-
|
7
5
|
##################################################
|
8
6
|
# Retrieve Tracking Settings #
|
9
7
|
# GET /tracking_settings #
|
@@ -30,7 +28,7 @@ puts response.headers
|
|
30
28
|
# Retrieve Click Track Settings #
|
31
29
|
# GET /tracking_settings/click #
|
32
30
|
|
33
|
-
response = sg.client.tracking_settings.click.get
|
31
|
+
response = sg.client.tracking_settings.click.get
|
34
32
|
puts response.status_code
|
35
33
|
puts response.body
|
36
34
|
puts response.headers
|
@@ -40,11 +38,11 @@ puts response.headers
|
|
40
38
|
# PATCH /tracking_settings/google_analytics #
|
41
39
|
|
42
40
|
data = JSON.parse('{
|
43
|
-
"enabled": true,
|
44
|
-
"utm_campaign": "website",
|
45
|
-
"utm_content": "",
|
46
|
-
"utm_medium": "email",
|
47
|
-
"utm_source": "sendgrid.com",
|
41
|
+
"enabled": true,
|
42
|
+
"utm_campaign": "website",
|
43
|
+
"utm_content": "",
|
44
|
+
"utm_medium": "email",
|
45
|
+
"utm_source": "sendgrid.com",
|
48
46
|
"utm_term": ""
|
49
47
|
}')
|
50
48
|
response = sg.client.tracking_settings.google_analytics.patch(request_body: data)
|
@@ -56,7 +54,7 @@ puts response.headers
|
|
56
54
|
# Retrieve Google Analytics Settings #
|
57
55
|
# GET /tracking_settings/google_analytics #
|
58
56
|
|
59
|
-
response = sg.client.tracking_settings.google_analytics.get
|
57
|
+
response = sg.client.tracking_settings.google_analytics.get
|
60
58
|
puts response.status_code
|
61
59
|
puts response.body
|
62
60
|
puts response.headers
|
@@ -77,7 +75,7 @@ puts response.headers
|
|
77
75
|
# Get Open Tracking Settings #
|
78
76
|
# GET /tracking_settings/open #
|
79
77
|
|
80
|
-
response = sg.client.tracking_settings.open.get
|
78
|
+
response = sg.client.tracking_settings.open.get
|
81
79
|
puts response.status_code
|
82
80
|
puts response.body
|
83
81
|
puts response.headers
|
@@ -87,11 +85,11 @@ puts response.headers
|
|
87
85
|
# PATCH /tracking_settings/subscription #
|
88
86
|
|
89
87
|
data = JSON.parse('{
|
90
|
-
"enabled": true,
|
91
|
-
"html_content": "html content",
|
92
|
-
"landing": "landing page html",
|
93
|
-
"plain_content": "text content",
|
94
|
-
"replace": "replacement tag",
|
88
|
+
"enabled": true,
|
89
|
+
"html_content": "html content",
|
90
|
+
"landing": "landing page html",
|
91
|
+
"plain_content": "text content",
|
92
|
+
"replace": "replacement tag",
|
95
93
|
"url": "url"
|
96
94
|
}')
|
97
95
|
response = sg.client.tracking_settings.subscription.patch(request_body: data)
|
@@ -103,8 +101,7 @@ puts response.headers
|
|
103
101
|
# Retrieve Subscription Tracking Settings #
|
104
102
|
# GET /tracking_settings/subscription #
|
105
103
|
|
106
|
-
response = sg.client.tracking_settings.subscription.get
|
104
|
+
response = sg.client.tracking_settings.subscription.get
|
107
105
|
puts response.status_code
|
108
106
|
puts response.body
|
109
107
|
puts response.headers
|
110
|
-
|
data/examples/user/user.rb
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
require 'sendgrid-ruby'
|
2
2
|
|
3
|
-
|
4
3
|
sg = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])
|
5
4
|
|
6
|
-
|
7
5
|
##################################################
|
8
6
|
# Get a user's account information. #
|
9
7
|
# GET /user/account #
|
10
8
|
|
11
|
-
response = sg.client.user.account.get
|
9
|
+
response = sg.client.user.account.get
|
12
10
|
puts response.status_code
|
13
11
|
puts response.body
|
14
12
|
puts response.headers
|
@@ -17,7 +15,7 @@ puts response.headers
|
|
17
15
|
# Retrieve your credit balance #
|
18
16
|
# GET /user/credits #
|
19
17
|
|
20
|
-
response = sg.client.user.credits.get
|
18
|
+
response = sg.client.user.credits.get
|
21
19
|
puts response.status_code
|
22
20
|
puts response.body
|
23
21
|
puts response.headers
|
@@ -38,7 +36,7 @@ puts response.headers
|
|
38
36
|
# Retrieve your account email address #
|
39
37
|
# GET /user/email #
|
40
38
|
|
41
|
-
response = sg.client.user.email.get
|
39
|
+
response = sg.client.user.email.get
|
42
40
|
puts response.status_code
|
43
41
|
puts response.body
|
44
42
|
puts response.headers
|
@@ -48,7 +46,7 @@ puts response.headers
|
|
48
46
|
# PUT /user/password #
|
49
47
|
|
50
48
|
data = JSON.parse('{
|
51
|
-
"new_password": "new_password",
|
49
|
+
"new_password": "new_password",
|
52
50
|
"old_password": "old_password"
|
53
51
|
}')
|
54
52
|
response = sg.client.user.password.put(request_body: data)
|
@@ -61,8 +59,8 @@ puts response.headers
|
|
61
59
|
# PATCH /user/profile #
|
62
60
|
|
63
61
|
data = JSON.parse('{
|
64
|
-
"city": "Orange",
|
65
|
-
"first_name": "Example",
|
62
|
+
"city": "Orange",
|
63
|
+
"first_name": "Example",
|
66
64
|
"last_name": "User"
|
67
65
|
}')
|
68
66
|
response = sg.client.user.profile.patch(request_body: data)
|
@@ -74,7 +72,7 @@ puts response.headers
|
|
74
72
|
# Get a user's profile #
|
75
73
|
# GET /user/profile #
|
76
74
|
|
77
|
-
response = sg.client.user.profile.get
|
75
|
+
response = sg.client.user.profile.get
|
78
76
|
puts response.status_code
|
79
77
|
puts response.body
|
80
78
|
puts response.headers
|
@@ -84,7 +82,7 @@ puts response.headers
|
|
84
82
|
# POST /user/scheduled_sends #
|
85
83
|
|
86
84
|
data = JSON.parse('{
|
87
|
-
"batch_id": "YOUR_BATCH_ID",
|
85
|
+
"batch_id": "YOUR_BATCH_ID",
|
88
86
|
"status": "pause"
|
89
87
|
}')
|
90
88
|
response = sg.client.user.scheduled_sends.post(request_body: data)
|
@@ -96,7 +94,7 @@ puts response.headers
|
|
96
94
|
# Retrieve all scheduled sends #
|
97
95
|
# GET /user/scheduled_sends #
|
98
96
|
|
99
|
-
response = sg.client.user.scheduled_sends.get
|
97
|
+
response = sg.client.user.scheduled_sends.get
|
100
98
|
puts response.status_code
|
101
99
|
puts response.body
|
102
100
|
puts response.headers
|
@@ -108,7 +106,7 @@ puts response.headers
|
|
108
106
|
data = JSON.parse('{
|
109
107
|
"status": "pause"
|
110
108
|
}')
|
111
|
-
batch_id =
|
109
|
+
batch_id = 'test_url_param'
|
112
110
|
response = sg.client.user.scheduled_sends._(batch_id).patch(request_body: data)
|
113
111
|
puts response.status_code
|
114
112
|
puts response.body
|
@@ -118,8 +116,8 @@ puts response.headers
|
|
118
116
|
# Retrieve scheduled send #
|
119
117
|
# GET /user/scheduled_sends/{batch_id} #
|
120
118
|
|
121
|
-
batch_id =
|
122
|
-
response = sg.client.user.scheduled_sends._(batch_id).get
|
119
|
+
batch_id = 'test_url_param'
|
120
|
+
response = sg.client.user.scheduled_sends._(batch_id).get
|
123
121
|
puts response.status_code
|
124
122
|
puts response.body
|
125
123
|
puts response.headers
|
@@ -128,8 +126,8 @@ puts response.headers
|
|
128
126
|
# Delete a cancellation or pause of a scheduled send #
|
129
127
|
# DELETE /user/scheduled_sends/{batch_id} #
|
130
128
|
|
131
|
-
batch_id =
|
132
|
-
response = sg.client.user.scheduled_sends._(batch_id).delete
|
129
|
+
batch_id = 'test_url_param'
|
130
|
+
response = sg.client.user.scheduled_sends._(batch_id).delete
|
133
131
|
puts response.status_code
|
134
132
|
puts response.body
|
135
133
|
puts response.headers
|
@@ -139,7 +137,7 @@ puts response.headers
|
|
139
137
|
# PATCH /user/settings/enforced_tls #
|
140
138
|
|
141
139
|
data = JSON.parse('{
|
142
|
-
"require_tls": true,
|
140
|
+
"require_tls": true,
|
143
141
|
"require_valid_cert": false
|
144
142
|
}')
|
145
143
|
response = sg.client.user.settings.enforced_tls.patch(request_body: data)
|
@@ -151,7 +149,7 @@ puts response.headers
|
|
151
149
|
# Retrieve current Enforced TLS settings. #
|
152
150
|
# GET /user/settings/enforced_tls #
|
153
151
|
|
154
|
-
response = sg.client.user.settings.enforced_tls.get
|
152
|
+
response = sg.client.user.settings.enforced_tls.get
|
155
153
|
puts response.status_code
|
156
154
|
puts response.body
|
157
155
|
puts response.headers
|
@@ -172,7 +170,7 @@ puts response.headers
|
|
172
170
|
# Retrieve your username #
|
173
171
|
# GET /user/username #
|
174
172
|
|
175
|
-
response = sg.client.user.username.get
|
173
|
+
response = sg.client.user.username.get
|
176
174
|
puts response.status_code
|
177
175
|
puts response.body
|
178
176
|
puts response.headers
|
@@ -182,18 +180,18 @@ puts response.headers
|
|
182
180
|
# PATCH /user/webhooks/event/settings #
|
183
181
|
|
184
182
|
data = JSON.parse('{
|
185
|
-
"bounce": true,
|
186
|
-
"click": true,
|
187
|
-
"deferred": true,
|
188
|
-
"delivered": true,
|
189
|
-
"dropped": true,
|
190
|
-
"enabled": true,
|
191
|
-
"group_resubscribe": true,
|
192
|
-
"group_unsubscribe": true,
|
193
|
-
"open": true,
|
194
|
-
"processed": true,
|
195
|
-
"spam_report": true,
|
196
|
-
"unsubscribe": true,
|
183
|
+
"bounce": true,
|
184
|
+
"click": true,
|
185
|
+
"deferred": true,
|
186
|
+
"delivered": true,
|
187
|
+
"dropped": true,
|
188
|
+
"enabled": true,
|
189
|
+
"group_resubscribe": true,
|
190
|
+
"group_unsubscribe": true,
|
191
|
+
"open": true,
|
192
|
+
"processed": true,
|
193
|
+
"spam_report": true,
|
194
|
+
"unsubscribe": true,
|
197
195
|
"url": "url"
|
198
196
|
}')
|
199
197
|
response = sg.client.user.webhooks.event.settings.patch(request_body: data)
|
@@ -205,7 +203,7 @@ puts response.headers
|
|
205
203
|
# Retrieve Event Webhook settings #
|
206
204
|
# GET /user/webhooks/event/settings #
|
207
205
|
|
208
|
-
response = sg.client.user.webhooks.event.settings.get
|
206
|
+
response = sg.client.user.webhooks.event.settings.get
|
209
207
|
puts response.status_code
|
210
208
|
puts response.body
|
211
209
|
puts response.headers
|
@@ -227,9 +225,9 @@ puts response.headers
|
|
227
225
|
# POST /user/webhooks/parse/settings #
|
228
226
|
|
229
227
|
data = JSON.parse('{
|
230
|
-
"hostname": "myhostname.com",
|
231
|
-
"send_raw": false,
|
232
|
-
"spam_check": true,
|
228
|
+
"hostname": "myhostname.com",
|
229
|
+
"send_raw": false,
|
230
|
+
"spam_check": true,
|
233
231
|
"url": "http://email.myhosthame.com"
|
234
232
|
}')
|
235
233
|
response = sg.client.user.webhooks.parse.settings.post(request_body: data)
|
@@ -241,7 +239,7 @@ puts response.headers
|
|
241
239
|
# Retrieve all parse settings #
|
242
240
|
# GET /user/webhooks/parse/settings #
|
243
241
|
|
244
|
-
response = sg.client.user.webhooks.parse.settings.get
|
242
|
+
response = sg.client.user.webhooks.parse.settings.get
|
245
243
|
puts response.status_code
|
246
244
|
puts response.body
|
247
245
|
puts response.headers
|
@@ -251,11 +249,11 @@ puts response.headers
|
|
251
249
|
# PATCH /user/webhooks/parse/settings/{hostname} #
|
252
250
|
|
253
251
|
data = JSON.parse('{
|
254
|
-
"send_raw": true,
|
255
|
-
"spam_check": false,
|
252
|
+
"send_raw": true,
|
253
|
+
"spam_check": false,
|
256
254
|
"url": "http://newdomain.com/parse"
|
257
255
|
}')
|
258
|
-
hostname =
|
256
|
+
hostname = 'test_url_param'
|
259
257
|
response = sg.client.user.webhooks.parse.settings._(hostname).patch(request_body: data)
|
260
258
|
puts response.status_code
|
261
259
|
puts response.body
|
@@ -265,8 +263,8 @@ puts response.headers
|
|
265
263
|
# Retrieve a specific parse setting #
|
266
264
|
# GET /user/webhooks/parse/settings/{hostname} #
|
267
265
|
|
268
|
-
hostname =
|
269
|
-
response = sg.client.user.webhooks.parse.settings._(hostname).get
|
266
|
+
hostname = 'test_url_param'
|
267
|
+
response = sg.client.user.webhooks.parse.settings._(hostname).get
|
270
268
|
puts response.status_code
|
271
269
|
puts response.body
|
272
270
|
puts response.headers
|
@@ -275,8 +273,8 @@ puts response.headers
|
|
275
273
|
# Delete a parse setting #
|
276
274
|
# DELETE /user/webhooks/parse/settings/{hostname} #
|
277
275
|
|
278
|
-
hostname =
|
279
|
-
response = sg.client.user.webhooks.parse.settings._(hostname).delete
|
276
|
+
hostname = 'test_url_param'
|
277
|
+
response = sg.client.user.webhooks.parse.settings._(hostname).delete
|
280
278
|
puts response.status_code
|
281
279
|
puts response.body
|
282
280
|
puts response.headers
|
@@ -290,4 +288,3 @@ response = sg.client.user.webhooks.parse.stats.get(query_params: params)
|
|
290
288
|
puts response.status_code
|
291
289
|
puts response.body
|
292
290
|
puts response.headers
|
293
|
-
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
# Middleware that verifies webhooks from SendGrid using the EventWebhook
|
5
|
+
# verifier.
|
6
|
+
#
|
7
|
+
# The middleware takes a public key with which to set up the request
|
8
|
+
# validator and any number of paths. When a path matches the incoming request
|
9
|
+
# path, the request will be verified using the signature and timestamp of the
|
10
|
+
# request.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
#
|
14
|
+
# require 'rack'
|
15
|
+
# use Rack::SendGridWebhookVerification, ENV['PUBLIC_KEY'], /\/emails/
|
16
|
+
#
|
17
|
+
# The above appends this middleware to the stack, using a public key saved in
|
18
|
+
# the ENV and only against paths that match /\/emails/. If the request
|
19
|
+
# validates then it gets passed on to the action as normal. If the request
|
20
|
+
# doesn't validate then the middleware responds immediately with a 403 status.
|
21
|
+
class SendGridWebhookVerification
|
22
|
+
def initialize(app, public_key, *paths)
|
23
|
+
@app = app
|
24
|
+
@public_key = public_key
|
25
|
+
@path_regex = Regexp.union(paths)
|
26
|
+
end
|
27
|
+
|
28
|
+
def call(env)
|
29
|
+
return @app.call(env) unless env['PATH_INFO'].match(@path_regex)
|
30
|
+
|
31
|
+
request = Rack::Request.new(env)
|
32
|
+
|
33
|
+
event_webhook = SendGrid::EventWebhook.new
|
34
|
+
ec_public_key = event_webhook.convert_public_key_to_ecdsa(@public_key)
|
35
|
+
verified = event_webhook.verify_signature(
|
36
|
+
ec_public_key,
|
37
|
+
request.body.read,
|
38
|
+
request.env[SendGrid::EventWebhookHeader::SIGNATURE],
|
39
|
+
request.env[SendGrid::EventWebhookHeader::TIMESTAMP]
|
40
|
+
)
|
41
|
+
|
42
|
+
if verified
|
43
|
+
@app.call(env)
|
44
|
+
else
|
45
|
+
[
|
46
|
+
403,
|
47
|
+
{ 'Content-Type' => 'text/plain' },
|
48
|
+
['SendGrid Request Verification Failed.']
|
49
|
+
]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/sendgrid-ruby.rb
CHANGED
@@ -2,6 +2,7 @@ require_relative 'sendgrid/base_interface'
|
|
2
2
|
require_relative 'sendgrid/sendgrid'
|
3
3
|
require_relative 'sendgrid/twilio_email'
|
4
4
|
require_relative 'sendgrid/version'
|
5
|
+
require_relative 'sendgrid/helpers/eventwebhook/eventwebhook'
|
5
6
|
require_relative 'sendgrid/helpers/ip_management/ip_management'
|
6
7
|
require_relative 'sendgrid/helpers/mail/asm'
|
7
8
|
require_relative 'sendgrid/helpers/mail/attachment'
|
@@ -29,3 +30,4 @@ require_relative 'sendgrid/helpers/stats/email_stats'
|
|
29
30
|
require_relative 'sendgrid/helpers/stats/stats_response'
|
30
31
|
require_relative 'sendgrid/helpers/stats/metrics'
|
31
32
|
require_relative 'sendgrid/helpers/permissions/scope'
|
33
|
+
require_relative 'rack/sendgrid_webhook_verification'
|
@@ -17,7 +17,7 @@ class BaseInterface
|
|
17
17
|
def initialize(auth:, host:, request_headers: nil, version: nil, impersonate_subuser: nil)
|
18
18
|
@auth = auth
|
19
19
|
@host = host
|
20
|
-
@version = version
|
20
|
+
@version = version || 'v3'
|
21
21
|
@impersonate_subuser = impersonate_subuser
|
22
22
|
@user_agent = "sendgrid/#{SendGrid::VERSION};ruby"
|
23
23
|
@request_headers = JSON.parse('
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'digest'
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module SendGrid
|
6
|
+
# This class allows you to use the Event Webhook feature. Read the docs for
|
7
|
+
# more details: https://sendgrid.com/docs/for-developers/tracking-events/event
|
8
|
+
class EventWebhook
|
9
|
+
# * *Args* :
|
10
|
+
# - +public_key+ -> verification key under Mail Settings
|
11
|
+
#
|
12
|
+
def convert_public_key_to_ecdsa(public_key)
|
13
|
+
verify_engine
|
14
|
+
OpenSSL::PKey::EC.new(Base64.decode64(public_key))
|
15
|
+
end
|
16
|
+
|
17
|
+
# * *Args* :
|
18
|
+
# - +public_key+ -> elliptic curve public key
|
19
|
+
# - +payload+ -> event payload in the request body
|
20
|
+
# - +signature+ -> signature value obtained from the 'X-Twilio-Email-Event-Webhook-Signature' header
|
21
|
+
# - +timestamp+ -> timestamp value obtained from the 'X-Twilio-Email-Event-Webhook-Timestamp' header
|
22
|
+
def verify_signature(public_key, payload, signature, timestamp)
|
23
|
+
verify_engine
|
24
|
+
timestamped_playload = "#{timestamp}#{payload}"
|
25
|
+
payload_digest = Digest::SHA256.digest(timestamped_playload)
|
26
|
+
decoded_signature = Base64.decode64(signature)
|
27
|
+
public_key.dsa_verify_asn1(payload_digest, decoded_signature)
|
28
|
+
rescue StandardError
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
def verify_engine
|
33
|
+
# JRuby does not fully support ECDSA: https://github.com/jruby/jruby-openssl/issues/193
|
34
|
+
raise NotSupportedError, "Event Webhook verification is not supported by JRuby" if RUBY_PLATFORM == "java"
|
35
|
+
end
|
36
|
+
|
37
|
+
class Error < ::RuntimeError
|
38
|
+
end
|
39
|
+
|
40
|
+
class NotSupportedError < Error
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# This class lists headers that get posted to the webhook. Read the docs for
|
45
|
+
# more details: https://sendgrid.com/docs/for-developers/tracking-events/event
|
46
|
+
class EventWebhookHeader
|
47
|
+
SIGNATURE = "HTTP_X_TWILIO_EMAIL_EVENT_WEBHOOK_SIGNATURE".freeze
|
48
|
+
TIMESTAMP = "HTTP_X_TWILIO_EMAIL_EVENT_WEBHOOK_TIMESTAMP".freeze
|
49
|
+
end
|
50
|
+
end
|
@@ -17,7 +17,7 @@
|
|
17
17
|
# Installation
|
18
18
|
|
19
19
|
In addition to the installation instructions in
|
20
|
-
[the main readme](
|
20
|
+
[the main readme](../../../../README.md#installation),
|
21
21
|
you must also add sinatra to your Gemfile:
|
22
22
|
|
23
23
|
```
|
@@ -47,7 +47,7 @@ bundle install
|
|
47
47
|
ruby ./lib/sendgrid/helpers/inbound/send.rb ./lib/sendgrid/helpers/inbound/sample_data/default_data.txt
|
48
48
|
```
|
49
49
|
|
50
|
-
More sample data can be found [here](
|
50
|
+
More sample data can be found [here](sample_data).
|
51
51
|
|
52
52
|
View the results in the first terminal.
|
53
53
|
|
@@ -82,11 +82,11 @@ Next, send an email to [anything]@inbound.yourdomain.com, then look at the termi
|
|
82
82
|
|
83
83
|
## app.rb
|
84
84
|
|
85
|
-
This module runs a [Sinatra](http://www.sinatrarb.com/) server, that by default (you can change those settings [here](
|
85
|
+
This module runs a [Sinatra](http://www.sinatrarb.com/) server, that by default (you can change those settings [here](config.yml)), listens for POSTs on http://localhost:9292. When the server receives the POST, it parses and prints the key/value data.
|
86
86
|
|
87
87
|
## config.yml
|
88
88
|
|
89
|
-
This module loads application environment variables (located in [config.yml](
|
89
|
+
This module loads application environment variables (located in [config.yml](config.yml)).
|
90
90
|
|
91
91
|
## send.rb & /sample_data
|
92
92
|
|
@@ -95,4 +95,4 @@ This module is used to send sample test data. It is useful for testing and devel
|
|
95
95
|
<a name="contributing"></a>
|
96
96
|
# Contributing
|
97
97
|
|
98
|
-
If you would like to contribute to this project, please see our [contributing guide](
|
98
|
+
If you would like to contribute to this project, please see our [contributing guide](../../../../CONTRIBUTING.md). Thanks!
|