Merge branch 'add-custom-emoji-test' into 'develop'
[akkoma] / test / web / activity_pub / transmogrifier_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
6 use Pleroma.DataCase
7 alias Pleroma.Web.ActivityPub.Transmogrifier
8 alias Pleroma.Web.ActivityPub.Utils
9 alias Pleroma.Web.ActivityPub.ActivityPub
10 alias Pleroma.Web.OStatus
11 alias Pleroma.Activity
12 alias Pleroma.User
13 alias Pleroma.Repo
14 alias Pleroma.Web.Websub.WebsubClientSubscription
15
16 import Pleroma.Factory
17 alias Pleroma.Web.CommonAPI
18
19 setup_all do
20 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
21 :ok
22 end
23
24 describe "handle_incoming" do
25 test "it ignores an incoming notice if we already have it" do
26 activity = insert(:note_activity)
27
28 data =
29 File.read!("test/fixtures/mastodon-post-activity.json")
30 |> Poison.decode!()
31 |> Map.put("object", activity.data["object"])
32
33 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
34
35 assert activity == returned_activity
36 end
37
38 test "it fetches replied-to activities if we don't have them" do
39 data =
40 File.read!("test/fixtures/mastodon-post-activity.json")
41 |> Poison.decode!()
42
43 object =
44 data["object"]
45 |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873")
46
47 data =
48 data
49 |> Map.put("object", object)
50
51 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
52
53 assert activity =
54 Activity.get_create_activity_by_object_ap_id(
55 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
56 )
57
58 assert returned_activity.data["object"]["inReplyToAtomUri"] ==
59 "https://shitposter.club/notice/2827873"
60
61 assert returned_activity.data["object"]["inReplyToStatusId"] == activity.id
62 end
63
64 test "it works for incoming notices" do
65 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
66
67 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
68
69 assert data["id"] ==
70 "http://mastodon.example.org/users/admin/statuses/99512778738411822/activity"
71
72 assert data["context"] ==
73 "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
74
75 assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
76
77 assert data["cc"] == [
78 "http://mastodon.example.org/users/admin/followers",
79 "http://localtesting.pleroma.lol/users/lain"
80 ]
81
82 assert data["actor"] == "http://mastodon.example.org/users/admin"
83
84 object = data["object"]
85 assert object["id"] == "http://mastodon.example.org/users/admin/statuses/99512778738411822"
86
87 assert object["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
88
89 assert object["cc"] == [
90 "http://mastodon.example.org/users/admin/followers",
91 "http://localtesting.pleroma.lol/users/lain"
92 ]
93
94 assert object["actor"] == "http://mastodon.example.org/users/admin"
95 assert object["attributedTo"] == "http://mastodon.example.org/users/admin"
96
97 assert object["context"] ==
98 "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
99
100 assert object["sensitive"] == true
101
102 user = User.get_by_ap_id(object["actor"])
103
104 assert user.info.note_count == 1
105 end
106
107 test "it works for incoming notices with hashtags" do
108 data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode!()
109
110 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
111 assert Enum.at(data["object"]["tag"], 2) == "moo"
112 end
113
114 test "it works for incoming notices with contentMap" do
115 data =
116 File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Poison.decode!()
117
118 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
119
120 assert data["object"]["content"] ==
121 "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>"
122 end
123
124 test "it works for incoming notices with to/cc not being an array (kroeg)" do
125 data = File.read!("test/fixtures/kroeg-post-activity.json") |> Poison.decode!()
126
127 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
128
129 assert data["object"]["content"] ==
130 "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>"
131 end
132
133 test "it works for incoming announces with actor being inlined (kroeg)" do
134 data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!()
135
136 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
137
138 assert data["actor"] == "https://puckipedia.com/"
139 end
140
141 test "it works for incoming notices with tag not being an array (kroeg)" do
142 data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!()
143
144 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
145
146 assert data["object"]["emoji"] == %{
147 "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
148 }
149
150 data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!()
151
152 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
153
154 assert "test" in data["object"]["tag"]
155 end
156
157 test "it works for incoming notices with url not being a string (prismo)" do
158 data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!()
159
160 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
161
162 assert data["object"]["url"] == "https://prismo.news/posts/83"
163 end
164
165 test "it works for incoming follow requests" do
166 user = insert(:user)
167
168 data =
169 File.read!("test/fixtures/mastodon-follow-activity.json")
170 |> Poison.decode!()
171 |> Map.put("object", user.ap_id)
172
173 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
174
175 assert data["actor"] == "http://mastodon.example.org/users/admin"
176 assert data["type"] == "Follow"
177 assert data["id"] == "http://mastodon.example.org/users/admin#follows/2"
178 assert User.following?(User.get_by_ap_id(data["actor"]), user)
179 end
180
181 test "it works for incoming follow requests from hubzilla" do
182 user = insert(:user)
183
184 data =
185 File.read!("test/fixtures/hubzilla-follow-activity.json")
186 |> Poison.decode!()
187 |> Map.put("object", user.ap_id)
188 |> Utils.normalize_params()
189
190 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
191
192 assert data["actor"] == "https://hubzilla.example.org/channel/kaniini"
193 assert data["type"] == "Follow"
194 assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2"
195 assert User.following?(User.get_by_ap_id(data["actor"]), user)
196 end
197
198 test "it works for incoming likes" do
199 user = insert(:user)
200 {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
201
202 data =
203 File.read!("test/fixtures/mastodon-like.json")
204 |> Poison.decode!()
205 |> Map.put("object", activity.data["object"]["id"])
206
207 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
208
209 assert data["actor"] == "http://mastodon.example.org/users/admin"
210 assert data["type"] == "Like"
211 assert data["id"] == "http://mastodon.example.org/users/admin#likes/2"
212 assert data["object"] == activity.data["object"]["id"]
213 end
214
215 test "it returns an error for incoming unlikes wihout a like activity" do
216 user = insert(:user)
217 {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
218
219 data =
220 File.read!("test/fixtures/mastodon-undo-like.json")
221 |> Poison.decode!()
222 |> Map.put("object", activity.data["object"]["id"])
223
224 assert Transmogrifier.handle_incoming(data) == :error
225 end
226
227 test "it works for incoming unlikes with an existing like activity" do
228 user = insert(:user)
229 {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
230
231 like_data =
232 File.read!("test/fixtures/mastodon-like.json")
233 |> Poison.decode!()
234 |> Map.put("object", activity.data["object"]["id"])
235
236 {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data)
237
238 data =
239 File.read!("test/fixtures/mastodon-undo-like.json")
240 |> Poison.decode!()
241 |> Map.put("object", like_data)
242 |> Map.put("actor", like_data["actor"])
243
244 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
245
246 assert data["actor"] == "http://mastodon.example.org/users/admin"
247 assert data["type"] == "Undo"
248 assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo"
249 assert data["object"]["id"] == "http://mastodon.example.org/users/admin#likes/2"
250 end
251
252 test "it works for incoming announces" do
253 data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
254
255 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
256
257 assert data["actor"] == "http://mastodon.example.org/users/admin"
258 assert data["type"] == "Announce"
259
260 assert data["id"] ==
261 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
262
263 assert data["object"] ==
264 "http://mastodon.example.org/users/admin/statuses/99541947525187367"
265
266 assert Activity.get_create_activity_by_object_ap_id(data["object"])
267 end
268
269 test "it works for incoming announces with an existing activity" do
270 user = insert(:user)
271 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
272
273 data =
274 File.read!("test/fixtures/mastodon-announce.json")
275 |> Poison.decode!()
276 |> Map.put("object", activity.data["object"]["id"])
277
278 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
279
280 assert data["actor"] == "http://mastodon.example.org/users/admin"
281 assert data["type"] == "Announce"
282
283 assert data["id"] ==
284 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
285
286 assert data["object"] == activity.data["object"]["id"]
287
288 assert Activity.get_create_activity_by_object_ap_id(data["object"]).id == activity.id
289 end
290
291 test "it does not clobber the addressing on announce activities" do
292 user = insert(:user)
293 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
294
295 data =
296 File.read!("test/fixtures/mastodon-announce.json")
297 |> Poison.decode!()
298 |> Map.put("object", activity.data["object"]["id"])
299 |> Map.put("to", ["http://mastodon.example.org/users/admin/followers"])
300 |> Map.put("cc", [])
301
302 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
303
304 assert data["to"] == ["http://mastodon.example.org/users/admin/followers"]
305 end
306
307 test "it works for incoming update activities" do
308 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
309
310 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
311 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
312
313 object =
314 update_data["object"]
315 |> Map.put("actor", data["actor"])
316 |> Map.put("id", data["actor"])
317
318 update_data =
319 update_data
320 |> Map.put("actor", data["actor"])
321 |> Map.put("object", object)
322
323 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
324
325 user = User.get_cached_by_ap_id(data["actor"])
326 assert user.name == "gargle"
327
328 assert user.avatar["url"] == [
329 %{
330 "href" =>
331 "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
332 }
333 ]
334
335 assert user.info.banner["url"] == [
336 %{
337 "href" =>
338 "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
339 }
340 ]
341
342 assert user.bio == "<p>Some bio</p>"
343 end
344
345 test "it works for incoming update activities which lock the account" do
346 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
347
348 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
349 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
350
351 object =
352 update_data["object"]
353 |> Map.put("actor", data["actor"])
354 |> Map.put("id", data["actor"])
355 |> Map.put("manuallyApprovesFollowers", true)
356
357 update_data =
358 update_data
359 |> Map.put("actor", data["actor"])
360 |> Map.put("object", object)
361
362 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
363
364 user = User.get_cached_by_ap_id(data["actor"])
365 assert user.info.locked == true
366 end
367
368 test "it works for incoming deletes" do
369 activity = insert(:note_activity)
370
371 data =
372 File.read!("test/fixtures/mastodon-delete.json")
373 |> Poison.decode!()
374
375 object =
376 data["object"]
377 |> Map.put("id", activity.data["object"]["id"])
378
379 data =
380 data
381 |> Map.put("object", object)
382 |> Map.put("actor", activity.data["actor"])
383
384 {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
385
386 refute Repo.get(Activity, activity.id)
387 end
388
389 test "it fails for incoming deletes with spoofed origin" do
390 activity = insert(:note_activity)
391
392 data =
393 File.read!("test/fixtures/mastodon-delete.json")
394 |> Poison.decode!()
395
396 object =
397 data["object"]
398 |> Map.put("id", activity.data["object"]["id"])
399
400 data =
401 data
402 |> Map.put("object", object)
403
404 :error = Transmogrifier.handle_incoming(data)
405
406 assert Repo.get(Activity, activity.id)
407 end
408
409 test "it works for incoming unannounces with an existing notice" do
410 user = insert(:user)
411 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
412
413 announce_data =
414 File.read!("test/fixtures/mastodon-announce.json")
415 |> Poison.decode!()
416 |> Map.put("object", activity.data["object"]["id"])
417
418 {:ok, %Activity{data: announce_data, local: false}} =
419 Transmogrifier.handle_incoming(announce_data)
420
421 data =
422 File.read!("test/fixtures/mastodon-undo-announce.json")
423 |> Poison.decode!()
424 |> Map.put("object", announce_data)
425 |> Map.put("actor", announce_data["actor"])
426
427 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
428
429 assert data["type"] == "Undo"
430 assert data["object"]["type"] == "Announce"
431 assert data["object"]["object"] == activity.data["object"]["id"]
432
433 assert data["object"]["id"] ==
434 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
435 end
436
437 test "it works for incomming unfollows with an existing follow" do
438 user = insert(:user)
439
440 follow_data =
441 File.read!("test/fixtures/mastodon-follow-activity.json")
442 |> Poison.decode!()
443 |> Map.put("object", user.ap_id)
444
445 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data)
446
447 data =
448 File.read!("test/fixtures/mastodon-unfollow-activity.json")
449 |> Poison.decode!()
450 |> Map.put("object", follow_data)
451
452 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
453
454 assert data["type"] == "Undo"
455 assert data["object"]["type"] == "Follow"
456 assert data["object"]["object"] == user.ap_id
457 assert data["actor"] == "http://mastodon.example.org/users/admin"
458
459 refute User.following?(User.get_by_ap_id(data["actor"]), user)
460 end
461
462 test "it works for incoming blocks" do
463 user = insert(:user)
464
465 data =
466 File.read!("test/fixtures/mastodon-block-activity.json")
467 |> Poison.decode!()
468 |> Map.put("object", user.ap_id)
469
470 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
471
472 assert data["type"] == "Block"
473 assert data["object"] == user.ap_id
474 assert data["actor"] == "http://mastodon.example.org/users/admin"
475
476 blocker = User.get_by_ap_id(data["actor"])
477
478 assert User.blocks?(blocker, user)
479 end
480
481 test "incoming blocks successfully tear down any follow relationship" do
482 blocker = insert(:user)
483 blocked = insert(:user)
484
485 data =
486 File.read!("test/fixtures/mastodon-block-activity.json")
487 |> Poison.decode!()
488 |> Map.put("object", blocked.ap_id)
489 |> Map.put("actor", blocker.ap_id)
490
491 {:ok, blocker} = User.follow(blocker, blocked)
492 {:ok, blocked} = User.follow(blocked, blocker)
493
494 assert User.following?(blocker, blocked)
495 assert User.following?(blocked, blocker)
496
497 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
498
499 assert data["type"] == "Block"
500 assert data["object"] == blocked.ap_id
501 assert data["actor"] == blocker.ap_id
502
503 blocker = User.get_by_ap_id(data["actor"])
504 blocked = User.get_by_ap_id(data["object"])
505
506 assert User.blocks?(blocker, blocked)
507
508 refute User.following?(blocker, blocked)
509 refute User.following?(blocked, blocker)
510 end
511
512 test "it works for incoming unblocks with an existing block" do
513 user = insert(:user)
514
515 block_data =
516 File.read!("test/fixtures/mastodon-block-activity.json")
517 |> Poison.decode!()
518 |> Map.put("object", user.ap_id)
519
520 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data)
521
522 data =
523 File.read!("test/fixtures/mastodon-unblock-activity.json")
524 |> Poison.decode!()
525 |> Map.put("object", block_data)
526
527 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
528 assert data["type"] == "Undo"
529 assert data["object"]["type"] == "Block"
530 assert data["object"]["object"] == user.ap_id
531 assert data["actor"] == "http://mastodon.example.org/users/admin"
532
533 blocker = User.get_by_ap_id(data["actor"])
534
535 refute User.blocks?(blocker, user)
536 end
537
538 test "it works for incoming accepts which were pre-accepted" do
539 follower = insert(:user)
540 followed = insert(:user)
541
542 {:ok, follower} = User.follow(follower, followed)
543 assert User.following?(follower, followed) == true
544
545 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
546
547 accept_data =
548 File.read!("test/fixtures/mastodon-accept-activity.json")
549 |> Poison.decode!()
550 |> Map.put("actor", followed.ap_id)
551
552 object =
553 accept_data["object"]
554 |> Map.put("actor", follower.ap_id)
555 |> Map.put("id", follow_activity.data["id"])
556
557 accept_data = Map.put(accept_data, "object", object)
558
559 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
560 refute activity.local
561
562 assert activity.data["object"] == follow_activity.data["id"]
563
564 follower = Repo.get(User, follower.id)
565
566 assert User.following?(follower, followed) == true
567 end
568
569 test "it works for incoming accepts which were orphaned" do
570 follower = insert(:user)
571 followed = insert(:user, %{info: %User.Info{locked: true}})
572
573 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
574
575 accept_data =
576 File.read!("test/fixtures/mastodon-accept-activity.json")
577 |> Poison.decode!()
578 |> Map.put("actor", followed.ap_id)
579
580 accept_data =
581 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
582
583 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
584 assert activity.data["object"] == follow_activity.data["id"]
585
586 follower = Repo.get(User, follower.id)
587
588 assert User.following?(follower, followed) == true
589 end
590
591 test "it works for incoming accepts which are referenced by IRI only" do
592 follower = insert(:user)
593 followed = insert(:user, %{info: %User.Info{locked: true}})
594
595 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
596
597 accept_data =
598 File.read!("test/fixtures/mastodon-accept-activity.json")
599 |> Poison.decode!()
600 |> Map.put("actor", followed.ap_id)
601 |> Map.put("object", follow_activity.data["id"])
602
603 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
604 assert activity.data["object"] == follow_activity.data["id"]
605
606 follower = Repo.get(User, follower.id)
607
608 assert User.following?(follower, followed) == true
609 end
610
611 test "it fails for incoming accepts which cannot be correlated" do
612 follower = insert(:user)
613 followed = insert(:user, %{info: %User.Info{locked: true}})
614
615 accept_data =
616 File.read!("test/fixtures/mastodon-accept-activity.json")
617 |> Poison.decode!()
618 |> Map.put("actor", followed.ap_id)
619
620 accept_data =
621 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
622
623 :error = Transmogrifier.handle_incoming(accept_data)
624
625 follower = Repo.get(User, follower.id)
626
627 refute User.following?(follower, followed) == true
628 end
629
630 test "it fails for incoming rejects which cannot be correlated" do
631 follower = insert(:user)
632 followed = insert(:user, %{info: %User.Info{locked: true}})
633
634 accept_data =
635 File.read!("test/fixtures/mastodon-reject-activity.json")
636 |> Poison.decode!()
637 |> Map.put("actor", followed.ap_id)
638
639 accept_data =
640 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
641
642 :error = Transmogrifier.handle_incoming(accept_data)
643
644 follower = Repo.get(User, follower.id)
645
646 refute User.following?(follower, followed) == true
647 end
648
649 test "it works for incoming rejects which are orphaned" do
650 follower = insert(:user)
651 followed = insert(:user, %{info: %User.Info{locked: true}})
652
653 {:ok, follower} = User.follow(follower, followed)
654 {:ok, _follow_activity} = ActivityPub.follow(follower, followed)
655
656 assert User.following?(follower, followed) == true
657
658 reject_data =
659 File.read!("test/fixtures/mastodon-reject-activity.json")
660 |> Poison.decode!()
661 |> Map.put("actor", followed.ap_id)
662
663 reject_data =
664 Map.put(reject_data, "object", Map.put(reject_data["object"], "actor", follower.ap_id))
665
666 {:ok, activity} = Transmogrifier.handle_incoming(reject_data)
667 refute activity.local
668
669 follower = Repo.get(User, follower.id)
670
671 assert User.following?(follower, followed) == false
672 end
673
674 test "it works for incoming rejects which are referenced by IRI only" do
675 follower = insert(:user)
676 followed = insert(:user, %{info: %User.Info{locked: true}})
677
678 {:ok, follower} = User.follow(follower, followed)
679 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
680
681 assert User.following?(follower, followed) == true
682
683 reject_data =
684 File.read!("test/fixtures/mastodon-reject-activity.json")
685 |> Poison.decode!()
686 |> Map.put("actor", followed.ap_id)
687 |> Map.put("object", follow_activity.data["id"])
688
689 {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data)
690
691 follower = Repo.get(User, follower.id)
692
693 assert User.following?(follower, followed) == false
694 end
695
696 test "it rejects activities without a valid ID" do
697 user = insert(:user)
698
699 data =
700 File.read!("test/fixtures/mastodon-follow-activity.json")
701 |> Poison.decode!()
702 |> Map.put("object", user.ap_id)
703 |> Map.put("id", "")
704
705 :error = Transmogrifier.handle_incoming(data)
706 end
707
708 test "it remaps video URLs as attachments if necessary" do
709 {:ok, object} =
710 ActivityPub.fetch_object_from_id(
711 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
712 )
713
714 attachment = %{
715 "type" => "Link",
716 "mediaType" => "video/mp4",
717 "href" =>
718 "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
719 "mimeType" => "video/mp4",
720 "size" => 5_015_880,
721 "url" => [
722 %{
723 "href" =>
724 "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
725 "mediaType" => "video/mp4",
726 "type" => "Link"
727 }
728 ],
729 "width" => 480
730 }
731
732 assert object.data["url"] ==
733 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
734
735 assert object.data["attachment"] == [attachment]
736 end
737 end
738
739 describe "prepare outgoing" do
740 test "it turns mentions into tags" do
741 user = insert(:user)
742 other_user = insert(:user)
743
744 {:ok, activity} =
745 CommonAPI.post(user, %{"status" => "hey, @#{other_user.nickname}, how are ya? #2hu"})
746
747 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
748 object = modified["object"]
749
750 expected_mention = %{
751 "href" => other_user.ap_id,
752 "name" => "@#{other_user.nickname}",
753 "type" => "Mention"
754 }
755
756 expected_tag = %{
757 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
758 "type" => "Hashtag",
759 "name" => "#2hu"
760 }
761
762 assert Enum.member?(object["tag"], expected_tag)
763 assert Enum.member?(object["tag"], expected_mention)
764 end
765
766 test "it adds the sensitive property" do
767 user = insert(:user)
768
769 {:ok, activity} = CommonAPI.post(user, %{"status" => "#nsfw hey"})
770 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
771
772 assert modified["object"]["sensitive"]
773 end
774
775 test "it adds the json-ld context and the conversation property" do
776 user = insert(:user)
777
778 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
779 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
780
781 assert modified["@context"] ==
782 Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"]
783
784 assert modified["object"]["conversation"] == modified["context"]
785 end
786
787 test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do
788 user = insert(:user)
789
790 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
791 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
792
793 assert modified["object"]["actor"] == modified["object"]["attributedTo"]
794 end
795
796 test "it translates ostatus IDs to external URLs" do
797 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
798 {:ok, [referent_activity]} = OStatus.handle_incoming(incoming)
799
800 user = insert(:user)
801
802 {:ok, activity, _} = CommonAPI.favorite(referent_activity.id, user)
803 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
804
805 assert modified["object"] == "http://gs.example.org:4040/index.php/notice/29"
806 end
807
808 test "it translates ostatus reply_to IDs to external URLs" do
809 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
810 {:ok, [referred_activity]} = OStatus.handle_incoming(incoming)
811
812 user = insert(:user)
813
814 {:ok, activity} =
815 CommonAPI.post(user, %{"status" => "HI!", "in_reply_to_status_id" => referred_activity.id})
816
817 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
818
819 assert modified["object"]["inReplyTo"] == "http://gs.example.org:4040/index.php/notice/29"
820 end
821
822 test "it strips internal hashtag data" do
823 user = insert(:user)
824
825 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu"})
826
827 expected_tag = %{
828 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
829 "type" => "Hashtag",
830 "name" => "#2hu"
831 }
832
833 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
834
835 assert modified["object"]["tag"] == [expected_tag]
836 end
837
838 test "it strips internal fields" do
839 user = insert(:user)
840
841 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu :moominmamma:"})
842
843 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
844
845 assert length(modified["object"]["tag"]) == 2
846
847 assert is_nil(modified["object"]["emoji"])
848 assert is_nil(modified["object"]["like_count"])
849 assert is_nil(modified["object"]["announcements"])
850 assert is_nil(modified["object"]["announcement_count"])
851 assert is_nil(modified["object"]["context_id"])
852 end
853
854 test "it strips internal fields of article" do
855 activity = insert(:article_activity)
856
857 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
858
859 assert length(modified["object"]["tag"]) == 2
860
861 assert is_nil(modified["object"]["emoji"])
862 assert is_nil(modified["object"]["like_count"])
863 assert is_nil(modified["object"]["announcements"])
864 assert is_nil(modified["object"]["announcement_count"])
865 assert is_nil(modified["object"]["context_id"])
866 end
867
868 test "it adds like collection to object" do
869 activity = insert(:note_activity)
870 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
871
872 assert modified["object"]["likes"]["type"] == "OrderedCollection"
873 assert modified["object"]["likes"]["totalItems"] == 0
874 end
875 end
876
877 describe "user upgrade" do
878 test "it upgrades a user to activitypub" do
879 user =
880 insert(:user, %{
881 nickname: "rye@niu.moe",
882 local: false,
883 ap_id: "https://niu.moe/users/rye",
884 follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
885 })
886
887 user_two = insert(:user, %{following: [user.follower_address]})
888
889 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
890 {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"})
891 assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
892
893 user = Repo.get(User, user.id)
894 assert user.info.note_count == 1
895
896 {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
897 assert user.info.ap_enabled
898 assert user.info.note_count == 1
899 assert user.follower_address == "https://niu.moe/users/rye/followers"
900
901 # Wait for the background task
902 :timer.sleep(1000)
903
904 user = Repo.get(User, user.id)
905 assert user.info.note_count == 1
906
907 activity = Repo.get(Activity, activity.id)
908 assert user.follower_address in activity.recipients
909
910 assert %{
911 "url" => [
912 %{
913 "href" =>
914 "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
915 }
916 ]
917 } = user.avatar
918
919 assert %{
920 "url" => [
921 %{
922 "href" =>
923 "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
924 }
925 ]
926 } = user.info.banner
927
928 refute "..." in activity.recipients
929
930 unrelated_activity = Repo.get(Activity, unrelated_activity.id)
931 refute user.follower_address in unrelated_activity.recipients
932
933 user_two = Repo.get(User, user_two.id)
934 assert user.follower_address in user_two.following
935 refute "..." in user_two.following
936 end
937 end
938
939 describe "maybe_retire_websub" do
940 test "it deletes all websub client subscripitions with the user as topic" do
941 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/rye.atom"}
942 {:ok, ws} = Repo.insert(subscription)
943
944 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/pasty.atom"}
945 {:ok, ws2} = Repo.insert(subscription)
946
947 Transmogrifier.maybe_retire_websub("https://niu.moe/users/rye")
948
949 refute Repo.get(WebsubClientSubscription, ws.id)
950 assert Repo.get(WebsubClientSubscription, ws2.id)
951 end
952 end
953
954 describe "actor rewriting" do
955 test "it fixes the actor URL property to be a proper URI" do
956 data = %{
957 "url" => %{"href" => "http://example.com"}
958 }
959
960 rewritten = Transmogrifier.maybe_fix_user_object(data)
961 assert rewritten["url"] == "http://example.com"
962 end
963 end
964
965 describe "actor origin containment" do
966 test "it rejects objects with a bogus origin" do
967 {:error, _} = ActivityPub.fetch_object_from_id("https://info.pleroma.site/activity.json")
968 end
969
970 test "it rejects activities which reference objects with bogus origins" do
971 data = %{
972 "@context" => "https://www.w3.org/ns/activitystreams",
973 "id" => "http://mastodon.example.org/users/admin/activities/1234",
974 "actor" => "http://mastodon.example.org/users/admin",
975 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
976 "object" => "https://info.pleroma.site/activity.json",
977 "type" => "Announce"
978 }
979
980 :error = Transmogrifier.handle_incoming(data)
981 end
982
983 test "it rejects objects when attributedTo is wrong (variant 1)" do
984 {:error, _} = ActivityPub.fetch_object_from_id("https://info.pleroma.site/activity2.json")
985 end
986
987 test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do
988 data = %{
989 "@context" => "https://www.w3.org/ns/activitystreams",
990 "id" => "http://mastodon.example.org/users/admin/activities/1234",
991 "actor" => "http://mastodon.example.org/users/admin",
992 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
993 "object" => "https://info.pleroma.site/activity2.json",
994 "type" => "Announce"
995 }
996
997 :error = Transmogrifier.handle_incoming(data)
998 end
999
1000 test "it rejects objects when attributedTo is wrong (variant 2)" do
1001 {:error, _} = ActivityPub.fetch_object_from_id("https://info.pleroma.site/activity3.json")
1002 end
1003
1004 test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do
1005 data = %{
1006 "@context" => "https://www.w3.org/ns/activitystreams",
1007 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1008 "actor" => "http://mastodon.example.org/users/admin",
1009 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1010 "object" => "https://info.pleroma.site/activity3.json",
1011 "type" => "Announce"
1012 }
1013
1014 :error = Transmogrifier.handle_incoming(data)
1015 end
1016 end
1017
1018 describe "general origin containment" do
1019 test "contain_origin_from_id() catches obvious spoofing attempts" do
1020 data = %{
1021 "id" => "http://example.com/~alyssa/activities/1234.json"
1022 }
1023
1024 :error =
1025 Transmogrifier.contain_origin_from_id(
1026 "http://example.org/~alyssa/activities/1234.json",
1027 data
1028 )
1029 end
1030
1031 test "contain_origin_from_id() allows alternate IDs within the same origin domain" do
1032 data = %{
1033 "id" => "http://example.com/~alyssa/activities/1234.json"
1034 }
1035
1036 :ok =
1037 Transmogrifier.contain_origin_from_id(
1038 "http://example.com/~alyssa/activities/1234",
1039 data
1040 )
1041 end
1042
1043 test "contain_origin_from_id() allows matching IDs" do
1044 data = %{
1045 "id" => "http://example.com/~alyssa/activities/1234.json"
1046 }
1047
1048 :ok =
1049 Transmogrifier.contain_origin_from_id(
1050 "http://example.com/~alyssa/activities/1234.json",
1051 data
1052 )
1053 end
1054
1055 test "users cannot be collided through fake direction spoofing attempts" do
1056 insert(:user, %{
1057 nickname: "rye@niu.moe",
1058 local: false,
1059 ap_id: "https://niu.moe/users/rye",
1060 follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
1061 })
1062
1063 {:error, _} = User.get_or_fetch_by_ap_id("https://n1u.moe/users/rye")
1064 end
1065
1066 test "all objects with fake directions are rejected by the object fetcher" do
1067 {:error, _} =
1068 ActivityPub.fetch_and_contain_remote_object_from_id(
1069 "https://info.pleroma.site/activity4.json"
1070 )
1071 end
1072 end
1073 end