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