Skip to content

Commit 252e68a

Browse files
authored
Merge pull request #13 from puretype/decode-webhook-body
deserialize JSON before passing to handler
2 parents a712326 + 56af9d7 commit 252e68a

File tree

2 files changed

+27
-9
lines changed

2 files changed

+27
-9
lines changed

lib/github_webhook.ex

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,24 @@ defmodule GitHubWebhook do
1414
"x-github-hook-installation-target-id" => :installation_target_id
1515
}
1616

17+
defmodule CacheBodyReader do
18+
@moduledoc false
19+
20+
def read_body(conn, opts) do
21+
{:ok, body, conn} = Plug.Conn.read_body(conn, opts)
22+
conn = update_in(conn.assigns[:raw_body], &[body | &1 || []])
23+
{:ok, body, conn}
24+
end
25+
end
26+
27+
@plug_parser Plug.Parsers.init(
28+
parsers: [:json],
29+
body_reader: {CacheBodyReader, :read_body, []},
30+
json_decoder: Application.compile_env(:github_webhook, :json_library, Jason)
31+
)
32+
33+
#
34+
1735
@impl true
1836
def init(options) do
1937
options
@@ -31,11 +49,12 @@ defmodule GitHubWebhook do
3149
secret = get_config(options, :secret)
3250
{module, function} = get_config(options, :action)
3351

34-
{:ok, payload, conn} = read_body(conn)
52+
conn = Plug.Parsers.call(conn, @plug_parser)
53+
3554
[signature_in_header] = get_req_header(conn, "x-hub-signature")
3655

37-
if verify_signature(payload, secret, signature_in_header) do
38-
apply(module, function, [conn, payload, request_header_opts(conn)])
56+
if verify_signature(conn.assigns.raw_body, secret, signature_in_header) do
57+
apply(module, function, [conn, conn.body_params, request_header_opts(conn)])
3958
conn |> send_resp(200, "OK") |> halt()
4059
else
4160
conn |> send_resp(403, "Forbidden") |> halt()

test/github_webhook_test.exs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ defmodule GitHubWebhookTest do
33
use Plug.Test
44

55
@test_body %{"hello" => "world"}
6-
@test_body_serialized Jason.encode!(@test_body)
76

87
# Demo plug with basic auth and a simple index action
98
defmodule DemoPlug do
@@ -40,7 +39,7 @@ defmodule GitHubWebhookTest do
4039
|> DemoPlug.call([])
4140

4241
assert conn.status == 200
43-
assert Process.get(:payload) == @test_body_serialized
42+
assert Process.get(:payload) == @test_body
4443
assert !Process.get(:next_in_chain_called)
4544
end
4645

@@ -60,7 +59,7 @@ defmodule GitHubWebhookTest do
6059
|> DemoPlug.call([])
6160

6261
assert conn.status == 200
63-
assert Process.get(:payload) == @test_body_serialized
62+
assert Process.get(:payload) == @test_body
6463
end
6564

6665
test "when path does not match, skips this plug and proceeds to next one" do
@@ -96,7 +95,7 @@ defmodule GitHubWebhookTest do
9695
|> DemoPlugParamPresendence.call([])
9796

9897
assert conn.status == 200
99-
assert Process.get(:payload) == @test_body_serialized
98+
assert Process.get(:payload) == @test_body
10099
end
101100

102101
test "when secret is not set in params, it uses application setting" do
@@ -118,7 +117,7 @@ defmodule GitHubWebhookTest do
118117
|> DemoPlugApplicationSecret.call([])
119118

120119
assert conn.status == 200
121-
assert Process.get(:payload) == @test_body_serialized
120+
assert Process.get(:payload) == @test_body
122121
end
123122

124123
test "when secret is not set in params or Application setting, it assumes an empty secret" do
@@ -140,7 +139,7 @@ defmodule GitHubWebhookTest do
140139
|> DemoPlugNoSecret.call([])
141140

142141
assert conn.status == 200
143-
assert Process.get(:payload) == @test_body_serialized
142+
assert Process.get(:payload) == @test_body
144143
end
145144

146145
defp gh_webhook_request(body \\ %{"hello" => "world"}, secret \\ "secret") do

0 commit comments

Comments
 (0)