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