Current section
Files
Jump to
Current section
Files
lib/webhoox/adapters/mandrill.ex
defmodule Webhoox.Adapter.Mandrill do
@moduledoc false
import Webhoox.Parser
@behaviour Webhoox.Adapter
@mandrill_header "x-mandrill-signature"
# Mandrill expects the webhook to respond to head req
def handle_webhook(conn = %{method: "HEAD"}, _, _opts), do: {:ok, conn}
def handle_webhook(conn, handler, opts) do
url = Keyword.fetch!(opts, :url)
secret = Keyword.fetch!(opts, :secret)
case verify_header(conn, url, secret) do
{:ok, conn} ->
json_decoder = Keyword.get(opts, :json_decoder, Jason)
conn.params
|> build_emails(json_decoder)
|> Enum.each(fn email ->
handler.process(email)
end)
{:ok, conn}
{:error, conn} ->
{:error, conn}
end
end
defp verify_header(conn, url, secret) do
signature = build_signature(url, conn.body_params, secret)
[expected] = Plug.Conn.get_req_header(conn, @mandrill_header)
if Plug.Crypto.secure_compare(signature, expected) do
{:ok, conn}
else
{:error, conn}
end
end
defp build_signature(url, body, secret) do
signed_data =
body
|> Map.to_list()
|> Enum.sort_by(fn {key, _value} -> key end)
|> Enum.reduce(url, fn {key, value}, acc ->
acc <> key <> value
end)
signature =
:crypto.mac(:hmac, :sha, secret, signed_data)
|> Base.encode64()
signature
end
defp build_emails(%{"mandrill_events" => events}, json_decoder) do
{:ok, events} = json_decoder.decode(events)
events
|> Enum.map(fn email_payload -> normalize_params(email_payload) end)
|> Enum.reject(&is_nil/1)
end
def normalize_params(
email = %{
"event" => event,
"msg" =>
msg = %{
"subject" => subject,
"to" => to,
"html" => html,
"text" => text,
"headers" => %{"Message-Id" => message_id}
},
"ts" => ts
}
) do
%Webhoox.Data.Email{
message_id: message_id,
event: event,
from: {msg["from_name"], msg["from_email"]},
subject: subject,
to: parse_recipients(to),
html: html,
text: text,
timestamp: parse_timestamp(ts),
raw_params: email
}
end
def normalize_params(_), do: nil
end