1 defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
3 alias Pleroma.Web.ActivityPub.Transmogrifier
4 alias Pleroma.Web.ActivityPub.Utils
5 alias Pleroma.Web.ActivityPub.ActivityPub
6 alias Pleroma.Web.OStatus
10 alias Pleroma.Web.Websub.WebsubClientSubscription
12 import Pleroma.Factory
13 alias Pleroma.Web.CommonAPI
15 describe "handle_incoming" do
16 test "it ignores an incoming notice if we already have it" do
17 activity = insert(:note_activity)
20 File.read!("test/fixtures/mastodon-post-activity.json")
22 |> Map.put("object", activity.data["object"])
24 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
26 assert activity == returned_activity
29 test "it fetches replied-to activities if we don't have them" do
31 File.read!("test/fixtures/mastodon-post-activity.json")
36 |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873")
40 |> Map.put("object", object)
42 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
45 Activity.get_create_activity_by_object_ap_id(
46 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
49 assert returned_activity.data["object"]["inReplyToAtomUri"] ==
50 "https://shitposter.club/notice/2827873"
52 assert returned_activity.data["object"]["inReplyToStatusId"] == activity.id
55 test "it works for incoming notices" do
56 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
58 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
61 "http://mastodon.example.org/users/admin/statuses/99512778738411822/activity"
63 assert data["context"] ==
64 "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
66 assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
68 assert data["cc"] == [
69 "http://mastodon.example.org/users/admin/followers",
70 "http://localtesting.pleroma.lol/users/lain"
73 assert data["actor"] == "http://mastodon.example.org/users/admin"
75 object = data["object"]
76 assert object["id"] == "http://mastodon.example.org/users/admin/statuses/99512778738411822"
78 assert object["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
80 assert object["cc"] == [
81 "http://mastodon.example.org/users/admin/followers",
82 "http://localtesting.pleroma.lol/users/lain"
85 assert object["actor"] == "http://mastodon.example.org/users/admin"
86 assert object["attributedTo"] == "http://mastodon.example.org/users/admin"
88 assert object["context"] ==
89 "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
91 assert object["sensitive"] == true
93 user = User.get_by_ap_id(object["actor"])
95 assert user.info["note_count"] == 1
98 test "it works for incoming notices with hashtags" do
99 data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode!()
101 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
102 assert Enum.at(data["object"]["tag"], 2) == "moo"
105 test "it works for incoming notices with contentMap" do
107 File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Poison.decode!()
109 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
111 assert data["object"]["content"] ==
112 "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>"
115 test "it works for incoming notices with to/cc not being an array (kroeg)" do
116 data = File.read!("test/fixtures/kroeg-post-activity.json") |> Poison.decode!()
118 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
120 assert data["object"]["content"] ==
121 "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>"
124 test "it works for incoming announces with actor being inlined (kroeg)" do
125 data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!()
127 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
129 assert data["actor"] == "https://puckipedia.com/"
132 test "it works for incoming notices with tag not being an array (kroeg)" do
133 data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!()
135 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
137 assert data["object"]["emoji"] == %{
138 "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
141 data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!()
143 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
145 assert "test" in data["object"]["tag"]
148 test "it works for incoming notices with url not being a string (prismo)" do
149 data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!()
151 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
153 assert data["object"]["url"] == "https://prismo.news/posts/83"
156 test "it works for incoming follow requests" do
160 File.read!("test/fixtures/mastodon-follow-activity.json")
162 |> Map.put("object", user.ap_id)
164 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
166 assert data["actor"] == "http://mastodon.example.org/users/admin"
167 assert data["type"] == "Follow"
168 assert data["id"] == "http://mastodon.example.org/users/admin#follows/2"
169 assert User.following?(User.get_by_ap_id(data["actor"]), user)
172 test "it works for incoming follow requests from hubzilla" do
176 File.read!("test/fixtures/hubzilla-follow-activity.json")
178 |> Map.put("object", user.ap_id)
179 |> Utils.normalize_params()
181 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
183 assert data["actor"] == "https://hubzilla.example.org/channel/kaniini"
184 assert data["type"] == "Follow"
185 assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2"
186 assert User.following?(User.get_by_ap_id(data["actor"]), user)
189 test "it works for incoming likes" do
191 {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
194 File.read!("test/fixtures/mastodon-like.json")
196 |> Map.put("object", activity.data["object"]["id"])
198 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
200 assert data["actor"] == "http://mastodon.example.org/users/admin"
201 assert data["type"] == "Like"
202 assert data["id"] == "http://mastodon.example.org/users/admin#likes/2"
203 assert data["object"] == activity.data["object"]["id"]
206 test "it returns an error for incoming unlikes wihout a like activity" do
208 {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
211 File.read!("test/fixtures/mastodon-undo-like.json")
213 |> Map.put("object", activity.data["object"]["id"])
215 assert Transmogrifier.handle_incoming(data) == :error
218 test "it works for incoming unlikes with an existing like activity" do
220 {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
223 File.read!("test/fixtures/mastodon-like.json")
225 |> Map.put("object", activity.data["object"]["id"])
227 {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data)
230 File.read!("test/fixtures/mastodon-undo-like.json")
232 |> Map.put("object", like_data)
233 |> Map.put("actor", like_data["actor"])
235 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
237 assert data["actor"] == "http://mastodon.example.org/users/admin"
238 assert data["type"] == "Undo"
239 assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo"
240 assert data["object"]["id"] == "http://mastodon.example.org/users/admin#likes/2"
243 test "it works for incoming announces" do
244 data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
246 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
248 assert data["actor"] == "http://mastodon.example.org/users/admin"
249 assert data["type"] == "Announce"
252 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
254 assert data["object"] ==
255 "http://mastodon.example.org/users/admin/statuses/99541947525187367"
257 assert Activity.get_create_activity_by_object_ap_id(data["object"])
260 test "it works for incoming announces with an existing activity" do
262 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
265 File.read!("test/fixtures/mastodon-announce.json")
267 |> Map.put("object", activity.data["object"]["id"])
269 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
271 assert data["actor"] == "http://mastodon.example.org/users/admin"
272 assert data["type"] == "Announce"
275 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
277 assert data["object"] == activity.data["object"]["id"]
279 assert Activity.get_create_activity_by_object_ap_id(data["object"]).id == activity.id
282 test "it works for incoming update activities" do
283 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
285 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
286 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
289 update_data["object"]
290 |> Map.put("actor", data["actor"])
291 |> Map.put("id", data["actor"])
295 |> Map.put("actor", data["actor"])
296 |> Map.put("object", object)
298 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
300 user = User.get_cached_by_ap_id(data["actor"])
301 assert user.name == "gargle"
303 assert user.avatar["url"] == [
306 "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
310 assert user.info["banner"]["url"] == [
313 "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
317 assert user.bio == "<p>Some bio</p>"
320 test "it works for incoming update activities which lock the account" do
321 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
323 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
324 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
327 update_data["object"]
328 |> Map.put("actor", data["actor"])
329 |> Map.put("id", data["actor"])
330 |> Map.put("manuallyApprovesFollowers", true)
334 |> Map.put("actor", data["actor"])
335 |> Map.put("object", object)
337 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
339 user = User.get_cached_by_ap_id(data["actor"])
340 assert user.info["locked"] == true
343 test "it works for incoming deletes" do
344 activity = insert(:note_activity)
347 File.read!("test/fixtures/mastodon-delete.json")
352 |> Map.put("id", activity.data["object"]["id"])
356 |> Map.put("object", object)
357 |> Map.put("actor", activity.data["actor"])
359 {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
361 refute Repo.get(Activity, activity.id)
364 test "it fails for incoming deletes with spoofed origin" do
365 activity = insert(:note_activity)
368 File.read!("test/fixtures/mastodon-delete.json")
373 |> Map.put("id", activity.data["object"]["id"])
377 |> Map.put("object", object)
379 :error = Transmogrifier.handle_incoming(data)
381 assert Repo.get(Activity, activity.id)
384 test "it works for incoming unannounces with an existing notice" do
386 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
389 File.read!("test/fixtures/mastodon-announce.json")
391 |> Map.put("object", activity.data["object"]["id"])
393 {:ok, %Activity{data: announce_data, local: false}} =
394 Transmogrifier.handle_incoming(announce_data)
397 File.read!("test/fixtures/mastodon-undo-announce.json")
399 |> Map.put("object", announce_data)
400 |> Map.put("actor", announce_data["actor"])
402 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
404 assert data["type"] == "Undo"
405 assert data["object"]["type"] == "Announce"
406 assert data["object"]["object"] == activity.data["object"]["id"]
408 assert data["object"]["id"] ==
409 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
412 test "it works for incomming unfollows with an existing follow" do
416 File.read!("test/fixtures/mastodon-follow-activity.json")
418 |> Map.put("object", user.ap_id)
420 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data)
423 File.read!("test/fixtures/mastodon-unfollow-activity.json")
425 |> Map.put("object", follow_data)
427 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
429 assert data["type"] == "Undo"
430 assert data["object"]["type"] == "Follow"
431 assert data["object"]["object"] == user.ap_id
432 assert data["actor"] == "http://mastodon.example.org/users/admin"
434 refute User.following?(User.get_by_ap_id(data["actor"]), user)
437 test "it works for incoming blocks" do
441 File.read!("test/fixtures/mastodon-block-activity.json")
443 |> Map.put("object", user.ap_id)
445 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
447 assert data["type"] == "Block"
448 assert data["object"] == user.ap_id
449 assert data["actor"] == "http://mastodon.example.org/users/admin"
451 blocker = User.get_by_ap_id(data["actor"])
453 assert User.blocks?(blocker, user)
456 test "incoming blocks successfully tear down any follow relationship" do
457 blocker = insert(:user)
458 blocked = insert(:user)
461 File.read!("test/fixtures/mastodon-block-activity.json")
463 |> Map.put("object", blocked.ap_id)
464 |> Map.put("actor", blocker.ap_id)
466 {:ok, blocker} = User.follow(blocker, blocked)
467 {:ok, blocked} = User.follow(blocked, blocker)
469 assert User.following?(blocker, blocked)
470 assert User.following?(blocked, blocker)
472 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
474 assert data["type"] == "Block"
475 assert data["object"] == blocked.ap_id
476 assert data["actor"] == blocker.ap_id
478 blocker = User.get_by_ap_id(data["actor"])
479 blocked = User.get_by_ap_id(data["object"])
481 assert User.blocks?(blocker, blocked)
483 refute User.following?(blocker, blocked)
484 refute User.following?(blocked, blocker)
487 test "it works for incoming unblocks with an existing block" do
491 File.read!("test/fixtures/mastodon-block-activity.json")
493 |> Map.put("object", user.ap_id)
495 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data)
498 File.read!("test/fixtures/mastodon-unblock-activity.json")
500 |> Map.put("object", block_data)
502 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
503 assert data["type"] == "Undo"
504 assert data["object"]["type"] == "Block"
505 assert data["object"]["object"] == user.ap_id
506 assert data["actor"] == "http://mastodon.example.org/users/admin"
508 blocker = User.get_by_ap_id(data["actor"])
510 refute User.blocks?(blocker, user)
513 test "it works for incoming accepts which were pre-accepted" do
514 follower = insert(:user)
515 followed = insert(:user)
517 {:ok, follower} = User.follow(follower, followed)
518 assert User.following?(follower, followed) == true
520 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
523 File.read!("test/fixtures/mastodon-accept-activity.json")
525 |> Map.put("actor", followed.ap_id)
528 accept_data["object"]
529 |> Map.put("actor", follower.ap_id)
530 |> Map.put("id", follow_activity.data["id"])
532 accept_data = Map.put(accept_data, "object", object)
534 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
535 refute activity.local
537 assert activity.data["object"] == follow_activity.data["id"]
539 follower = Repo.get(User, follower.id)
541 assert User.following?(follower, followed) == true
544 test "it works for incoming accepts which were orphaned" do
545 follower = insert(:user)
546 followed = insert(:user, %{info: %{"locked" => true}})
548 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
551 File.read!("test/fixtures/mastodon-accept-activity.json")
553 |> Map.put("actor", followed.ap_id)
556 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
558 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
559 assert activity.data["object"] == follow_activity.data["id"]
561 follower = Repo.get(User, follower.id)
563 assert User.following?(follower, followed) == true
566 test "it works for incoming accepts which are referenced by IRI only" do
567 follower = insert(:user)
568 followed = insert(:user, %{info: %{"locked" => true}})
570 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
573 File.read!("test/fixtures/mastodon-accept-activity.json")
575 |> Map.put("actor", followed.ap_id)
576 |> Map.put("object", follow_activity.data["id"])
578 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
579 assert activity.data["object"] == follow_activity.data["id"]
581 follower = Repo.get(User, follower.id)
583 assert User.following?(follower, followed) == true
586 test "it fails for incoming accepts which cannot be correlated" do
587 follower = insert(:user)
588 followed = insert(:user, %{info: %{"locked" => true}})
591 File.read!("test/fixtures/mastodon-accept-activity.json")
593 |> Map.put("actor", followed.ap_id)
596 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
598 :error = Transmogrifier.handle_incoming(accept_data)
600 follower = Repo.get(User, follower.id)
602 refute User.following?(follower, followed) == true
605 test "it fails for incoming rejects which cannot be correlated" do
606 follower = insert(:user)
607 followed = insert(:user, %{info: %{"locked" => true}})
610 File.read!("test/fixtures/mastodon-reject-activity.json")
612 |> Map.put("actor", followed.ap_id)
615 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
617 :error = Transmogrifier.handle_incoming(accept_data)
619 follower = Repo.get(User, follower.id)
621 refute User.following?(follower, followed) == true
624 test "it works for incoming rejects which are orphaned" do
625 follower = insert(:user)
626 followed = insert(:user, %{info: %{"locked" => true}})
628 {:ok, follower} = User.follow(follower, followed)
629 {:ok, _follow_activity} = ActivityPub.follow(follower, followed)
631 assert User.following?(follower, followed) == true
634 File.read!("test/fixtures/mastodon-reject-activity.json")
636 |> Map.put("actor", followed.ap_id)
639 Map.put(reject_data, "object", Map.put(reject_data["object"], "actor", follower.ap_id))
641 {:ok, activity} = Transmogrifier.handle_incoming(reject_data)
642 refute activity.local
644 follower = Repo.get(User, follower.id)
646 assert User.following?(follower, followed) == false
649 test "it works for incoming rejects which are referenced by IRI only" do
650 follower = insert(:user)
651 followed = insert(:user, %{info: %{"locked" => true}})
653 {:ok, follower} = User.follow(follower, followed)
654 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
656 assert User.following?(follower, followed) == true
659 File.read!("test/fixtures/mastodon-reject-activity.json")
661 |> Map.put("actor", followed.ap_id)
662 |> Map.put("object", follow_activity.data["id"])
664 {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data)
666 follower = Repo.get(User, follower.id)
668 assert User.following?(follower, followed) == false
671 test "it rejects activities without a valid ID" do
675 File.read!("test/fixtures/mastodon-follow-activity.json")
677 |> Map.put("object", user.ap_id)
680 :error = Transmogrifier.handle_incoming(data)
684 describe "prepare outgoing" do
685 test "it turns mentions into tags" do
687 other_user = insert(:user)
690 CommonAPI.post(user, %{"status" => "hey, @#{other_user.nickname}, how are ya? #2hu"})
692 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
693 object = modified["object"]
695 expected_mention = %{
696 "href" => other_user.ap_id,
697 "name" => "@#{other_user.nickname}",
702 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
707 assert Enum.member?(object["tag"], expected_tag)
708 assert Enum.member?(object["tag"], expected_mention)
711 test "it adds the sensitive property" do
714 {:ok, activity} = CommonAPI.post(user, %{"status" => "#nsfw hey"})
715 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
717 assert modified["object"]["sensitive"]
720 test "it adds the json-ld context and the conversation property" do
723 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
724 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
726 assert modified["@context"] ==
727 Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"]
729 assert modified["object"]["conversation"] == modified["context"]
732 test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do
735 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
736 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
738 assert modified["object"]["actor"] == modified["object"]["attributedTo"]
741 test "it translates ostatus IDs to external URLs" do
742 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
743 {:ok, [referent_activity]} = OStatus.handle_incoming(incoming)
747 {:ok, activity, _} = CommonAPI.favorite(referent_activity.id, user)
748 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
750 assert modified["object"] == "http://gs.example.org:4040/index.php/notice/29"
753 test "it translates ostatus reply_to IDs to external URLs" do
754 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
755 {:ok, [referred_activity]} = OStatus.handle_incoming(incoming)
760 CommonAPI.post(user, %{"status" => "HI!", "in_reply_to_status_id" => referred_activity.id})
762 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
764 assert modified["object"]["inReplyTo"] == "http://gs.example.org:4040/index.php/notice/29"
767 test "it strips internal hashtag data" do
770 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu"})
773 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
778 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
780 assert modified["object"]["tag"] == [expected_tag]
783 test "it strips internal fields" do
786 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu :moominmamma:"})
788 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
790 assert length(modified["object"]["tag"]) == 2
792 assert is_nil(modified["object"]["emoji"])
793 assert is_nil(modified["object"]["likes"])
794 assert is_nil(modified["object"]["like_count"])
795 assert is_nil(modified["object"]["announcements"])
796 assert is_nil(modified["object"]["announcement_count"])
797 assert is_nil(modified["object"]["context_id"])
801 describe "user upgrade" do
802 test "it upgrades a user to activitypub" do
805 nickname: "rye@niu.moe",
807 ap_id: "https://niu.moe/users/rye",
808 follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
811 user_two = insert(:user, %{following: [user.follower_address]})
813 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
814 {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"})
815 assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
817 user = Repo.get(User, user.id)
818 assert user.info["note_count"] == 1
820 {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
821 assert user.info["ap_enabled"]
822 assert user.info["note_count"] == 1
823 assert user.follower_address == "https://niu.moe/users/rye/followers"
825 # Wait for the background task
828 user = Repo.get(User, user.id)
829 assert user.info["note_count"] == 1
831 activity = Repo.get(Activity, activity.id)
832 assert user.follower_address in activity.recipients
838 "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
847 "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
850 } = user.info["banner"]
852 refute "..." in activity.recipients
854 unrelated_activity = Repo.get(Activity, unrelated_activity.id)
855 refute user.follower_address in unrelated_activity.recipients
857 user_two = Repo.get(User, user_two.id)
858 assert user.follower_address in user_two.following
859 refute "..." in user_two.following
863 describe "maybe_retire_websub" do
864 test "it deletes all websub client subscripitions with the user as topic" do
865 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/rye.atom"}
866 {:ok, ws} = Repo.insert(subscription)
868 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/pasty.atom"}
869 {:ok, ws2} = Repo.insert(subscription)
871 Transmogrifier.maybe_retire_websub("https://niu.moe/users/rye")
873 refute Repo.get(WebsubClientSubscription, ws.id)
874 assert Repo.get(WebsubClientSubscription, ws2.id)
878 describe "actor rewriting" do
879 test "it fixes the actor URL property to be a proper URI" do
881 "url" => %{"href" => "http://example.com"}
884 rewritten = Transmogrifier.maybe_fix_user_object(data)
885 assert rewritten["url"] == "http://example.com"
889 describe "actor origin containment" do
890 test "it rejects objects with a bogus origin" do
891 {:error, _} = ActivityPub.fetch_object_from_id("https://info.pleroma.site/activity.json")
894 test "it rejects activities which reference objects with bogus origins" do
896 "@context" => "https://www.w3.org/ns/activitystreams",
897 "id" => "http://mastodon.example.org/users/admin/activities/1234",
898 "actor" => "http://mastodon.example.org/users/admin",
899 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
900 "object" => "https://info.pleroma.site/activity.json",
904 :error = Transmogrifier.handle_incoming(data)
907 test "it rejects objects when attributedTo is wrong (variant 1)" do
908 {:error, _} = ActivityPub.fetch_object_from_id("https://info.pleroma.site/activity2.json")
911 test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do
913 "@context" => "https://www.w3.org/ns/activitystreams",
914 "id" => "http://mastodon.example.org/users/admin/activities/1234",
915 "actor" => "http://mastodon.example.org/users/admin",
916 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
917 "object" => "https://info.pleroma.site/activity2.json",
921 :error = Transmogrifier.handle_incoming(data)
924 test "it rejects objects when attributedTo is wrong (variant 2)" do
925 {:error, _} = ActivityPub.fetch_object_from_id("https://info.pleroma.site/activity3.json")
928 test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do
930 "@context" => "https://www.w3.org/ns/activitystreams",
931 "id" => "http://mastodon.example.org/users/admin/activities/1234",
932 "actor" => "http://mastodon.example.org/users/admin",
933 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
934 "object" => "https://info.pleroma.site/activity3.json",
938 :error = Transmogrifier.handle_incoming(data)
942 describe "general origin containment" do
943 test "contain_origin_from_id() catches obvious spoofing attempts" do
945 "id" => "http://example.com/~alyssa/activities/1234.json"
949 Transmogrifier.contain_origin_from_id(
950 "http://example.org/~alyssa/activities/1234.json",
955 test "contain_origin_from_id() allows alternate IDs within the same origin domain" do
957 "id" => "http://example.com/~alyssa/activities/1234.json"
961 Transmogrifier.contain_origin_from_id(
962 "http://example.com/~alyssa/activities/1234",
967 test "contain_origin_from_id() allows matching IDs" do
969 "id" => "http://example.com/~alyssa/activities/1234.json"
973 Transmogrifier.contain_origin_from_id(
974 "http://example.com/~alyssa/activities/1234.json",
979 test "users cannot be collided through fake direction spoofing attempts" do
982 nickname: "rye@niu.moe",
984 ap_id: "https://niu.moe/users/rye",
985 follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
988 {:error, _} = User.get_or_fetch_by_ap_id("https://n1u.moe/users/rye")
991 test "all objects with fake directions are rejected by the object fetcher" do
993 ActivityPub.fetch_and_contain_remote_object_from_id(
994 "https://info.pleroma.site/activity4.json"