diff --git a/lib/activity_pub/headers.ex b/lib/activity_pub/headers.ex index bde3cdb..5ab22d9 100644 --- a/lib/activity_pub/headers.ex +++ b/lib/activity_pub/headers.ex @@ -10,6 +10,23 @@ defmodule ActivityPub.Headers do ] def signing_headers(method, url, body, actor_url, private_key) do + url = case url do + url when is_struct(url, URI) -> + url + url when is_binary(url) -> + URI.parse(url) + end + + private_key = case private_key do + "-----BEGIN" <> _ = key_pem -> + key_pem + |> :public_key.pem_decode() + |> hd() + |> :public_key.pem_entry_decode() + private_key -> + private_key + end + method = String.downcase("#{method}") date = DateTime.utc_now() |> Calendar.strftime(@http_date_format) host = url.host @@ -35,9 +52,11 @@ defmodule ActivityPub.Headers do end def verify(method, path, headers, actor_fetcher \\ &fetch_actor_key/1) do + dbg(headers) + {_key, signature_header} = Enum.find(headers, fn {key, _} -> key == "signature" end) - signature_kv = SignatureSplitter.split(signature_header) + signature_kv = SignatureSplitter.split(signature_header) |> dbg() key_id = find_value(signature_kv, "keyId") signature = signature_kv |> find_value("signature") |> Base.decode64!() signing_text_headers = signature_kv |> find_value("headers") |> String.split(" ") @@ -65,6 +84,7 @@ defmodule ActivityPub.Headers do value = find_value(header_pairs, header_name) "#{header_name}: #{value}\n" end) + |> String.trim() end def split_signature_header(signature_header) do @@ -79,6 +99,8 @@ defmodule ActivityPub.Headers do end defp find_value(headers, key) do + key = String.downcase(key) + Enum.find_value(headers, fn {key_candidate, value} -> String.downcase(key_candidate) == key && value end) @@ -90,7 +112,7 @@ defmodule ActivityPub.Headers do key_map = body["publicKey"] if key_map["id"] == key_id do - [public_key | _] = + public_key = :public_key.pem_decode(key_map["publicKeyPem"]) |> hd() |> :public_key.pem_entry_decode() diff --git a/lib/postland/activities.ex b/lib/postland/activities.ex index 14ede6c..2f9d8bd 100644 --- a/lib/postland/activities.ex +++ b/lib/postland/activities.ex @@ -26,6 +26,8 @@ defmodule Postland.Activities do def cause_effects(%Activity{actor_id: actor_id, type: "Accept", data: %{"object" => %{"type" => "Follow"}}} = activity) do case Follows.get(Postland.my_actor_id(), actor_id) do nil -> + # TODO: Need to handle the scenario where the we're following has an alias (/@foobar becomes /users/foobar by the time + # they Accept) Logger.warning("Got accept for a follow we don't have in the db: #{actor_id}") {:ok, activity} @@ -50,5 +52,5 @@ defmodule Postland.Activities do end end - def cause_effects(activity), do: activity + def cause_effects(activity), do: {:ok, activity} end diff --git a/lib/postland/follows.ex b/lib/postland/follows.ex index 4bdf3fd..a1ed31c 100644 --- a/lib/postland/follows.ex +++ b/lib/postland/follows.ex @@ -23,7 +23,7 @@ defmodule Postland.Follows do encoded_followee = Base.url_encode64(to_actor_id) encoded_follower = Base.url_encode64(Postland.my_actor_id()) - actor = ActivityPub.fetch_actor(to_actor_id) + {:ok, actor} = ActivityPub.fetch_actor(to_actor_id) inbox = Map.get(actor, "inbox") follow_request = %{ "@context" => "https://www.w3.org/ns/activitystreams", diff --git a/lib/postland_web/controllers/actor_controller.ex b/lib/postland_web/controllers/actor_controller.ex index 1404f7f..77d0743 100644 --- a/lib/postland_web/controllers/actor_controller.ex +++ b/lib/postland_web/controllers/actor_controller.ex @@ -2,6 +2,8 @@ defmodule PostlandWeb.ActorController do use PostlandWeb, :controller def get(conn, _params) do - render(conn, :actor, %{}) + conn + |> Plug.Conn.put_resp_header("content-type", "application/activity+json") + |> render(:actor, %{}) end end diff --git a/lib/postland_web/controllers/inbox_controller.ex b/lib/postland_web/controllers/inbox_controller.ex index 393cc74..75f5578 100644 --- a/lib/postland_web/controllers/inbox_controller.ex +++ b/lib/postland_web/controllers/inbox_controller.ex @@ -10,13 +10,13 @@ defmodule PostlandWeb.InboxController do if Headers.verify(conn.method, conn.request_path, conn.req_headers) do case Activities.process_activity(params) do {:ok, _activity} -> - render(conn, :ok) + Plug.Conn.send_resp(conn, 200, Jason.encode!(params)) error -> Logger.error(error) - render(conn, :unprocessable_entity) + Plug.Conn.send_resp(conn, 422, "unprocessable entity") end else - render(conn, :forbidden) + Plug.Conn.send_resp(conn, 403, "forbidden") end end end diff --git a/lib/postland_web/router.ex b/lib/postland_web/router.ex index 60a9c50..0d1d73c 100644 --- a/lib/postland_web/router.ex +++ b/lib/postland_web/router.ex @@ -27,7 +27,7 @@ defmodule PostlandWeb.Router do get "/.well-known/webfinger", WebfingerController, :get get "/actor", ActorController, :get - get "/inbox", InboxController, :post + post "/inbox", InboxController, :post get "/outbox", OutboxController, :get end