Skip to content

Commit

Permalink
Make function in Mailings.SenderAdapters.SES for caching keys public
Browse files Browse the repository at this point in the history
This allows for less code duplication in the SES adapter and webhook tests
and ensures well-defined behavior in the tests.
  • Loading branch information
wmnnd committed Aug 12, 2022
1 parent c8693ab commit 0db5581
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 28 deletions.
16 changes: 14 additions & 2 deletions lib/keila/mailings/sender_adapters/ses.ex
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,27 @@ defmodule Keila.Mailings.SenderAdapters.SES do
end
end

defp put_cached_key(url, key) do
@doc """
Put the key from the given URL in an in-memory cache.
This function is public to allow testing this module.
"""
@spec put_cached_key(String.t(), :public_key.public_key()) :: :ok
def put_cached_key(url, key) do
if is_nil(Process.whereis(__MODULE__.Cache)) do
Agent.start_link(fn -> %{} end, name: __MODULE__.Cache)
end

Agent.update(__MODULE__.Cache, &Map.put(&1, url, key))
end

defp extract_key(pem_file) do
@doc """
Extract the public key from a PEM file.
This function is public to allow testing this module.
"""
@spec extract_key(Path.t()) :: :public_key.public_key()
def extract_key(pem_file) do
pem_file
|> :public_key.pem_decode()
|> then(fn [{_, cert, _}] -> :public_key.pkix_decode_cert(cert, :otp) end)
Expand Down
18 changes: 18 additions & 0 deletions test/keila/mailings/ses/mailings_senders_ses_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@ defmodule Keila.Mailings.SendersSESTest do
use Keila.DataCase, async: true
alias Keila.Mailings

@aws_key "SimpleNotificationService-7ff5318490ec183fbaddaa2a969abfda.pem"

setup do
# Add AWS key to cache because otherwise test data might go stale when
# AWS cycles keys
Path.join(:code.priv_dir(:keila), "vendor/aws/#{@aws_key}")
|> File.read!()
|> Keila.Mailings.SenderAdapters.SES.extract_key()
|> then(fn key ->
Keila.Mailings.SenderAdapters.SES.put_cached_key(
"https://sns.eu-central-1.amazonaws.com/#{@aws_key}",
key
)
end)

:ok
end

@tag :mailings_ses
test "validate signature from SES/SNS notification" do
notification =
Expand Down
35 changes: 9 additions & 26 deletions test/keila_web/controllers/ses_webhook_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,21 @@ defmodule KeilaWeb.SESWebhookControllerTest do

# Add AWS key to cache because otherwise test data might go stale when
# AWS cycles keys
File.read!(Path.join(:code.priv_dir(:keila), "vendor/aws/#{@aws_key}"))
|> extract_key()
Path.join(:code.priv_dir(:keila), "vendor/aws/#{@aws_key}")
|> File.read!()
|> Keila.Mailings.SenderAdapters.SES.extract_key()
|> then(fn key ->
put_cached_key("https://sns.eu-central-1.amazonaws.com/#{@aws_key}", key)
Keila.Mailings.SenderAdapters.SES.put_cached_key(
"https://sns.eu-central-1.amazonaws.com/#{@aws_key}",
key
)
end)

%{project: project}
end

@tag :ses_webhook_controller
:ses_webhook_controller

test "handle bounces from SES", %{conn: conn, project: project} do
contact = insert!(:contact, project_id: project.id)
campaign = insert!(:mailings_campaign, project_id: project.id)
Expand Down Expand Up @@ -59,26 +64,4 @@ defmodule KeilaWeb.SESWebhookControllerTest do

assert 200 == conn.status
end

# Functions taken from Keila.Mailings.SenderAdapters.SES
defp put_cached_key(url, key) do
if is_nil(Process.whereis(Keila.Mailings.SenderAdapters.SES.Cache)) do
Agent.start_link(fn -> %{} end, name: Keila.Mailings.SenderAdapters.SES.Cache)
end

Agent.update(Keila.Mailings.SenderAdapters.SES.Cache, &Map.put(&1, url, key))
end

defp extract_key(pem_file) do
pem_file
|> :public_key.pem_decode()
|> then(fn [{_, cert, _}] -> :public_key.pkix_decode_cert(cert, :otp) end)
|> fetch_record_field(:OTPTBSCertificate)
|> fetch_record_field(:OTPSubjectPublicKeyInfo)
|> fetch_record_field(:RSAPublicKey)
end

defp fetch_record_field(record, key) do
record |> Tuple.to_list() |> Enum.find(fn el -> is_tuple(el) && elem(el, 0) == key end)
end
end

0 comments on commit 0db5581

Please sign in to comment.