fix: Fix various bugs with following

This commit is contained in:
Ro 2024-10-10 02:01:30 +00:00
parent 90a158c380
commit 54bc877f0a
6 changed files with 35 additions and 9 deletions

View file

@ -10,6 +10,23 @@ defmodule ActivityPub.Headers do
] ]
def signing_headers(method, url, body, actor_url, private_key) 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}") method = String.downcase("#{method}")
date = DateTime.utc_now() |> Calendar.strftime(@http_date_format) date = DateTime.utc_now() |> Calendar.strftime(@http_date_format)
host = url.host host = url.host
@ -35,9 +52,11 @@ defmodule ActivityPub.Headers do
end end
def verify(method, path, headers, actor_fetcher \\ &fetch_actor_key/1) do 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) {_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") key_id = find_value(signature_kv, "keyId")
signature = signature_kv |> find_value("signature") |> Base.decode64!() signature = signature_kv |> find_value("signature") |> Base.decode64!()
signing_text_headers = signature_kv |> find_value("headers") |> String.split(" ") 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) value = find_value(header_pairs, header_name)
"#{header_name}: #{value}\n" "#{header_name}: #{value}\n"
end) end)
|> String.trim()
end end
def split_signature_header(signature_header) do def split_signature_header(signature_header) do
@ -79,6 +99,8 @@ defmodule ActivityPub.Headers do
end end
defp find_value(headers, key) do defp find_value(headers, key) do
key = String.downcase(key)
Enum.find_value(headers, fn {key_candidate, value} -> Enum.find_value(headers, fn {key_candidate, value} ->
String.downcase(key_candidate) == key && value String.downcase(key_candidate) == key && value
end) end)
@ -90,7 +112,7 @@ defmodule ActivityPub.Headers do
key_map = body["publicKey"] key_map = body["publicKey"]
if key_map["id"] == key_id do if key_map["id"] == key_id do
[public_key | _] = public_key =
:public_key.pem_decode(key_map["publicKeyPem"]) :public_key.pem_decode(key_map["publicKeyPem"])
|> hd() |> hd()
|> :public_key.pem_entry_decode() |> :public_key.pem_entry_decode()

View file

@ -26,6 +26,8 @@ defmodule Postland.Activities do
def cause_effects(%Activity{actor_id: actor_id, type: "Accept", data: %{"object" => %{"type" => "Follow"}}} = activity) 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 case Follows.get(Postland.my_actor_id(), actor_id) do
nil -> 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}") Logger.warning("Got accept for a follow we don't have in the db: #{actor_id}")
{:ok, activity} {:ok, activity}
@ -50,5 +52,5 @@ defmodule Postland.Activities do
end end
end end
def cause_effects(activity), do: activity def cause_effects(activity), do: {:ok, activity}
end end

View file

@ -23,7 +23,7 @@ defmodule Postland.Follows do
encoded_followee = Base.url_encode64(to_actor_id) encoded_followee = Base.url_encode64(to_actor_id)
encoded_follower = Base.url_encode64(Postland.my_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") inbox = Map.get(actor, "inbox")
follow_request = %{ follow_request = %{
"@context" => "https://www.w3.org/ns/activitystreams", "@context" => "https://www.w3.org/ns/activitystreams",

View file

@ -2,6 +2,8 @@ defmodule PostlandWeb.ActorController do
use PostlandWeb, :controller use PostlandWeb, :controller
def get(conn, _params) do def get(conn, _params) do
render(conn, :actor, %{}) conn
|> Plug.Conn.put_resp_header("content-type", "application/activity+json")
|> render(:actor, %{})
end end
end end

View file

@ -10,13 +10,13 @@ defmodule PostlandWeb.InboxController do
if Headers.verify(conn.method, conn.request_path, conn.req_headers) do if Headers.verify(conn.method, conn.request_path, conn.req_headers) do
case Activities.process_activity(params) do case Activities.process_activity(params) do
{:ok, _activity} -> {:ok, _activity} ->
render(conn, :ok) Plug.Conn.send_resp(conn, 200, Jason.encode!(params))
error -> error ->
Logger.error(error) Logger.error(error)
render(conn, :unprocessable_entity) Plug.Conn.send_resp(conn, 422, "unprocessable entity")
end end
else else
render(conn, :forbidden) Plug.Conn.send_resp(conn, 403, "forbidden")
end end
end end
end end

View file

@ -27,7 +27,7 @@ defmodule PostlandWeb.Router do
get "/.well-known/webfinger", WebfingerController, :get get "/.well-known/webfinger", WebfingerController, :get
get "/actor", ActorController, :get get "/actor", ActorController, :get
get "/inbox", InboxController, :post post "/inbox", InboxController, :post
get "/outbox", OutboxController, :get get "/outbox", OutboxController, :get
end end