diff --git a/lib/federation/activity_pub/publisher.ex b/lib/federation/activity_pub/publisher.ex index be92faf0..355d4ed1 100644 --- a/lib/federation/activity_pub/publisher.ex +++ b/lib/federation/activity_pub/publisher.ex @@ -45,8 +45,12 @@ defmodule Mobilizon.Federation.ActivityPub.Publisher do {recipients, followers} = convert_followers_in_recipients(recipients) + Logger.debug("Found the following followers: #{inspect(Enum.map(followers, & &1.url))}") + {recipients, members} = convert_members_in_recipients(recipients) + Logger.debug("Found the following followers: #{inspect(Enum.map(members, & &1.url))}") + remote_inboxes = (remote_actors(recipients) ++ followers ++ members) |> Enum.map(fn actor -> actor.shared_inbox_url || actor.inbox_url end) diff --git a/lib/federation/activity_pub/utils.ex b/lib/federation/activity_pub/utils.ex index e6977fe7..c19f7688 100644 --- a/lib/federation/activity_pub/utils.ex +++ b/lib/federation/activity_pub/utils.ex @@ -192,6 +192,8 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do _attributed_to ) when is_map(object) do + Logger.debug("Maybe relay if group activity (object is map)") + Logger.debug(inspect(object)) do_maybe_relay_if_group_activity(object, object["attributedTo"]) end @@ -201,10 +203,12 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do %Actor{url: attributed_to_url} ) when is_binary(object) and is_binary(attributed_to_url) do + Logger.debug("Maybe relay if group activity (object is binary)") do_maybe_relay_if_group_activity(object, attributed_to_url) end def maybe_relay_if_group_activity(_activity, _attributedTo) do + Logger.debug("Will not replay : not a group activity") :ok end @@ -214,6 +218,7 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do do: do_maybe_relay_if_group_activity(object, hd(attributed_to)) defp do_maybe_relay_if_group_activity(object, attributed_to) when is_binary(attributed_to) do + Logger.debug("Let's try to relay group activity") id = "#{Endpoint.url()}/announces/#{Ecto.UUID.generate()}" case Actors.get_local_group_by_url(attributed_to) do @@ -223,8 +228,9 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do Logger.info("Forwarded activity to external members of the group") :ok - {:error, _err} -> + {:error, err} -> Logger.info("Failed to forward activity to external members of the group") + Logger.debug(inspect(err)) :error end @@ -233,7 +239,9 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do end end - defp do_maybe_relay_if_group_activity(_, _), do: :ok + defp do_maybe_relay_if_group_activity(_, attributed_to) do + Logger.debug("Will not relay group activity, attributed to is : #{inspect(attributed_to)}") + end @spec remote_actors(list(String.t())) :: list(Actor.t()) def remote_actors(recipients) do @@ -439,18 +447,23 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do ) do {to, cc} = if public do + Logger.debug("Making announce data for a public object") + {[actor.followers_url, object_actor_url], ["https://www.w3.org/ns/activitystreams#Public"]} else + Logger.debug("Making announce data for a private object") + if actor_type == :Group do + Logger.debug("Making announce data for a group private object") + to = - (object["to"] || []) - |> MapSet.new() - |> MapSet.intersection(MapSet.new([actor.followers_url, actor.members_url])) - |> MapSet.to_list() + Map.get(object, "to", []) ++ + Map.get(object, "cc", []) ++ [actor.followers_url, actor.members_url] {to, []} else + Logger.debug("Making announce data for a private object") {[actor.followers_url], []} end end @@ -463,6 +476,11 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do "cc" => cc } + data = + if object["attributedTo"], + do: Map.put(data, "attributedTo", object["attributedTo"]), + else: data + if activity_id, do: Map.put(data, "id", activity_id), else: data end diff --git a/lib/federation/activity_stream/converter/comment.ex b/lib/federation/activity_stream/converter/comment.ex index eaa15ac0..77a02daf 100644 --- a/lib/federation/activity_stream/converter/comment.ex +++ b/lib/federation/activity_stream/converter/comment.ex @@ -196,6 +196,10 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Comment do |> Map.put(:origin_comment_id, origin_comment_id) |> Map.put(:discussion_id, discussion_id) + # Reply to a deleted entity + {:ok, %Mobilizon.Tombstone{}} -> + data + # Anything else is kind of a MP {:error, parent} -> Logger.warn("Parent object is something we don't handle") diff --git a/lib/federation/activity_stream/converter/event.ex b/lib/federation/activity_stream/converter/event.ex index 62801441..1ec250b1 100644 --- a/lib/federation/activity_stream/converter/event.ex +++ b/lib/federation/activity_stream/converter/event.ex @@ -107,7 +107,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do def model_to_as(%EventModel{} = event) do {to, cc} = if event.visibility == :public, - do: {[@ap_public], []}, + do: {[@ap_public], [event.organizer_actor.followers_url]}, else: {[attributed_to_or_default(event).followers_url], [@ap_public]} %{ diff --git a/lib/federation/http_signatures/signature.ex b/lib/federation/http_signatures/signature.ex index cf334487..ccb53203 100644 --- a/lib/federation/http_signatures/signature.ex +++ b/lib/federation/http_signatures/signature.ex @@ -10,7 +10,6 @@ defmodule Mobilizon.Federation.HTTPSignatures.Signature do @behaviour HTTPSignatures.Adapter - alias Mobilizon.Actors alias Mobilizon.Actors.Actor alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor @@ -53,16 +52,8 @@ defmodule Mobilizon.Federation.HTTPSignatures.Signature do {:ok, String.t()} | {:error, :actor_not_found | :pem_decode_error} defp get_public_key_for_url(url) do - case Actors.get_actor_by_url(url) do - {:ok, %Actor{} = actor} -> - get_actor_public_key(actor) - - {:error, :actor_not_found} -> - Logger.info( - "Unable to get actor with URL #{url} from local database, returning empty keys to trigger refreshment" - ) - - {:ok, ""} + with {:ok, %Actor{} = actor} <- ActivityPubActor.get_or_fetch_actor_by_url(url) do + get_actor_public_key(actor) end end diff --git a/lib/web/plugs/mapped_signature_to_identity.ex b/lib/web/plugs/mapped_signature_to_identity.ex index 351daba3..bf993423 100644 --- a/lib/web/plugs/mapped_signature_to_identity.ex +++ b/lib/web/plugs/mapped_signature_to_identity.ex @@ -10,8 +10,8 @@ defmodule Mobilizon.Web.Plugs.MappedSignatureToIdentity do import Plug.Conn + alias Mobilizon.Actors alias Mobilizon.Actors.Actor - alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.Utils alias Mobilizon.Federation.HTTPSignatures.Signature @@ -41,7 +41,7 @@ defmodule Mobilizon.Web.Plugs.MappedSignatureToIdentity do # We don't need to call refreshment here since # the Mobilizon.Federation.HTTPSignatures.Signature plug # should already have refreshed the actor if needed - ActivityPubActor.make_actor_from_url(key_actor_id, ignore_sign_object_fetches: true) + Actors.get_actor_by_url(key_actor_id) nil -> {:error, :no_key_in_conn} diff --git a/test/federation/activity_pub/transmogrifier_test.exs b/test/federation/activity_pub/transmogrifier_test.exs index 8f7ede5d..e584aeff 100644 --- a/test/federation/activity_pub/transmogrifier_test.exs +++ b/test/federation/activity_pub/transmogrifier_test.exs @@ -119,7 +119,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do assert object["to"] == ["https://www.w3.org/ns/activitystreams#Public"] - assert object["cc"] == [] + # assert object["cc"] == [] assert object["actor"] == "https://demo.gancio.org/federation/u/gancio" assert object["location"]["name"] == "Colosseo" @@ -146,11 +146,14 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do preferred_username: "member" ) + relay = Relay.get_actor() + with_mock ActivityPubActor, [:passthrough], get_or_fetch_actor_by_url: fn url -> case url do ^group_url -> {:ok, group} ^actor_url -> {:ok, actor} + "https://www.w3.org/ns/activitystreams#Public" -> {:ok, relay} end end do data = File.read!("test/fixtures/mobilizon-post-activity-group.json") |> Jason.decode!() diff --git a/test/federation/activity_pub/types/events_test.exs b/test/federation/activity_pub/types/events_test.exs index dad62333..99589f04 100644 --- a/test/federation/activity_pub/types/events_test.exs +++ b/test/federation/activity_pub/types/events_test.exs @@ -37,7 +37,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.EventsTest do "attachment" => [], "attributedTo" => ^actor_url, "category" => nil, - "cc" => [], + "cc" => [^followers_url], "commentsEnabled" => false, "content" => nil, "draft" => false, @@ -108,7 +108,8 @@ defmodule Mobilizon.Federation.ActivityPub.Types.EventsTest do end test "from a group member" do - %Actor{id: organizer_actor_id, url: actor_url} = actor = insert(:actor) + %Actor{id: organizer_actor_id, url: actor_url, followers_url: actor_followers_url} = + actor = insert(:actor) %Actor{ id: attributed_to_id, @@ -139,7 +140,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.EventsTest do "attachment" => [], "attributedTo" => ^group_url, "category" => nil, - "cc" => [], + "cc" => [^actor_followers_url], "commentsEnabled" => false, "content" => nil, "draft" => false, @@ -189,7 +190,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.EventsTest do "actor" => ^actor_url, "anonymousParticipationEnabled" => false, "attributedTo" => ^actor_url, - "cc" => [], + "cc" => [^followers_url], "commentsEnabled" => false, "draft" => false, "ical:status" => "CONFIRMED", @@ -212,7 +213,9 @@ defmodule Mobilizon.Federation.ActivityPub.Types.EventsTest do test "from a group member" do %Actor{} = actor_1 = insert(:actor) - %Actor{id: organizer_actor_2_id, url: actor_2_url} = actor_2 = insert(:actor) + + %Actor{id: organizer_actor_2_id, url: actor_2_url, followers_url: actor_followers_url} = + actor_2 = insert(:actor) %Actor{ url: group_url, @@ -247,7 +250,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.EventsTest do "actor" => ^actor_2_url, "anonymousParticipationEnabled" => false, "attributedTo" => ^group_url, - "cc" => [], + "cc" => [^actor_followers_url], "commentsEnabled" => false, "draft" => false, "ical:status" => "CONFIRMED", @@ -269,7 +272,9 @@ defmodule Mobilizon.Federation.ActivityPub.Types.EventsTest do end test "from a remote group member" do - %Actor{id: organizer_actor_1_id, url: actor_1_url} = actor_1 = insert(:actor) + %Actor{id: organizer_actor_1_id, url: actor_1_url, followers_url: actor_followers_url} = + actor_1 = insert(:actor) + %Actor{} = actor_2 = insert(:actor) %Actor{ @@ -305,7 +310,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.EventsTest do "actor" => ^actor_1_url, "anonymousParticipationEnabled" => false, "attributedTo" => ^group_url, - "cc" => [], + "cc" => [^actor_followers_url], "commentsEnabled" => false, "draft" => false, "ical:status" => "CONFIRMED", diff --git a/test/fixtures/signature/framapiaf_admin.json b/test/fixtures/signature/framapiaf_admin.json deleted file mode 100644 index 5f4f87b9..00000000 --- a/test/fixtures/signature/framapiaf_admin.json +++ /dev/null @@ -1,112 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - { - "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", - "toot": "http://joinmastodon.org/ns#", - "featured": { - "@id": "toot:featured", - "@type": "@id" - }, - "alsoKnownAs": { - "@id": "as:alsoKnownAs", - "@type": "@id" - }, - "movedTo": { - "@id": "as:movedTo", - "@type": "@id" - }, - "schema": "http://schema.org#", - "PropertyValue": "schema:PropertyValue", - "value": "schema:value", - "IdentityProof": "toot:IdentityProof", - "discoverable": "toot:discoverable", - "Device": "toot:Device", - "Ed25519Signature": "toot:Ed25519Signature", - "Ed25519Key": "toot:Ed25519Key", - "Curve25519Key": "toot:Curve25519Key", - "EncryptedMessage": "toot:EncryptedMessage", - "publicKeyBase64": "toot:publicKeyBase64", - "deviceId": "toot:deviceId", - "claim": { - "@type": "@id", - "@id": "toot:claim" - }, - "fingerprintKey": { - "@type": "@id", - "@id": "toot:fingerprintKey" - }, - "identityKey": { - "@type": "@id", - "@id": "toot:identityKey" - }, - "devices": { - "@type": "@id", - "@id": "toot:devices" - }, - "messageFranking": "toot:messageFranking", - "messageType": "toot:messageType", - "cipherText": "toot:cipherText", - "focalPoint": { - "@container": "@list", - "@id": "toot:focalPoint" - } - } - ], - "id": "https://framapiaf.org/users/admin", - "type": "Service", - "following": "https://framapiaf.org/users/admin/following", - "followers": "https://framapiaf.org/users/admin/followers", - "inbox": "https://framapiaf.org/users/admin/inbox", - "outbox": "https://framapiaf.org/users/admin/outbox", - "featured": "https://framapiaf.org/users/admin/collections/featured", - "preferredUsername": "admin", - "name": "Administrateur", - "summary": "
Je ne suis qu'un compte inutile. Merci nous de contacter via https://contact.framasoft.org/
", - "url": "https://framapiaf.org/@admin", - "manuallyApprovesFollowers": false, - "discoverable": null, - "devices": "https://framapiaf.org/users/admin/collections/devices", - "publicKey": { - "id": "https://framapiaf.org/users/admin#main-key", - "owner": "https://framapiaf.org/users/admin", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyHaU/AZ5dWtSxZXkPa89\nDUQ4z+JQHGGUG/xkGuq0v8P6qJfQqtHPBO5vH0IQJqluXWQS96gqTwjZnYevcpNA\nveYv0K25DWszx5Ehz6JX2/sSvu2rNUcQ3YZvSjdo/Yy1u5Fuc5lLmvw8uFzXYekD\nWovTMOnp4mIKpVEm/G/v4w8jvFEKw88h743vwaEIim88GEQItMxzGAV6zSqV1DWO\nLxtoRsinslJYfAG46ex4YUATFveWvOUeWk5W1sEa5f3c0moaTmBM/PAAo8vLxhlw\nJhsHihsCH+BcXKVMjW8OCqYYqISMxEifUBX63HcJt78ELHpOuc1c2eG59PomtTjQ\nywIDAQAB\n-----END PUBLIC KEY-----\n" - }, - "tag": [], - "attachment": [ - { - "type": "PropertyValue", - "name": "News", - "value": "@Framasoft" - }, - { - "type": "PropertyValue", - "name": "Support", - "value": "https://contact.framasoft.org/" - }, - { - "type": "PropertyValue", - "name": "Soutenir", - "value": "https://soutenir.framasoft.org/" - }, - { - "type": "PropertyValue", - "name": "Site", - "value": "https://framasoft.org/" - } - ], - "endpoints": { - "sharedInbox": "https://framapiaf.org/inbox" - }, - "icon": { - "type": "Image", - "mediaType": "image/jpeg", - "url": "https://framapiaf.s3.framasoft.org/framapiaf/accounts/avatars/000/000/002/original/85fbb27ad5e3cf71.jpg" - }, - "image": { - "type": "Image", - "mediaType": "image/jpeg", - "url": "https://framapiaf.s3.framasoft.org/framapiaf/accounts/headers/000/000/002/original/6aba75f1ab1ab6de.jpg" - } -} diff --git a/test/fixtures/signature/nyu_rye.json b/test/fixtures/signature/nyu_rye.json deleted file mode 100644 index a05fd4f9..00000000 --- a/test/fixtures/signature/nyu_rye.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - { - "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", - "toot": "http://joinmastodon.org/ns#", - "featured": { "@id": "toot:featured", "@type": "@id" }, - "alsoKnownAs": { "@id": "as:alsoKnownAs", "@type": "@id" }, - "movedTo": { "@id": "as:movedTo", "@type": "@id" }, - "schema": "http://schema.org#", - "PropertyValue": "schema:PropertyValue", - "value": "schema:value", - "IdentityProof": "toot:IdentityProof", - "discoverable": "toot:discoverable", - "Device": "toot:Device", - "Ed25519Signature": "toot:Ed25519Signature", - "Ed25519Key": "toot:Ed25519Key", - "Curve25519Key": "toot:Curve25519Key", - "EncryptedMessage": "toot:EncryptedMessage", - "publicKeyBase64": "toot:publicKeyBase64", - "deviceId": "toot:deviceId", - "claim": { "@type": "@id", "@id": "toot:claim" }, - "fingerprintKey": { "@type": "@id", "@id": "toot:fingerprintKey" }, - "identityKey": { "@type": "@id", "@id": "toot:identityKey" }, - "devices": { "@type": "@id", "@id": "toot:devices" }, - "messageFranking": "toot:messageFranking", - "messageType": "toot:messageType", - "cipherText": "toot:cipherText", - "focalPoint": { "@container": "@list", "@id": "toot:focalPoint" } - } - ], - "id": "https://niu.moe/users/rye", - "type": "Person", - "following": "https://niu.moe/users/rye/following", - "followers": "https://niu.moe/users/rye/followers", - "inbox": "https://niu.moe/users/rye/inbox", - "outbox": "https://niu.moe/users/rye/outbox", - "featured": "https://niu.moe/users/rye/collections/featured", - "preferredUsername": "rye", - "name": "♡ rye ♡", - "summary": "\\u003cp\\u003ecome back with a warrant\\u003c/p\\u003e", - "url": "https://niu.moe/@rye", - "manuallyApprovesFollowers": false, - "discoverable": false, - "devices": "https://niu.moe/users/rye/collections/devices", - "publicKey": { - "id": "https://niu.moe/users/rye#main-key", - "owner": "https://niu.moe/users/rye", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA83uRWjCFO35FwfA38mzv\\nEL0TUaXB7+2hYvPwNrn1WY6me5DRbqB5zzMrzWMGr0HSooqNqEYBafGsmVTWUqIk\\nKM9ehtIBraJI+mT5X7DPR3LrXOJF4a9EEslg8XvAk8MN9IrAhm6UljnvB67RtDcA\\nTNB01VWy9yWnxFRtz9o/EMoBPyw5giOaXE2ibVNP8lQIqGKuuBKPzPjSJygdvQ5q\\nxfow2z1TpKRqdsNDqn4n6U6zCXYTzkr0J71/tGw7fsgfv78l0Wjrc7EcuBk74OaG\\nC65UDiu3X4Q6kxCfCEhPSfuwLN+UZkzxcn6goWR0iYpWs57+4tFKu9nJYP4QJ0K9\\nTwIDAQAB\\n-----END PUBLIC KEY-----\\n" - }, - "tag": [], - "attachment": [], - "endpoints": { "sharedInbox": "https://niu.moe/inbox" } -} diff --git a/test/web/plugs/mapped_identity_to_signature_test.exs b/test/web/plugs/mapped_identity_to_signature_test.exs index f9607a1f..d948ec15 100644 --- a/test/web/plugs/mapped_identity_to_signature_test.exs +++ b/test/web/plugs/mapped_identity_to_signature_test.exs @@ -5,9 +5,8 @@ defmodule Mobilizon.Web.Plugs.MappedSignatureToIdentityTest do use Mobilizon.Web.ConnCase - import Mox + import Mobilizon.Factory - alias Mobilizon.Service.HTTP.ActivityPub.Mock alias Mobilizon.Web.Plugs.MappedSignatureToIdentity defp set_signature(conn, key_id) do @@ -16,30 +15,8 @@ defmodule Mobilizon.Web.Plugs.MappedSignatureToIdentityTest do |> assign(:valid_signature, true) end - defp framapiaf_admin do - "test/fixtures/signature/framapiaf_admin.json" - |> File.read!() - |> Jason.decode!() - end - - defp nyu_rye do - "test/fixtures/signature/nyu_rye.json" - |> File.read!() - |> Jason.decode!() - end - test "it successfully maps a valid identity with a valid signature" do - Mock - |> expect(:call, fn - %{method: :get, url: "https://framapiaf.org/users/admin"}, _opts -> - {:ok, %Tesla.Env{status: 200, body: framapiaf_admin()}} - end) - - Mock - |> expect(:call, fn - %{method: :get, url: "/doesntmattter"}, _opts -> - {:ok, %Tesla.Env{status: 200, body: ""}} - end) + insert(:actor, domain: "framapiaf.org", url: "https://framapiaf.org/users/admin") conn = build_conn(:get, "/doesntmattter") @@ -50,17 +27,7 @@ defmodule Mobilizon.Web.Plugs.MappedSignatureToIdentityTest do end test "it successfully maps a valid identity with a valid signature with payload" do - Mock - |> expect(:call, fn - %{method: :get, url: "https://framapiaf.org/users/admin"}, _opts -> - {:ok, %Tesla.Env{status: 200, body: framapiaf_admin()}} - end) - - Mock - |> expect(:call, fn - %{method: :post, url: "/doesntmattter"}, _opts -> - {:ok, %Tesla.Env{status: 200, body: ""}} - end) + insert(:actor, domain: "framapiaf.org", url: "https://framapiaf.org/users/admin") conn = build_conn(:post, "/doesntmattter", %{"actor" => "https://framapiaf.org/users/admin"}) @@ -71,17 +38,8 @@ defmodule Mobilizon.Web.Plugs.MappedSignatureToIdentityTest do end test "it considers a mapped identity to be invalid when it mismatches a payload" do - Mock - |> expect(:call, fn - %{method: :get, url: "https://niu.moe/users/rye"}, _opts -> - {:ok, %Tesla.Env{status: 200, body: nyu_rye()}} - end) - - Mock - |> expect(:call, fn - %{method: :post, url: "/doesntmattter"}, _opts -> - {:ok, %Tesla.Env{status: 200, body: ""}} - end) + insert(:actor, domain: "framapiaf.org", url: "https://framapiaf.org/users/admin") + insert(:actor, domain: "niu.moe", url: "https://niu.moe/users/rye") conn = build_conn(:post, "/doesntmattter", %{"actor" => "https://framapiaf.org/users/admin"}) @@ -91,19 +49,9 @@ defmodule Mobilizon.Web.Plugs.MappedSignatureToIdentityTest do assert %{valid_signature: false} == conn.assigns end - @tag skip: "Available again when lib/web/plugs/mapped_signature_to_identity.ex#62 is fixed" test "it considers a mapped identity to be invalid when the identity cannot be found" do - Mock - |> expect(:call, fn - %{method: :get, url: "https://mastodon.social/users/gargron"}, _opts -> - {:ok, %Tesla.Env{status: 404, body: ""}} - end) - - Mock - |> expect(:call, fn - %{method: :post, url: "/doesntmattter"}, _opts -> - {:ok, %Tesla.Env{status: 200, body: ""}} - end) + insert(:actor, domain: "framapiaf.org", url: "https://framapiaf.org/users/admin") + insert(:actor, domain: "mastodon.social", url: "https://mastodon.social/users/gargron") conn = build_conn(:post, "/doesntmattter", %{"actor" => "https://framapiaf.org/users/admin"})