From d830a243a34b1c41ac81f368bce565285bd89ab7 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Thu, 27 Sep 2018 07:14:15 +0000 Subject: [PATCH] transmogrifier: more robustly handle dereferencing pointer URIs --- .../web/activity_pub/transmogrifier.ex | 53 ++++++++++-------- ...n.example.org_users_admin_status_1234.json | 55 +++++++++++++++++++ .../kroeg-announce-with-inline-actor.json | 2 +- test/support/httpoison_mock.ex | 11 ++++ test/web/activity_pub/transmogrifier_test.exs | 16 +++--- 5 files changed, 103 insertions(+), 34 deletions(-) create mode 100644 test/fixtures/httpoison_mock/http___mastodon.example.org_users_admin_status_1234.json diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 29d7b642b..611c0e623 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -114,7 +114,7 @@ def fix_in_reply_to(%{"inReplyTo" => in_reply_to} = object) "" end - case ActivityPub.fetch_object_from_id(in_reply_to_id) do + case fetch_obj_helper(in_reply_to_id) do {:ok, replied_object} -> with %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(replied_object.data["id"]) do @@ -323,7 +323,8 @@ defp get_follow_activity(follow_object, followed) do def handle_incoming( %{"type" => "Accept", "object" => follow_object, "actor" => actor, "id" => id} = data ) do - with %User{} = followed <- User.get_or_fetch_by_ap_id(actor), + with actor <- get_actor(data), + %User{} = followed <- User.get_or_fetch_by_ap_id(actor), {:ok, follow_activity} <- get_follow_activity(follow_object, followed), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), {:ok, activity} <- @@ -347,7 +348,8 @@ def handle_incoming( def handle_incoming( %{"type" => "Reject", "object" => follow_object, "actor" => actor, "id" => id} = data ) do - with %User{} = followed <- User.get_or_fetch_by_ap_id(actor), + with actor <- get_actor(data), + %User{} = followed <- User.get_or_fetch_by_ap_id(actor), {:ok, follow_activity} <- get_follow_activity(follow_object, followed), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), {:ok, activity} <- @@ -367,11 +369,11 @@ def handle_incoming( end def handle_incoming( - %{"type" => "Like", "object" => object_id, "actor" => actor, "id" => id} = _data + %{"type" => "Like", "object" => object_id, "actor" => actor, "id" => id} = data ) do - with %User{} = actor <- User.get_or_fetch_by_ap_id(actor), - {:ok, object} <- - get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), + with actor <- get_actor(data), + %User{} = actor <- User.get_or_fetch_by_ap_id(actor), + {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), {:ok, activity, _object} <- ActivityPub.like(actor, object, id, false) do {:ok, activity} else @@ -380,11 +382,11 @@ def handle_incoming( end def handle_incoming( - %{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = _data + %{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = data ) do - with %User{} = actor <- User.get_or_fetch_by_ap_id(actor), - {:ok, object} <- - get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), + with actor <- get_actor(data), + %User{} = actor <- User.get_or_fetch_by_ap_id(actor), + {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), {:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false) do {:ok, activity} else @@ -428,13 +430,13 @@ def handle_incoming( # TODO: Make secure. def handle_incoming( - %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => _id} = _data + %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => _id} = data ) do object_id = Utils.get_ap_id(object_id) - with %User{} = _actor <- User.get_or_fetch_by_ap_id(actor), - {:ok, object} <- - get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), + with actor <- get_actor(data), + %User{} = _actor <- User.get_or_fetch_by_ap_id(actor), + {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), {:ok, activity} <- ActivityPub.delete(object, false) do {:ok, activity} else @@ -448,11 +450,11 @@ def handle_incoming( "object" => %{"type" => "Announce", "object" => object_id}, "actor" => actor, "id" => id - } = _data + } = data ) do - with %User{} = actor <- User.get_or_fetch_by_ap_id(actor), - {:ok, object} <- - get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), + with actor <- get_actor(data), + %User{} = actor <- User.get_or_fetch_by_ap_id(actor), + {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), {:ok, activity, _} <- ActivityPub.unannounce(actor, object, id, false) do {:ok, activity} else @@ -521,11 +523,11 @@ def handle_incoming( "object" => %{"type" => "Like", "object" => object_id}, "actor" => actor, "id" => id - } = _data + } = data ) do - with %User{} = actor <- User.get_or_fetch_by_ap_id(actor), - {:ok, object} <- - get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), + with actor <- get_actor(data), + %User{} = actor <- User.get_or_fetch_by_ap_id(actor), + {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), {:ok, activity, _, _} <- ActivityPub.unlike(actor, object, id, false) do {:ok, activity} else @@ -535,6 +537,9 @@ def handle_incoming( def handle_incoming(_), do: :error + def fetch_obj_helper(id) when is_bitstring(id), do: ActivityPub.fetch_object_from_id(id) + def fetch_obj_helper(obj) when is_map(obj), do: ActivityPub.fetch_object_from_id(obj["id"]) + def get_obj_helper(id) do if object = Object.normalize(id), do: {:ok, object}, else: nil end @@ -630,7 +635,7 @@ def prepare_outgoing(%{"type" => _type} = data) do def maybe_fix_object_url(data) do if is_binary(data["object"]) and not String.starts_with?(data["object"], "http") do - case ActivityPub.fetch_object_from_id(data["object"]) do + case fetch_obj_helper(data["object"]) do {:ok, relative_object} -> if relative_object.data["external_url"] do _data = diff --git a/test/fixtures/httpoison_mock/http___mastodon.example.org_users_admin_status_1234.json b/test/fixtures/httpoison_mock/http___mastodon.example.org_users_admin_status_1234.json new file mode 100644 index 000000000..5c7c9c6d3 --- /dev/null +++ b/test/fixtures/httpoison_mock/http___mastodon.example.org_users_admin_status_1234.json @@ -0,0 +1,55 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "sensitive": "as:sensitive", + "movedTo": { + "@id": "as:movedTo", + "@type": "@id" + }, + "Hashtag": "as:Hashtag", + "ostatus": "http://ostatus.org#", + "atomUri": "ostatus:atomUri", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "toot": "http://joinmastodon.org/ns#", + "Emoji": "toot:Emoji", + "focalPoint": { + "@container": "@list", + "@id": "toot:focalPoint" + }, + "featured": { + "@id": "toot:featured", + "@type": "@id" + }, + "schema": "http://schema.org#", + "PropertyValue": "schema:PropertyValue", + "value": "schema:value" + } + ], + "id": "http://mastodon.example.org/users/admin/statuses/100787282858396771", + "type": "Note", + "summary": null, + "inReplyTo": null, + "published": "2018-09-25T16:11:29Z", + "url": "https://mastodon.example.org/@admin/100787282858396771", + "attributedTo": "http://mastodon.example.org/users/admin", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "http://mastodon.example.org/users/admin/followers" + ], + "sensitive": false, + "atomUri": "http://mastodon.example.org/users/admin/statuses/100787282858396771", + "inReplyToAtomUri": null, + "conversation": "tag:mastodon.social,2018-09-25:objectId=55659382:objectType=Conversation", + "content": "

the name's jond (jeans bond)

", + "contentMap": { + "en": "

the name's jond (jeans bond)

" + }, + "attachment": [], + "tag": [] +} diff --git a/test/fixtures/kroeg-announce-with-inline-actor.json b/test/fixtures/kroeg-announce-with-inline-actor.json index a2a7e2396..7bd6e8199 100644 --- a/test/fixtures/kroeg-announce-with-inline-actor.json +++ b/test/fixtures/kroeg-announce-with-inline-actor.json @@ -1 +1 @@ -{"@context":["https://www.w3.org/ns/activitystreams","https://puckipedia.com/-/context"],"actor":{"endpoints":"https://puckipedia.com/#endpoints","followers":"https://puckipedia.com/followers","following":"https://puckipedia.com/following","icon":{"mediaType":"image/png","type":"Image","url":"https://puckipedia.com/images/avatar.png"},"id":"https://puckipedia.com/","inbox":"https://puckipedia.com/inbox","kroeg:blocks":{"id":"https://puckipedia.com/blocks"},"liked":"https://puckipedia.com/liked","manuallyApprovesFollowers":false,"name":"HACKER TEEN PUCKIPEDIA 👩‍💻","outbox":"https://puckipedia.com/outbox","preferredUsername":"puckipedia","publicKey":{"id":"https://puckipedia.com/#key","owner":"https://puckipedia.com/","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvN05xIcFE0Qgany7Rht4\n0ZI5wu++IT7K5iSqRimBYkpoeHbVcT9RFlW+aWH/QJJW/YgZ7+LMr8AMCrKrwSpS\nCndyrpx4O4lZ3FNRLu7tbklh01rGZfE6R1SFfYBpvMvImc9nYT6iezYDbv6NkHku\no3aVhjql216XlA0OhIrqQme9sAdrLbjbMrTUS8douCTkDOX+JFj1ghHCqdYEMZJI\nOY9kovtgnqyxFLm0RsPGsO1+g/OVojqG+VqHz6O2lceaTVQLlnZ4gOhLVG1tVsA2\nRfXQK+R/VgXncYE+BlQVd/tcdGAz7CDL7PP3rP65gmARnafhGR96cCOi/KzlAXSO\nMwIDAQAB\n-----END PUBLIC KEY-----","type":[]},"summary":"

federated hacker teen
\n[she/they]

","type":"Person","updated":"2017-12-19T16:56:29.7576707+00:00"},"cc":"https://mastodon.social/users/revenant","id":"https://puckipedia.com/cc56a9658e","object":{"as:sensitive":false,"attributedTo":{"endpoints":{"sharedInbox":"https://mastodon.social/inbox","type":[]},"followers":"https://mastodon.social/users/revenant/followers","following":"https://mastodon.social/users/revenant/following","icon":{"mediaType":"image/png","type":"Image","url":"https://files.mastodon.social/accounts/avatars/000/015/163/original/70ca6c52b01ca913.png"},"id":"https://mastodon.social/users/revenant","inbox":"https://mastodon.social/users/revenant/inbox","manuallyApprovesFollowers":{"@value":"False","type":"xsd:boolean"},"name":"","outbox":"https://mastodon.social/users/revenant/outbox","preferredUsername":"revenant","publicKey":{"id":"https://mastodon.social/users/revenant#main-key","owner":"https://mastodon.social/users/revenant","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0gEN3wPW7gkE2gQqnmfB\n1ychjmFIf2LIwY0oCJLiGE/xpZrUKoq+eWH30AP7mATw4LD0gOYABL/ijqPUrPqR\nDXLL+0CqMP8HsZKvRlj9KArMK3YtNiSGGj2U7iReiRrD7nJzjJlsjjJXflLZhZ7/\nenSv1CcaeK8tB0PoAgShy/MyfhPF7WI5/Zm9DmmDQFvUEnDYKXAf/vG/IWw1EyMC\nkbaEYJeIowQU3GsbPxzRGI22bQtfotm431Ch2MbNo+kyzmYVFLAVoSGNMzvJwOPg\nTxLIIBeQXG7MinRyK887yPKhxhcALea4yCcALaa+3jPE7yqwIKYwTHtSlblsHDAo\nmQIDAQAB\n-----END PUBLIC KEY-----\n","type":[]},"summary":"

neatly partitioned meats and cheeses appeal to me on an aesthetic level | any pronouns | revenant1.net

","type":"Person","url":"https://mastodon.social/@revenant"},"cc":"https://mastodon.social/users/revenant/followers","content":"

the name's jond (jeans bond)

","contentMap":{"en":"

the name's jond (jeans bond)

"},"conversation":"tag:mastodon.social,2018-09-25:objectId=55659382:objectType=Conversation","id":"https://mastodon.social/users/revenant/statuses/100787282858396771","ostatus:atomUri":"https://mastodon.social/users/revenant/statuses/100787282858396771","published":"2018-09-25T16:11:29Z","to":"https://www.w3.org/ns/activitystreams#Public","type":"Note","url":"https://mastodon.social/@revenant/100787282858396771"},"to":["https://www.w3.org/ns/activitystreams#Public","https://puckipedia.com/followers"],"type":"Announce"} \ No newline at end of file +{"@context":["https://www.w3.org/ns/activitystreams","https://puckipedia.com/-/context"],"actor":{"endpoints":"https://puckipedia.com/#endpoints","followers":"https://puckipedia.com/followers","following":"https://puckipedia.com/following","icon":{"mediaType":"image/png","type":"Image","url":"https://puckipedia.com/images/avatar.png"},"id":"https://puckipedia.com/","inbox":"https://puckipedia.com/inbox","kroeg:blocks":{"id":"https://puckipedia.com/blocks"},"liked":"https://puckipedia.com/liked","manuallyApprovesFollowers":false,"name":"HACKER TEEN PUCKIPEDIA 👩‍💻","outbox":"https://puckipedia.com/outbox","preferredUsername":"puckipedia","publicKey":{"id":"https://puckipedia.com/#key","owner":"https://puckipedia.com/","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvN05xIcFE0Qgany7Rht4\n0ZI5wu++IT7K5iSqRimBYkpoeHbVcT9RFlW+aWH/QJJW/YgZ7+LMr8AMCrKrwSpS\nCndyrpx4O4lZ3FNRLu7tbklh01rGZfE6R1SFfYBpvMvImc9nYT6iezYDbv6NkHku\no3aVhjql216XlA0OhIrqQme9sAdrLbjbMrTUS8douCTkDOX+JFj1ghHCqdYEMZJI\nOY9kovtgnqyxFLm0RsPGsO1+g/OVojqG+VqHz6O2lceaTVQLlnZ4gOhLVG1tVsA2\nRfXQK+R/VgXncYE+BlQVd/tcdGAz7CDL7PP3rP65gmARnafhGR96cCOi/KzlAXSO\nMwIDAQAB\n-----END PUBLIC KEY-----","type":[]},"summary":"

federated hacker teen
\n[she/they]

","type":"Person","updated":"2017-12-19T16:56:29.7576707+00:00"},"cc":"http://mastodon.example.org/users/admin","id":"https://puckipedia.com/cc56a9658e","object":{"as:sensitive":false,"attributedTo":{"endpoints":{"sharedInbox":"https://mastodon.social/inbox","type":[]},"followers":"http://mastodon.example.org/users/admin/followers","following":"http://mastodon.example.org/users/admin/following","icon":{"mediaType":"image/png","type":"Image","url":"https://files.mastodon.social/accounts/avatars/000/015/163/original/70ca6c52b01ca913.png"},"id":"http://mastodon.example.org/users/admin","inbox":"http://mastodon.example.org/users/admin/inbox","manuallyApprovesFollowers":{"@value":"False","type":"xsd:boolean"},"name":"","outbox":"http://mastodon.example.org/users/admin/outbox","preferredUsername":"revenant","publicKey":{"id":"http://mastodon.example.org/users/admin#main-key","owner":"http://mastodon.example.org/users/admin","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0gEN3wPW7gkE2gQqnmfB\n1ychjmFIf2LIwY0oCJLiGE/xpZrUKoq+eWH30AP7mATw4LD0gOYABL/ijqPUrPqR\nDXLL+0CqMP8HsZKvRlj9KArMK3YtNiSGGj2U7iReiRrD7nJzjJlsjjJXflLZhZ7/\nenSv1CcaeK8tB0PoAgShy/MyfhPF7WI5/Zm9DmmDQFvUEnDYKXAf/vG/IWw1EyMC\nkbaEYJeIowQU3GsbPxzRGI22bQtfotm431Ch2MbNo+kyzmYVFLAVoSGNMzvJwOPg\nTxLIIBeQXG7MinRyK887yPKhxhcALea4yCcALaa+3jPE7yqwIKYwTHtSlblsHDAo\nmQIDAQAB\n-----END PUBLIC KEY-----\n","type":[]},"summary":"

neatly partitioned meats and cheeses appeal to me on an aesthetic level | any pronouns | revenant1.net

","type":"Person","url":"https://mastodon.social/@revenant"},"cc":"http://mastodon.example.org/users/admin/followers","content":"

the name's jond (jeans bond)

","contentMap":{"en":"

the name's jond (jeans bond)

"},"conversation":"tag:mastodon.social,2018-09-25:objectId=55659382:objectType=Conversation","id":"http://mastodon.example.org/users/admin/statuses/100787282858396771","ostatus:atomUri":"http://mastodon.example.org/users/admin/statuses/100787282858396771","published":"2018-09-25T16:11:29Z","to":"https://www.w3.org/ns/activitystreams#Public","type":"Note","url":"https://mastodon.social/@revenant/100787282858396771"},"to":["https://www.w3.org/ns/activitystreams#Public","https://puckipedia.com/followers"],"type":"Announce"} diff --git a/test/support/httpoison_mock.ex b/test/support/httpoison_mock.ex index 7057f30fb..d8b79abef 100644 --- a/test/support/httpoison_mock.ex +++ b/test/support/httpoison_mock.ex @@ -404,6 +404,17 @@ def post( }} end + def get("http://mastodon.example.org/users/admin/statuses/100787282858396771", _, _) do + {:ok, + %Response{ + status_code: 200, + body: + File.read!( + "test/fixtures/httpoison_mock/http___mastodon.example.org_users_admin_status_1234.json" + ) + }} + end + def get( "https://pawoo.net/.well-known/webfinger", [Accept: "application/xrd+xml,application/jrd+json"], diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 7b3c05caa..6a6f2a44c 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -121,15 +121,13 @@ test "it works for incoming notices with to/cc not being an array (kroeg)" do "

henlo from my Psion netBook

message sent from my Psion netBook

" end - # Broken ;/ - # - # test "it works for incoming announces with actor being inlined (kroeg)" do - # data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!() - # - # {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - # - # assert data["object"]["actor"] == "https://puckipedia.com/" - # end + test "it works for incoming announces with actor being inlined (kroeg)" do + data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "https://puckipedia.com/" + end test "it works for incoming notices with tag not being an array (kroeg)" do data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!()