tests: add legal boilerplate
[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 works for incoming update activities" do
292 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
293
294 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
295 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
296
297 object =
298 update_data["object"]
299 |> Map.put("actor", data["actor"])
300 |> Map.put("id", data["actor"])
301
302 update_data =
303 update_data
304 |> Map.put("actor", data["actor"])
305 |> Map.put("object", object)
306
307 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
308
309 user = User.get_cached_by_ap_id(data["actor"])
310 assert user.name == "gargle"
311
312 assert user.avatar["url"] == [
313 %{
314 "href" =>
315 "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
316 }
317 ]
318
319 assert user.info.banner["url"] == [
320 %{
321 "href" =>
322 "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
323 }
324 ]
325
326 assert user.bio == "<p>Some bio</p>"
327 end
328
329 test "it works for incoming update activities which lock the account" do
330 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
331
332 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
333 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
334
335 object =
336 update_data["object"]
337 |> Map.put("actor", data["actor"])
338 |> Map.put("id", data["actor"])
339 |> Map.put("manuallyApprovesFollowers", true)
340
341 update_data =
342 update_data
343 |> Map.put("actor", data["actor"])
344 |> Map.put("object", object)
345
346 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
347
348 user = User.get_cached_by_ap_id(data["actor"])
349 assert user.info.locked == true
350 end
351
352 test "it works for incoming deletes" do
353 activity = insert(:note_activity)
354
355 data =
356 File.read!("test/fixtures/mastodon-delete.json")
357 |> Poison.decode!()
358
359 object =
360 data["object"]
361 |> Map.put("id", activity.data["object"]["id"])
362
363 data =
364 data
365 |> Map.put("object", object)
366 |> Map.put("actor", activity.data["actor"])
367
368 {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
369
370 refute Repo.get(Activity, activity.id)
371 end
372
373 test "it fails for incoming deletes with spoofed origin" do
374 activity = insert(:note_activity)
375
376 data =
377 File.read!("test/fixtures/mastodon-delete.json")
378 |> Poison.decode!()
379
380 object =
381 data["object"]
382 |> Map.put("id", activity.data["object"]["id"])
383
384 data =
385 data
386 |> Map.put("object", object)
387
388 :error = Transmogrifier.handle_incoming(data)
389
390 assert Repo.get(Activity, activity.id)
391 end
392
393 test "it works for incoming unannounces with an existing notice" do
394 user = insert(:user)
395 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
396
397 announce_data =
398 File.read!("test/fixtures/mastodon-announce.json")
399 |> Poison.decode!()
400 |> Map.put("object", activity.data["object"]["id"])
401
402 {:ok, %Activity{data: announce_data, local: false}} =
403 Transmogrifier.handle_incoming(announce_data)
404
405 data =
406 File.read!("test/fixtures/mastodon-undo-announce.json")
407 |> Poison.decode!()
408 |> Map.put("object", announce_data)
409 |> Map.put("actor", announce_data["actor"])
410
411 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
412
413 assert data["type"] == "Undo"
414 assert data["object"]["type"] == "Announce"
415 assert data["object"]["object"] == activity.data["object"]["id"]
416
417 assert data["object"]["id"] ==
418 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
419 end
420
421 test "it works for incomming unfollows with an existing follow" do
422 user = insert(:user)
423
424 follow_data =
425 File.read!("test/fixtures/mastodon-follow-activity.json")
426 |> Poison.decode!()
427 |> Map.put("object", user.ap_id)
428
429 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data)
430
431 data =
432 File.read!("test/fixtures/mastodon-unfollow-activity.json")
433 |> Poison.decode!()
434 |> Map.put("object", follow_data)
435
436 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
437
438 assert data["type"] == "Undo"
439 assert data["object"]["type"] == "Follow"
440 assert data["object"]["object"] == user.ap_id
441 assert data["actor"] == "http://mastodon.example.org/users/admin"
442
443 refute User.following?(User.get_by_ap_id(data["actor"]), user)
444 end
445
446 test "it works for incoming blocks" do
447 user = insert(:user)
448
449 data =
450 File.read!("test/fixtures/mastodon-block-activity.json")
451 |> Poison.decode!()
452 |> Map.put("object", user.ap_id)
453
454 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
455
456 assert data["type"] == "Block"
457 assert data["object"] == user.ap_id
458 assert data["actor"] == "http://mastodon.example.org/users/admin"
459
460 blocker = User.get_by_ap_id(data["actor"])
461
462 assert User.blocks?(blocker, user)
463 end
464
465 test "incoming blocks successfully tear down any follow relationship" do
466 blocker = insert(:user)
467 blocked = insert(:user)
468
469 data =
470 File.read!("test/fixtures/mastodon-block-activity.json")
471 |> Poison.decode!()
472 |> Map.put("object", blocked.ap_id)
473 |> Map.put("actor", blocker.ap_id)
474
475 {:ok, blocker} = User.follow(blocker, blocked)
476 {:ok, blocked} = User.follow(blocked, blocker)
477
478 assert User.following?(blocker, blocked)
479 assert User.following?(blocked, blocker)
480
481 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
482
483 assert data["type"] == "Block"
484 assert data["object"] == blocked.ap_id
485 assert data["actor"] == blocker.ap_id
486
487 blocker = User.get_by_ap_id(data["actor"])
488 blocked = User.get_by_ap_id(data["object"])
489
490 assert User.blocks?(blocker, blocked)
491
492 refute User.following?(blocker, blocked)
493 refute User.following?(blocked, blocker)
494 end
495
496 test "it works for incoming unblocks with an existing block" do
497 user = insert(:user)
498
499 block_data =
500 File.read!("test/fixtures/mastodon-block-activity.json")
501 |> Poison.decode!()
502 |> Map.put("object", user.ap_id)
503
504 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data)
505
506 data =
507 File.read!("test/fixtures/mastodon-unblock-activity.json")
508 |> Poison.decode!()
509 |> Map.put("object", block_data)
510
511 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
512 assert data["type"] == "Undo"
513 assert data["object"]["type"] == "Block"
514 assert data["object"]["object"] == user.ap_id
515 assert data["actor"] == "http://mastodon.example.org/users/admin"
516
517 blocker = User.get_by_ap_id(data["actor"])
518
519 refute User.blocks?(blocker, user)
520 end
521
522 test "it works for incoming accepts which were pre-accepted" do
523 follower = insert(:user)
524 followed = insert(:user)
525
526 {:ok, follower} = User.follow(follower, followed)
527 assert User.following?(follower, followed) == true
528
529 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
530
531 accept_data =
532 File.read!("test/fixtures/mastodon-accept-activity.json")
533 |> Poison.decode!()
534 |> Map.put("actor", followed.ap_id)
535
536 object =
537 accept_data["object"]
538 |> Map.put("actor", follower.ap_id)
539 |> Map.put("id", follow_activity.data["id"])
540
541 accept_data = Map.put(accept_data, "object", object)
542
543 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
544 refute activity.local
545
546 assert activity.data["object"] == follow_activity.data["id"]
547
548 follower = Repo.get(User, follower.id)
549
550 assert User.following?(follower, followed) == true
551 end
552
553 test "it works for incoming accepts which were orphaned" do
554 follower = insert(:user)
555 followed = insert(:user, %{info: %User.Info{locked: true}})
556
557 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
558
559 accept_data =
560 File.read!("test/fixtures/mastodon-accept-activity.json")
561 |> Poison.decode!()
562 |> Map.put("actor", followed.ap_id)
563
564 accept_data =
565 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
566
567 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
568 assert activity.data["object"] == follow_activity.data["id"]
569
570 follower = Repo.get(User, follower.id)
571
572 assert User.following?(follower, followed) == true
573 end
574
575 test "it works for incoming accepts which are referenced by IRI only" do
576 follower = insert(:user)
577 followed = insert(:user, %{info: %User.Info{locked: true}})
578
579 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
580
581 accept_data =
582 File.read!("test/fixtures/mastodon-accept-activity.json")
583 |> Poison.decode!()
584 |> Map.put("actor", followed.ap_id)
585 |> Map.put("object", follow_activity.data["id"])
586
587 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
588 assert activity.data["object"] == follow_activity.data["id"]
589
590 follower = Repo.get(User, follower.id)
591
592 assert User.following?(follower, followed) == true
593 end
594
595 test "it fails for incoming accepts which cannot be correlated" do
596 follower = insert(:user)
597 followed = insert(:user, %{info: %User.Info{locked: true}})
598
599 accept_data =
600 File.read!("test/fixtures/mastodon-accept-activity.json")
601 |> Poison.decode!()
602 |> Map.put("actor", followed.ap_id)
603
604 accept_data =
605 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
606
607 :error = Transmogrifier.handle_incoming(accept_data)
608
609 follower = Repo.get(User, follower.id)
610
611 refute User.following?(follower, followed) == true
612 end
613
614 test "it fails for incoming rejects which cannot be correlated" do
615 follower = insert(:user)
616 followed = insert(:user, %{info: %User.Info{locked: true}})
617
618 accept_data =
619 File.read!("test/fixtures/mastodon-reject-activity.json")
620 |> Poison.decode!()
621 |> Map.put("actor", followed.ap_id)
622
623 accept_data =
624 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
625
626 :error = Transmogrifier.handle_incoming(accept_data)
627
628 follower = Repo.get(User, follower.id)
629
630 refute User.following?(follower, followed) == true
631 end
632
633 test "it works for incoming rejects which are orphaned" do
634 follower = insert(:user)
635 followed = insert(:user, %{info: %User.Info{locked: true}})
636
637 {:ok, follower} = User.follow(follower, followed)
638 {:ok, _follow_activity} = ActivityPub.follow(follower, followed)
639
640 assert User.following?(follower, followed) == true
641
642 reject_data =
643 File.read!("test/fixtures/mastodon-reject-activity.json")
644 |> Poison.decode!()
645 |> Map.put("actor", followed.ap_id)
646
647 reject_data =
648 Map.put(reject_data, "object", Map.put(reject_data["object"], "actor", follower.ap_id))
649
650 {:ok, activity} = Transmogrifier.handle_incoming(reject_data)
651 refute activity.local
652
653 follower = Repo.get(User, follower.id)
654
655 assert User.following?(follower, followed) == false
656 end
657
658 test "it works for incoming rejects which are referenced by IRI only" do
659 follower = insert(:user)
660 followed = insert(:user, %{info: %User.Info{locked: true}})
661
662 {:ok, follower} = User.follow(follower, followed)
663 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
664
665 assert User.following?(follower, followed) == true
666
667 reject_data =
668 File.read!("test/fixtures/mastodon-reject-activity.json")
669 |> Poison.decode!()
670 |> Map.put("actor", followed.ap_id)
671 |> Map.put("object", follow_activity.data["id"])
672
673 {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data)
674
675 follower = Repo.get(User, follower.id)
676
677 assert User.following?(follower, followed) == false
678 end
679
680 test "it rejects activities without a valid ID" do
681 user = insert(:user)
682
683 data =
684 File.read!("test/fixtures/mastodon-follow-activity.json")
685 |> Poison.decode!()
686 |> Map.put("object", user.ap_id)
687 |> Map.put("id", "")
688
689 :error = Transmogrifier.handle_incoming(data)
690 end
691
692 test "it remaps video URLs as attachments if necessary" do
693 {:ok, object} =
694 ActivityPub.fetch_object_from_id(
695 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
696 )
697
698 attachment = %{
699 "type" => "Link",
700 "mediaType" => "video/mp4",
701 "href" =>
702 "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
703 "mimeType" => "video/mp4",
704 "size" => 5_015_880,
705 "url" => [
706 %{
707 "href" =>
708 "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
709 "mediaType" => "video/mp4",
710 "type" => "Link"
711 }
712 ],
713 "width" => 480
714 }
715
716 assert object.data["url"] ==
717 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
718
719 assert object.data["attachment"] == [attachment]
720 end
721 end
722
723 describe "prepare outgoing" do
724 test "it turns mentions into tags" do
725 user = insert(:user)
726 other_user = insert(:user)
727
728 {:ok, activity} =
729 CommonAPI.post(user, %{"status" => "hey, @#{other_user.nickname}, how are ya? #2hu"})
730
731 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
732 object = modified["object"]
733
734 expected_mention = %{
735 "href" => other_user.ap_id,
736 "name" => "@#{other_user.nickname}",
737 "type" => "Mention"
738 }
739
740 expected_tag = %{
741 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
742 "type" => "Hashtag",
743 "name" => "#2hu"
744 }
745
746 assert Enum.member?(object["tag"], expected_tag)
747 assert Enum.member?(object["tag"], expected_mention)
748 end
749
750 test "it adds the sensitive property" do
751 user = insert(:user)
752
753 {:ok, activity} = CommonAPI.post(user, %{"status" => "#nsfw hey"})
754 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
755
756 assert modified["object"]["sensitive"]
757 end
758
759 test "it adds the json-ld context and the conversation property" do
760 user = insert(:user)
761
762 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
763 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
764
765 assert modified["@context"] ==
766 Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"]
767
768 assert modified["object"]["conversation"] == modified["context"]
769 end
770
771 test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do
772 user = insert(:user)
773
774 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
775 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
776
777 assert modified["object"]["actor"] == modified["object"]["attributedTo"]
778 end
779
780 test "it translates ostatus IDs to external URLs" do
781 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
782 {:ok, [referent_activity]} = OStatus.handle_incoming(incoming)
783
784 user = insert(:user)
785
786 {:ok, activity, _} = CommonAPI.favorite(referent_activity.id, user)
787 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
788
789 assert modified["object"] == "http://gs.example.org:4040/index.php/notice/29"
790 end
791
792 test "it translates ostatus reply_to IDs to external URLs" do
793 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
794 {:ok, [referred_activity]} = OStatus.handle_incoming(incoming)
795
796 user = insert(:user)
797
798 {:ok, activity} =
799 CommonAPI.post(user, %{"status" => "HI!", "in_reply_to_status_id" => referred_activity.id})
800
801 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
802
803 assert modified["object"]["inReplyTo"] == "http://gs.example.org:4040/index.php/notice/29"
804 end
805
806 test "it strips internal hashtag data" do
807 user = insert(:user)
808
809 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu"})
810
811 expected_tag = %{
812 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
813 "type" => "Hashtag",
814 "name" => "#2hu"
815 }
816
817 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
818
819 assert modified["object"]["tag"] == [expected_tag]
820 end
821
822 test "it strips internal fields" do
823 user = insert(:user)
824
825 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu :moominmamma:"})
826
827 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
828
829 assert length(modified["object"]["tag"]) == 2
830
831 assert is_nil(modified["object"]["emoji"])
832 assert is_nil(modified["object"]["likes"])
833 assert is_nil(modified["object"]["like_count"])
834 assert is_nil(modified["object"]["announcements"])
835 assert is_nil(modified["object"]["announcement_count"])
836 assert is_nil(modified["object"]["context_id"])
837 end
838 end
839
840 describe "user upgrade" do
841 test "it upgrades a user to activitypub" do
842 user =
843 insert(:user, %{
844 nickname: "rye@niu.moe",
845 local: false,
846 ap_id: "https://niu.moe/users/rye",
847 follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
848 })
849
850 user_two = insert(:user, %{following: [user.follower_address]})
851
852 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
853 {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"})
854 assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
855
856 user = Repo.get(User, user.id)
857 assert user.info.note_count == 1
858
859 {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
860 assert user.info.ap_enabled
861 assert user.info.note_count == 1
862 assert user.follower_address == "https://niu.moe/users/rye/followers"
863
864 # Wait for the background task
865 :timer.sleep(1000)
866
867 user = Repo.get(User, user.id)
868 assert user.info.note_count == 1
869
870 activity = Repo.get(Activity, activity.id)
871 assert user.follower_address in activity.recipients
872
873 assert %{
874 "url" => [
875 %{
876 "href" =>
877 "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
878 }
879 ]
880 } = user.avatar
881
882 assert %{
883 "url" => [
884 %{
885 "href" =>
886 "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
887 }
888 ]
889 } = user.info.banner
890
891 refute "..." in activity.recipients
892
893 unrelated_activity = Repo.get(Activity, unrelated_activity.id)
894 refute user.follower_address in unrelated_activity.recipients
895
896 user_two = Repo.get(User, user_two.id)
897 assert user.follower_address in user_two.following
898 refute "..." in user_two.following
899 end
900 end
901
902 describe "maybe_retire_websub" do
903 test "it deletes all websub client subscripitions with the user as topic" do
904 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/rye.atom"}
905 {:ok, ws} = Repo.insert(subscription)
906
907 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/pasty.atom"}
908 {:ok, ws2} = Repo.insert(subscription)
909
910 Transmogrifier.maybe_retire_websub("https://niu.moe/users/rye")
911
912 refute Repo.get(WebsubClientSubscription, ws.id)
913 assert Repo.get(WebsubClientSubscription, ws2.id)
914 end
915 end
916
917 describe "actor rewriting" do
918 test "it fixes the actor URL property to be a proper URI" do
919 data = %{
920 "url" => %{"href" => "http://example.com"}
921 }
922
923 rewritten = Transmogrifier.maybe_fix_user_object(data)
924 assert rewritten["url"] == "http://example.com"
925 end
926 end
927
928 describe "actor origin containment" do
929 test "it rejects objects with a bogus origin" do
930 {:error, _} = ActivityPub.fetch_object_from_id("https://info.pleroma.site/activity.json")
931 end
932
933 test "it rejects activities which reference objects with bogus origins" do
934 data = %{
935 "@context" => "https://www.w3.org/ns/activitystreams",
936 "id" => "http://mastodon.example.org/users/admin/activities/1234",
937 "actor" => "http://mastodon.example.org/users/admin",
938 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
939 "object" => "https://info.pleroma.site/activity.json",
940 "type" => "Announce"
941 }
942
943 :error = Transmogrifier.handle_incoming(data)
944 end
945
946 test "it rejects objects when attributedTo is wrong (variant 1)" do
947 {:error, _} = ActivityPub.fetch_object_from_id("https://info.pleroma.site/activity2.json")
948 end
949
950 test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do
951 data = %{
952 "@context" => "https://www.w3.org/ns/activitystreams",
953 "id" => "http://mastodon.example.org/users/admin/activities/1234",
954 "actor" => "http://mastodon.example.org/users/admin",
955 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
956 "object" => "https://info.pleroma.site/activity2.json",
957 "type" => "Announce"
958 }
959
960 :error = Transmogrifier.handle_incoming(data)
961 end
962
963 test "it rejects objects when attributedTo is wrong (variant 2)" do
964 {:error, _} = ActivityPub.fetch_object_from_id("https://info.pleroma.site/activity3.json")
965 end
966
967 test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do
968 data = %{
969 "@context" => "https://www.w3.org/ns/activitystreams",
970 "id" => "http://mastodon.example.org/users/admin/activities/1234",
971 "actor" => "http://mastodon.example.org/users/admin",
972 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
973 "object" => "https://info.pleroma.site/activity3.json",
974 "type" => "Announce"
975 }
976
977 :error = Transmogrifier.handle_incoming(data)
978 end
979 end
980
981 describe "general origin containment" do
982 test "contain_origin_from_id() catches obvious spoofing attempts" do
983 data = %{
984 "id" => "http://example.com/~alyssa/activities/1234.json"
985 }
986
987 :error =
988 Transmogrifier.contain_origin_from_id(
989 "http://example.org/~alyssa/activities/1234.json",
990 data
991 )
992 end
993
994 test "contain_origin_from_id() allows alternate IDs within the same origin domain" do
995 data = %{
996 "id" => "http://example.com/~alyssa/activities/1234.json"
997 }
998
999 :ok =
1000 Transmogrifier.contain_origin_from_id(
1001 "http://example.com/~alyssa/activities/1234",
1002 data
1003 )
1004 end
1005
1006 test "contain_origin_from_id() allows matching IDs" do
1007 data = %{
1008 "id" => "http://example.com/~alyssa/activities/1234.json"
1009 }
1010
1011 :ok =
1012 Transmogrifier.contain_origin_from_id(
1013 "http://example.com/~alyssa/activities/1234.json",
1014 data
1015 )
1016 end
1017
1018 test "users cannot be collided through fake direction spoofing attempts" do
1019 insert(:user, %{
1020 nickname: "rye@niu.moe",
1021 local: false,
1022 ap_id: "https://niu.moe/users/rye",
1023 follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
1024 })
1025
1026 {:error, _} = User.get_or_fetch_by_ap_id("https://n1u.moe/users/rye")
1027 end
1028
1029 test "all objects with fake directions are rejected by the object fetcher" do
1030 {:error, _} =
1031 ActivityPub.fetch_and_contain_remote_object_from_id(
1032 "https://info.pleroma.site/activity4.json"
1033 )
1034 end
1035 end
1036 end