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