91a3109bbb948f3514ed629d6ad5c48047348b9a
[akkoma] / test / pleroma / web / activity_pub / activity_pub_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
6 use Pleroma.Web.ConnCase
7 use Oban.Testing, repo: Pleroma.Repo
8
9 alias Pleroma.Activity
10 alias Pleroma.Delivery
11 alias Pleroma.Instances
12 alias Pleroma.Object
13 alias Pleroma.Tests.ObanHelpers
14 alias Pleroma.User
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.ActivityPub.ObjectView
17 alias Pleroma.Web.ActivityPub.Relay
18 alias Pleroma.Web.ActivityPub.UserView
19 alias Pleroma.Web.ActivityPub.Utils
20 alias Pleroma.Web.CommonAPI
21 alias Pleroma.Web.Endpoint
22 alias Pleroma.Workers.ReceiverWorker
23
24 import Pleroma.Factory
25
26 require Pleroma.Constants
27
28 setup_all do
29 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
30 :ok
31 end
32
33 setup do: clear_config([:instance, :federating], true)
34
35 describe "/relay" do
36 setup do: clear_config([:instance, :allow_relay])
37
38 test "with the relay active, it returns the relay user", %{conn: conn} do
39 res =
40 conn
41 |> get(activity_pub_path(conn, :relay))
42 |> json_response(200)
43
44 assert res["id"] =~ "/relay"
45 end
46
47 test "with the relay disabled, it returns 404", %{conn: conn} do
48 clear_config([:instance, :allow_relay], false)
49
50 conn
51 |> get(activity_pub_path(conn, :relay))
52 |> json_response(404)
53 end
54
55 test "on non-federating instance, it returns 404", %{conn: conn} do
56 clear_config([:instance, :federating], false)
57 user = insert(:user)
58
59 conn
60 |> assign(:user, user)
61 |> get(activity_pub_path(conn, :relay))
62 |> json_response(404)
63 end
64 end
65
66 describe "/internal/fetch" do
67 test "it returns the internal fetch user", %{conn: conn} do
68 res =
69 conn
70 |> get(activity_pub_path(conn, :internal_fetch))
71 |> json_response(200)
72
73 assert res["id"] =~ "/fetch"
74 end
75
76 test "on non-federating instance, it returns 404", %{conn: conn} do
77 clear_config([:instance, :federating], false)
78 user = insert(:user)
79
80 conn
81 |> assign(:user, user)
82 |> get(activity_pub_path(conn, :internal_fetch))
83 |> json_response(404)
84 end
85 end
86
87 describe "/users/:nickname" do
88 test "it returns a json representation of the user with accept application/json", %{
89 conn: conn
90 } do
91 user = insert(:user)
92
93 conn =
94 conn
95 |> put_req_header("accept", "application/json")
96 |> get("/users/#{user.nickname}")
97
98 user = User.get_cached_by_id(user.id)
99
100 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
101 end
102
103 test "it returns a json representation of the user with accept application/activity+json", %{
104 conn: conn
105 } do
106 user = insert(:user)
107
108 conn =
109 conn
110 |> put_req_header("accept", "application/activity+json")
111 |> get("/users/#{user.nickname}")
112
113 user = User.get_cached_by_id(user.id)
114
115 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
116 end
117
118 test "it returns a json representation of the user with accept application/ld+json", %{
119 conn: conn
120 } do
121 user = insert(:user)
122
123 conn =
124 conn
125 |> put_req_header(
126 "accept",
127 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
128 )
129 |> get("/users/#{user.nickname}")
130
131 user = User.get_cached_by_id(user.id)
132
133 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
134 end
135
136 test "it returns 404 for remote users", %{
137 conn: conn
138 } do
139 user = insert(:user, local: false, nickname: "remoteuser@example.com")
140
141 conn =
142 conn
143 |> put_req_header("accept", "application/json")
144 |> get("/users/#{user.nickname}.json")
145
146 assert json_response(conn, 404)
147 end
148
149 test "it returns error when user is not found", %{conn: conn} do
150 response =
151 conn
152 |> put_req_header("accept", "application/json")
153 |> get("/users/jimm")
154 |> json_response(404)
155
156 assert response == "Not found"
157 end
158 end
159
160 describe "mastodon compatibility routes" do
161 test "it returns a json representation of the object with accept application/json", %{
162 conn: conn
163 } do
164 {:ok, object} =
165 %{
166 "type" => "Note",
167 "content" => "hey",
168 "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999",
169 "actor" => Endpoint.url() <> "/users/raymoo",
170 "to" => [Pleroma.Constants.as_public()]
171 }
172 |> Object.create()
173
174 conn =
175 conn
176 |> put_req_header("accept", "application/json")
177 |> get("/users/raymoo/statuses/999999999")
178
179 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: object})
180 end
181
182 test "it returns a json representation of the activity with accept application/json", %{
183 conn: conn
184 } do
185 {:ok, object} =
186 %{
187 "type" => "Note",
188 "content" => "hey",
189 "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999",
190 "actor" => Endpoint.url() <> "/users/raymoo",
191 "to" => [Pleroma.Constants.as_public()]
192 }
193 |> Object.create()
194
195 {:ok, activity, _} =
196 %{
197 "id" => object.data["id"] <> "/activity",
198 "type" => "Create",
199 "object" => object.data["id"],
200 "actor" => object.data["actor"],
201 "to" => object.data["to"]
202 }
203 |> ActivityPub.persist(local: true)
204
205 conn =
206 conn
207 |> put_req_header("accept", "application/json")
208 |> get("/users/raymoo/statuses/999999999/activity")
209
210 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity})
211 end
212 end
213
214 describe "/objects/:uuid" do
215 test "it doesn't return a local-only object", %{conn: conn} do
216 user = insert(:user)
217 {:ok, post} = CommonAPI.post(user, %{status: "test", visibility: "local"})
218
219 assert Pleroma.Web.ActivityPub.Visibility.is_local_public?(post)
220
221 object = Object.normalize(post, fetch: false)
222 uuid = String.split(object.data["id"], "/") |> List.last()
223
224 conn =
225 conn
226 |> put_req_header("accept", "application/json")
227 |> get("/objects/#{uuid}")
228
229 assert json_response(conn, 404)
230 end
231
232 test "it returns a json representation of the object with accept application/json", %{
233 conn: conn
234 } do
235 note = insert(:note)
236 uuid = String.split(note.data["id"], "/") |> List.last()
237
238 conn =
239 conn
240 |> put_req_header("accept", "application/json")
241 |> get("/objects/#{uuid}")
242
243 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
244 end
245
246 test "it returns a json representation of the object with accept application/activity+json",
247 %{conn: conn} do
248 note = insert(:note)
249 uuid = String.split(note.data["id"], "/") |> List.last()
250
251 conn =
252 conn
253 |> put_req_header("accept", "application/activity+json")
254 |> get("/objects/#{uuid}")
255
256 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
257 end
258
259 test "it returns a json representation of the object with accept application/ld+json", %{
260 conn: conn
261 } do
262 note = insert(:note)
263 uuid = String.split(note.data["id"], "/") |> List.last()
264
265 conn =
266 conn
267 |> put_req_header(
268 "accept",
269 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
270 )
271 |> get("/objects/#{uuid}")
272
273 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
274 end
275
276 test "it returns 404 for non-public messages", %{conn: conn} do
277 note = insert(:direct_note)
278 uuid = String.split(note.data["id"], "/") |> List.last()
279
280 conn =
281 conn
282 |> put_req_header("accept", "application/activity+json")
283 |> get("/objects/#{uuid}")
284
285 assert json_response(conn, 404)
286 end
287
288 test "it returns 404 for tombstone objects", %{conn: conn} do
289 tombstone = insert(:tombstone)
290 uuid = String.split(tombstone.data["id"], "/") |> List.last()
291
292 conn =
293 conn
294 |> put_req_header("accept", "application/activity+json")
295 |> get("/objects/#{uuid}")
296
297 assert json_response(conn, 404)
298 end
299
300 test "it caches a response", %{conn: conn} do
301 note = insert(:note)
302 uuid = String.split(note.data["id"], "/") |> List.last()
303
304 conn1 =
305 conn
306 |> put_req_header("accept", "application/activity+json")
307 |> get("/objects/#{uuid}")
308
309 assert json_response(conn1, :ok)
310 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
311
312 conn2 =
313 conn
314 |> put_req_header("accept", "application/activity+json")
315 |> get("/objects/#{uuid}")
316
317 assert json_response(conn1, :ok) == json_response(conn2, :ok)
318 assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
319 end
320
321 test "cached purged after object deletion", %{conn: conn} do
322 note = insert(:note)
323 uuid = String.split(note.data["id"], "/") |> List.last()
324
325 conn1 =
326 conn
327 |> put_req_header("accept", "application/activity+json")
328 |> get("/objects/#{uuid}")
329
330 assert json_response(conn1, :ok)
331 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
332
333 Object.delete(note)
334
335 conn2 =
336 conn
337 |> put_req_header("accept", "application/activity+json")
338 |> get("/objects/#{uuid}")
339
340 assert "Not found" == json_response(conn2, :not_found)
341 end
342 end
343
344 describe "/activities/:uuid" do
345 test "it doesn't return a local-only activity", %{conn: conn} do
346 user = insert(:user)
347 {:ok, post} = CommonAPI.post(user, %{status: "test", visibility: "local"})
348
349 assert Pleroma.Web.ActivityPub.Visibility.is_local_public?(post)
350
351 uuid = String.split(post.data["id"], "/") |> List.last()
352
353 conn =
354 conn
355 |> put_req_header("accept", "application/json")
356 |> get("/activities/#{uuid}")
357
358 assert json_response(conn, 404)
359 end
360
361 test "it returns a json representation of the activity", %{conn: conn} do
362 activity = insert(:note_activity)
363 uuid = String.split(activity.data["id"], "/") |> List.last()
364
365 conn =
366 conn
367 |> put_req_header("accept", "application/activity+json")
368 |> get("/activities/#{uuid}")
369
370 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity})
371 end
372
373 test "it returns 404 for non-public activities", %{conn: conn} do
374 activity = insert(:direct_note_activity)
375 uuid = String.split(activity.data["id"], "/") |> List.last()
376
377 conn =
378 conn
379 |> put_req_header("accept", "application/activity+json")
380 |> get("/activities/#{uuid}")
381
382 assert json_response(conn, 404)
383 end
384
385 test "it caches a response", %{conn: conn} do
386 activity = insert(:note_activity)
387 uuid = String.split(activity.data["id"], "/") |> List.last()
388
389 conn1 =
390 conn
391 |> put_req_header("accept", "application/activity+json")
392 |> get("/activities/#{uuid}")
393
394 assert json_response(conn1, :ok)
395 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
396
397 conn2 =
398 conn
399 |> put_req_header("accept", "application/activity+json")
400 |> get("/activities/#{uuid}")
401
402 assert json_response(conn1, :ok) == json_response(conn2, :ok)
403 assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
404 end
405
406 test "cached purged after activity deletion", %{conn: conn} do
407 user = insert(:user)
408 {:ok, activity} = CommonAPI.post(user, %{status: "cofe"})
409
410 uuid = String.split(activity.data["id"], "/") |> List.last()
411
412 conn1 =
413 conn
414 |> put_req_header("accept", "application/activity+json")
415 |> get("/activities/#{uuid}")
416
417 assert json_response(conn1, :ok)
418 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
419
420 Activity.delete_all_by_object_ap_id(activity.object.data["id"])
421
422 conn2 =
423 conn
424 |> put_req_header("accept", "application/activity+json")
425 |> get("/activities/#{uuid}")
426
427 assert "Not found" == json_response(conn2, :not_found)
428 end
429 end
430
431 describe "/inbox" do
432 test "it inserts an incoming activity into the database", %{conn: conn} do
433 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
434
435 conn =
436 conn
437 |> assign(:valid_signature, true)
438 |> put_req_header("content-type", "application/activity+json")
439 |> post("/inbox", data)
440
441 assert "ok" == json_response(conn, 200)
442
443 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
444 assert Activity.get_by_ap_id(data["id"])
445 end
446
447 @tag capture_log: true
448 test "it inserts an incoming activity into the database" <>
449 "even if we can't fetch the user but have it in our db",
450 %{conn: conn} do
451 user =
452 insert(:user,
453 ap_id: "https://mastodon.example.org/users/raymoo",
454 ap_enabled: true,
455 local: false,
456 last_refreshed_at: nil
457 )
458
459 data =
460 File.read!("test/fixtures/mastodon-post-activity.json")
461 |> Jason.decode!()
462 |> Map.put("actor", user.ap_id)
463 |> put_in(["object", "attridbutedTo"], user.ap_id)
464
465 conn =
466 conn
467 |> assign(:valid_signature, true)
468 |> put_req_header("content-type", "application/activity+json")
469 |> post("/inbox", data)
470
471 assert "ok" == json_response(conn, 200)
472
473 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
474 assert Activity.get_by_ap_id(data["id"])
475 end
476
477 test "it clears `unreachable` federation status of the sender", %{conn: conn} do
478 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
479
480 sender_url = data["actor"]
481 Instances.set_consistently_unreachable(sender_url)
482 refute Instances.reachable?(sender_url)
483
484 conn =
485 conn
486 |> assign(:valid_signature, true)
487 |> put_req_header("content-type", "application/activity+json")
488 |> post("/inbox", data)
489
490 assert "ok" == json_response(conn, 200)
491 assert Instances.reachable?(sender_url)
492 end
493
494 test "accept follow activity", %{conn: conn} do
495 clear_config([:instance, :federating], true)
496 relay = Relay.get_actor()
497
498 assert {:ok, %Activity{} = activity} = Relay.follow("https://relay.mastodon.host/actor")
499
500 followed_relay = Pleroma.User.get_by_ap_id("https://relay.mastodon.host/actor")
501 relay = refresh_record(relay)
502
503 accept =
504 File.read!("test/fixtures/relay/accept-follow.json")
505 |> String.replace("{{ap_id}}", relay.ap_id)
506 |> String.replace("{{activity_id}}", activity.data["id"])
507
508 assert "ok" ==
509 conn
510 |> assign(:valid_signature, true)
511 |> put_req_header("content-type", "application/activity+json")
512 |> post("/inbox", accept)
513 |> json_response(200)
514
515 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
516
517 assert Pleroma.FollowingRelationship.following?(
518 relay,
519 followed_relay
520 )
521
522 Mix.shell(Mix.Shell.Process)
523
524 on_exit(fn ->
525 Mix.shell(Mix.Shell.IO)
526 end)
527
528 :ok = Mix.Tasks.Pleroma.Relay.run(["list"])
529 assert_receive {:mix_shell, :info, ["https://relay.mastodon.host/actor"]}
530 end
531
532 @tag capture_log: true
533 test "without valid signature, " <>
534 "it only accepts Create activities and requires enabled federation",
535 %{conn: conn} do
536 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
537 non_create_data = File.read!("test/fixtures/mastodon-announce.json") |> Jason.decode!()
538
539 conn = put_req_header(conn, "content-type", "application/activity+json")
540
541 clear_config([:instance, :federating], false)
542
543 conn
544 |> post("/inbox", data)
545 |> json_response(403)
546
547 conn
548 |> post("/inbox", non_create_data)
549 |> json_response(403)
550
551 clear_config([:instance, :federating], true)
552
553 ret_conn = post(conn, "/inbox", data)
554 assert "ok" == json_response(ret_conn, 200)
555
556 conn
557 |> post("/inbox", non_create_data)
558 |> json_response(400)
559 end
560 end
561
562 describe "/users/:nickname/inbox" do
563 setup do
564 data =
565 File.read!("test/fixtures/mastodon-post-activity.json")
566 |> Jason.decode!()
567
568 [data: data]
569 end
570
571 test "it inserts an incoming activity into the database", %{conn: conn, data: data} do
572 user = insert(:user)
573 data = Map.put(data, "bcc", [user.ap_id])
574
575 conn =
576 conn
577 |> assign(:valid_signature, true)
578 |> put_req_header("content-type", "application/activity+json")
579 |> post("/users/#{user.nickname}/inbox", data)
580
581 assert "ok" == json_response(conn, 200)
582 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
583 assert Activity.get_by_ap_id(data["id"])
584 end
585
586 test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do
587 user = insert(:user)
588
589 data =
590 Map.put(data, "to", user.ap_id)
591 |> Map.delete("cc")
592
593 conn =
594 conn
595 |> assign(:valid_signature, true)
596 |> put_req_header("content-type", "application/activity+json")
597 |> post("/users/#{user.nickname}/inbox", data)
598
599 assert "ok" == json_response(conn, 200)
600 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
601 assert Activity.get_by_ap_id(data["id"])
602 end
603
604 test "it accepts messages with cc as string instead of array", %{conn: conn, data: data} do
605 user = insert(:user)
606
607 data =
608 Map.put(data, "cc", user.ap_id)
609 |> Map.delete("to")
610
611 conn =
612 conn
613 |> assign(:valid_signature, true)
614 |> put_req_header("content-type", "application/activity+json")
615 |> post("/users/#{user.nickname}/inbox", data)
616
617 assert "ok" == json_response(conn, 200)
618 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
619 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
620 assert user.ap_id in activity.recipients
621 end
622
623 test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do
624 user = insert(:user)
625
626 data =
627 Map.put(data, "bcc", user.ap_id)
628 |> Map.delete("to")
629 |> Map.delete("cc")
630
631 conn =
632 conn
633 |> assign(:valid_signature, true)
634 |> put_req_header("content-type", "application/activity+json")
635 |> post("/users/#{user.nickname}/inbox", data)
636
637 assert "ok" == json_response(conn, 200)
638 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
639 assert Activity.get_by_ap_id(data["id"])
640 end
641
642 test "it accepts announces with to as string instead of array", %{conn: conn} do
643 user = insert(:user)
644
645 {:ok, post} = CommonAPI.post(user, %{status: "hey"})
646 announcer = insert(:user, local: false)
647
648 data = %{
649 "@context" => "https://www.w3.org/ns/activitystreams",
650 "actor" => announcer.ap_id,
651 "id" => "#{announcer.ap_id}/statuses/19512778738411822/activity",
652 "object" => post.data["object"],
653 "to" => "https://www.w3.org/ns/activitystreams#Public",
654 "cc" => [user.ap_id],
655 "type" => "Announce"
656 }
657
658 conn =
659 conn
660 |> assign(:valid_signature, true)
661 |> put_req_header("content-type", "application/activity+json")
662 |> post("/users/#{user.nickname}/inbox", data)
663
664 assert "ok" == json_response(conn, 200)
665 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
666 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
667 assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients
668 end
669
670 test "it accepts messages from actors that are followed by the user", %{
671 conn: conn,
672 data: data
673 } do
674 recipient = insert(:user)
675 actor = insert(:user, %{ap_id: "http://mastodon.example.org/users/actor"})
676
677 {:ok, recipient, actor} = User.follow(recipient, actor)
678
679 object =
680 data["object"]
681 |> Map.put("attributedTo", actor.ap_id)
682
683 data =
684 data
685 |> Map.put("actor", actor.ap_id)
686 |> Map.put("object", object)
687
688 conn =
689 conn
690 |> assign(:valid_signature, true)
691 |> put_req_header("content-type", "application/activity+json")
692 |> post("/users/#{recipient.nickname}/inbox", data)
693
694 assert "ok" == json_response(conn, 200)
695 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
696 assert Activity.get_by_ap_id(data["id"])
697 end
698
699 test "it rejects reads from other users", %{conn: conn} do
700 user = insert(:user)
701 other_user = insert(:user)
702
703 conn =
704 conn
705 |> assign(:user, other_user)
706 |> put_req_header("accept", "application/activity+json")
707 |> get("/users/#{user.nickname}/inbox")
708
709 assert json_response(conn, 403)
710 end
711
712 test "it returns a note activity in a collection", %{conn: conn} do
713 note_activity = insert(:direct_note_activity)
714 note_object = Object.normalize(note_activity, fetch: false)
715 user = User.get_cached_by_ap_id(hd(note_activity.data["to"]))
716
717 conn =
718 conn
719 |> assign(:user, user)
720 |> put_req_header("accept", "application/activity+json")
721 |> get("/users/#{user.nickname}/inbox?page=true")
722
723 assert response(conn, 200) =~ note_object.data["content"]
724 end
725
726 test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do
727 user = insert(:user)
728 data = Map.put(data, "bcc", [user.ap_id])
729
730 sender_host = URI.parse(data["actor"]).host
731 Instances.set_consistently_unreachable(sender_host)
732 refute Instances.reachable?(sender_host)
733
734 conn =
735 conn
736 |> assign(:valid_signature, true)
737 |> put_req_header("content-type", "application/activity+json")
738 |> post("/users/#{user.nickname}/inbox", data)
739
740 assert "ok" == json_response(conn, 200)
741 assert Instances.reachable?(sender_host)
742 end
743
744 test "it removes all follower collections but actor's", %{conn: conn} do
745 [actor, recipient] = insert_pair(:user)
746
747 data =
748 File.read!("test/fixtures/activitypub-client-post-activity.json")
749 |> Jason.decode!()
750
751 object = Map.put(data["object"], "attributedTo", actor.ap_id)
752
753 data =
754 data
755 |> Map.put("id", Utils.generate_object_id())
756 |> Map.put("actor", actor.ap_id)
757 |> Map.put("object", object)
758 |> Map.put("cc", [
759 recipient.follower_address,
760 actor.follower_address
761 ])
762 |> Map.put("to", [
763 recipient.ap_id,
764 recipient.follower_address,
765 "https://www.w3.org/ns/activitystreams#Public"
766 ])
767
768 conn
769 |> assign(:valid_signature, true)
770 |> put_req_header("content-type", "application/activity+json")
771 |> post("/users/#{recipient.nickname}/inbox", data)
772 |> json_response(200)
773
774 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
775
776 activity = Activity.get_by_ap_id(data["id"])
777
778 assert activity.id
779 assert actor.follower_address in activity.recipients
780 assert actor.follower_address in activity.data["cc"]
781
782 refute recipient.follower_address in activity.recipients
783 refute recipient.follower_address in activity.data["cc"]
784 refute recipient.follower_address in activity.data["to"]
785 end
786
787 test "it requires authentication", %{conn: conn} do
788 user = insert(:user)
789 conn = put_req_header(conn, "accept", "application/activity+json")
790
791 ret_conn = get(conn, "/users/#{user.nickname}/inbox")
792 assert json_response(ret_conn, 403)
793
794 ret_conn =
795 conn
796 |> assign(:user, user)
797 |> get("/users/#{user.nickname}/inbox")
798
799 assert json_response(ret_conn, 200)
800 end
801
802 @tag capture_log: true
803 test "forwarded report", %{conn: conn} do
804 admin = insert(:user, is_admin: true)
805 actor = insert(:user, local: false)
806 remote_domain = URI.parse(actor.ap_id).host
807 reported_user = insert(:user)
808
809 note = insert(:note_activity, user: reported_user)
810
811 data = %{
812 "@context" => [
813 "https://www.w3.org/ns/activitystreams",
814 "https://#{remote_domain}/schemas/litepub-0.1.jsonld",
815 %{
816 "@language" => "und"
817 }
818 ],
819 "actor" => actor.ap_id,
820 "cc" => [
821 reported_user.ap_id
822 ],
823 "content" => "test",
824 "context" => "context",
825 "id" => "http://#{remote_domain}/activities/02be56cf-35e3-46b4-b2c6-47ae08dfee9e",
826 "nickname" => reported_user.nickname,
827 "object" => [
828 reported_user.ap_id,
829 %{
830 "actor" => %{
831 "actor_type" => "Person",
832 "approval_pending" => false,
833 "avatar" => "",
834 "confirmation_pending" => false,
835 "deactivated" => false,
836 "display_name" => "test user",
837 "id" => reported_user.id,
838 "local" => false,
839 "nickname" => reported_user.nickname,
840 "registration_reason" => nil,
841 "roles" => %{
842 "admin" => false,
843 "moderator" => false
844 },
845 "tags" => [],
846 "url" => reported_user.ap_id
847 },
848 "content" => "",
849 "id" => note.data["id"],
850 "published" => note.data["published"],
851 "type" => "Note"
852 }
853 ],
854 "published" => note.data["published"],
855 "state" => "open",
856 "to" => [],
857 "type" => "Flag"
858 }
859
860 conn
861 |> assign(:valid_signature, true)
862 |> put_req_header("content-type", "application/activity+json")
863 |> post("/users/#{reported_user.nickname}/inbox", data)
864 |> json_response(200)
865
866 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
867
868 assert Pleroma.Repo.aggregate(Activity, :count, :id) == 2
869
870 ObanHelpers.perform_all()
871
872 Swoosh.TestAssertions.assert_email_sent(
873 to: {admin.name, admin.email},
874 html_body: ~r/Reported Account:/i
875 )
876 end
877
878 @tag capture_log: true
879 test "forwarded report from mastodon", %{conn: conn} do
880 admin = insert(:user, is_admin: true)
881 actor = insert(:user, local: false)
882 remote_domain = URI.parse(actor.ap_id).host
883 remote_actor = "https://#{remote_domain}/actor"
884 [reported_user, another] = insert_list(2, :user)
885
886 note = insert(:note_activity, user: reported_user)
887
888 Pleroma.Web.CommonAPI.favorite(another, note.id)
889
890 mock_json_body =
891 "test/fixtures/mastodon/application_actor.json"
892 |> File.read!()
893 |> String.replace("{{DOMAIN}}", remote_domain)
894
895 Tesla.Mock.mock(fn %{url: ^remote_actor} ->
896 %Tesla.Env{
897 status: 200,
898 body: mock_json_body,
899 headers: [{"content-type", "application/activity+json"}]
900 }
901 end)
902
903 data = %{
904 "@context" => "https://www.w3.org/ns/activitystreams",
905 "actor" => remote_actor,
906 "content" => "test report",
907 "id" => "https://#{remote_domain}/e3b12fd1-948c-446e-b93b-a5e67edbe1d8",
908 "nickname" => reported_user.nickname,
909 "object" => [
910 reported_user.ap_id,
911 note.data["object"]
912 ],
913 "type" => "Flag"
914 }
915
916 conn
917 |> assign(:valid_signature, true)
918 |> put_req_header("content-type", "application/activity+json")
919 |> post("/users/#{reported_user.nickname}/inbox", data)
920 |> json_response(200)
921
922 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
923
924 flag_activity = "Flag" |> Pleroma.Activity.Queries.by_type() |> Pleroma.Repo.one()
925 reported_user_ap_id = reported_user.ap_id
926
927 [^reported_user_ap_id, flag_data] = flag_activity.data["object"]
928
929 Enum.each(~w(actor content id published type), &Map.has_key?(flag_data, &1))
930 ObanHelpers.perform_all()
931
932 Swoosh.TestAssertions.assert_email_sent(
933 to: {admin.name, admin.email},
934 html_body: ~r/#{note.data["object"]}/i
935 )
936 end
937 end
938
939 describe "GET /users/:nickname/outbox" do
940 test "it paginates correctly", %{conn: conn} do
941 user = insert(:user)
942 conn = assign(conn, :user, user)
943 outbox_endpoint = user.ap_id <> "/outbox"
944
945 _posts =
946 for i <- 0..25 do
947 {:ok, activity} = CommonAPI.post(user, %{status: "post #{i}"})
948 activity
949 end
950
951 result =
952 conn
953 |> put_req_header("accept", "application/activity+json")
954 |> get(outbox_endpoint <> "?page=true")
955 |> json_response(200)
956
957 result_ids = Enum.map(result["orderedItems"], fn x -> x["id"] end)
958 assert length(result["orderedItems"]) == 20
959 assert length(result_ids) == 20
960 assert result["next"]
961 assert String.starts_with?(result["next"], outbox_endpoint)
962
963 result_next =
964 conn
965 |> put_req_header("accept", "application/activity+json")
966 |> get(result["next"])
967 |> json_response(200)
968
969 result_next_ids = Enum.map(result_next["orderedItems"], fn x -> x["id"] end)
970 assert length(result_next["orderedItems"]) == 6
971 assert length(result_next_ids) == 6
972 refute Enum.find(result_next_ids, fn x -> x in result_ids end)
973 refute Enum.find(result_ids, fn x -> x in result_next_ids end)
974 assert String.starts_with?(result["id"], outbox_endpoint)
975
976 result_next_again =
977 conn
978 |> put_req_header("accept", "application/activity+json")
979 |> get(result_next["id"])
980 |> json_response(200)
981
982 assert result_next == result_next_again
983 end
984
985 test "it returns 200 even if there're no activities", %{conn: conn} do
986 user = insert(:user)
987 outbox_endpoint = user.ap_id <> "/outbox"
988
989 conn =
990 conn
991 |> assign(:user, user)
992 |> put_req_header("accept", "application/activity+json")
993 |> get(outbox_endpoint)
994
995 result = json_response(conn, 200)
996 assert outbox_endpoint == result["id"]
997 end
998
999 test "it returns a note activity in a collection", %{conn: conn} do
1000 note_activity = insert(:note_activity)
1001 note_object = Object.normalize(note_activity, fetch: false)
1002 user = User.get_cached_by_ap_id(note_activity.data["actor"])
1003
1004 conn =
1005 conn
1006 |> assign(:user, user)
1007 |> put_req_header("accept", "application/activity+json")
1008 |> get("/users/#{user.nickname}/outbox?page=true")
1009
1010 assert response(conn, 200) =~ note_object.data["content"]
1011 end
1012
1013 test "it returns an announce activity in a collection", %{conn: conn} do
1014 announce_activity = insert(:announce_activity)
1015 user = User.get_cached_by_ap_id(announce_activity.data["actor"])
1016
1017 conn =
1018 conn
1019 |> assign(:user, user)
1020 |> put_req_header("accept", "application/activity+json")
1021 |> get("/users/#{user.nickname}/outbox?page=true")
1022
1023 assert response(conn, 200) =~ announce_activity.data["object"]
1024 end
1025 end
1026
1027 describe "POST /users/:nickname/outbox (C2S)" do
1028 setup do: clear_config([:instance, :limit])
1029
1030 setup do
1031 [
1032 activity: %{
1033 "@context" => "https://www.w3.org/ns/activitystreams",
1034 "type" => "Create",
1035 "object" => %{"type" => "Note", "content" => "AP C2S test"},
1036 "to" => "https://www.w3.org/ns/activitystreams#Public",
1037 "cc" => []
1038 }
1039 ]
1040 end
1041
1042 test "it rejects posts from other users / unauthenticated users", %{
1043 conn: conn,
1044 activity: activity
1045 } do
1046 user = insert(:user)
1047 other_user = insert(:user)
1048 conn = put_req_header(conn, "content-type", "application/activity+json")
1049
1050 conn
1051 |> post("/users/#{user.nickname}/outbox", activity)
1052 |> json_response(403)
1053
1054 conn
1055 |> assign(:user, other_user)
1056 |> post("/users/#{user.nickname}/outbox", activity)
1057 |> json_response(403)
1058 end
1059
1060 test "it inserts an incoming create activity into the database", %{
1061 conn: conn,
1062 activity: activity
1063 } do
1064 user = insert(:user)
1065
1066 result =
1067 conn
1068 |> assign(:user, user)
1069 |> put_req_header("content-type", "application/activity+json")
1070 |> post("/users/#{user.nickname}/outbox", activity)
1071 |> json_response(201)
1072
1073 assert Activity.get_by_ap_id(result["id"])
1074 assert result["object"]
1075 assert %Object{data: object} = Object.normalize(result["object"], fetch: false)
1076 assert object["content"] == activity["object"]["content"]
1077 end
1078
1079 test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do
1080 user = insert(:user)
1081
1082 activity =
1083 activity
1084 |> put_in(["object", "type"], "Benis")
1085
1086 _result =
1087 conn
1088 |> assign(:user, user)
1089 |> put_req_header("content-type", "application/activity+json")
1090 |> post("/users/#{user.nickname}/outbox", activity)
1091 |> json_response(400)
1092 end
1093
1094 test "it inserts an incoming sensitive activity into the database", %{
1095 conn: conn,
1096 activity: activity
1097 } do
1098 user = insert(:user)
1099 conn = assign(conn, :user, user)
1100 object = Map.put(activity["object"], "sensitive", true)
1101 activity = Map.put(activity, "object", object)
1102
1103 response =
1104 conn
1105 |> put_req_header("content-type", "application/activity+json")
1106 |> post("/users/#{user.nickname}/outbox", activity)
1107 |> json_response(201)
1108
1109 assert Activity.get_by_ap_id(response["id"])
1110 assert response["object"]
1111 assert %Object{data: response_object} = Object.normalize(response["object"], fetch: false)
1112 assert response_object["sensitive"] == true
1113 assert response_object["content"] == activity["object"]["content"]
1114
1115 representation =
1116 conn
1117 |> put_req_header("accept", "application/activity+json")
1118 |> get(response["id"])
1119 |> json_response(200)
1120
1121 assert representation["object"]["sensitive"] == true
1122 end
1123
1124 test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do
1125 user = insert(:user)
1126 activity = Map.put(activity, "type", "BadType")
1127
1128 conn =
1129 conn
1130 |> assign(:user, user)
1131 |> put_req_header("content-type", "application/activity+json")
1132 |> post("/users/#{user.nickname}/outbox", activity)
1133
1134 assert json_response(conn, 400)
1135 end
1136
1137 test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
1138 note_activity = insert(:note_activity)
1139 note_object = Object.normalize(note_activity, fetch: false)
1140 user = User.get_cached_by_ap_id(note_activity.data["actor"])
1141
1142 data = %{
1143 type: "Delete",
1144 object: %{
1145 id: note_object.data["id"]
1146 }
1147 }
1148
1149 conn =
1150 conn
1151 |> assign(:user, user)
1152 |> put_req_header("content-type", "application/activity+json")
1153 |> post("/users/#{user.nickname}/outbox", data)
1154
1155 result = json_response(conn, 201)
1156 assert Activity.get_by_ap_id(result["id"])
1157
1158 assert object = Object.get_by_ap_id(note_object.data["id"])
1159 assert object.data["type"] == "Tombstone"
1160 end
1161
1162 test "it rejects delete activity of object from other actor", %{conn: conn} do
1163 note_activity = insert(:note_activity)
1164 note_object = Object.normalize(note_activity, fetch: false)
1165 user = insert(:user)
1166
1167 data = %{
1168 type: "Delete",
1169 object: %{
1170 id: note_object.data["id"]
1171 }
1172 }
1173
1174 conn =
1175 conn
1176 |> assign(:user, user)
1177 |> put_req_header("content-type", "application/activity+json")
1178 |> post("/users/#{user.nickname}/outbox", data)
1179
1180 assert json_response(conn, 400)
1181 end
1182
1183 test "it increases like count when receiving a like action", %{conn: conn} do
1184 note_activity = insert(:note_activity)
1185 note_object = Object.normalize(note_activity, fetch: false)
1186 user = User.get_cached_by_ap_id(note_activity.data["actor"])
1187
1188 data = %{
1189 type: "Like",
1190 object: %{
1191 id: note_object.data["id"]
1192 }
1193 }
1194
1195 conn =
1196 conn
1197 |> assign(:user, user)
1198 |> put_req_header("content-type", "application/activity+json")
1199 |> post("/users/#{user.nickname}/outbox", data)
1200
1201 result = json_response(conn, 201)
1202 assert Activity.get_by_ap_id(result["id"])
1203
1204 assert object = Object.get_by_ap_id(note_object.data["id"])
1205 assert object.data["like_count"] == 1
1206 end
1207
1208 test "it doesn't spreads faulty attributedTo or actor fields", %{
1209 conn: conn,
1210 activity: activity
1211 } do
1212 reimu = insert(:user, nickname: "reimu")
1213 cirno = insert(:user, nickname: "cirno")
1214
1215 assert reimu.ap_id
1216 assert cirno.ap_id
1217
1218 activity =
1219 activity
1220 |> put_in(["object", "actor"], reimu.ap_id)
1221 |> put_in(["object", "attributedTo"], reimu.ap_id)
1222 |> put_in(["actor"], reimu.ap_id)
1223 |> put_in(["attributedTo"], reimu.ap_id)
1224
1225 _reimu_outbox =
1226 conn
1227 |> assign(:user, cirno)
1228 |> put_req_header("content-type", "application/activity+json")
1229 |> post("/users/#{reimu.nickname}/outbox", activity)
1230 |> json_response(403)
1231
1232 cirno_outbox =
1233 conn
1234 |> assign(:user, cirno)
1235 |> put_req_header("content-type", "application/activity+json")
1236 |> post("/users/#{cirno.nickname}/outbox", activity)
1237 |> json_response(201)
1238
1239 assert cirno_outbox["attributedTo"] == nil
1240 assert cirno_outbox["actor"] == cirno.ap_id
1241
1242 assert cirno_object = Object.normalize(cirno_outbox["object"], fetch: false)
1243 assert cirno_object.data["actor"] == cirno.ap_id
1244 assert cirno_object.data["attributedTo"] == cirno.ap_id
1245 end
1246
1247 test "Character limitation", %{conn: conn, activity: activity} do
1248 clear_config([:instance, :limit], 5)
1249 user = insert(:user)
1250
1251 result =
1252 conn
1253 |> assign(:user, user)
1254 |> put_req_header("content-type", "application/activity+json")
1255 |> post("/users/#{user.nickname}/outbox", activity)
1256 |> json_response(400)
1257
1258 assert result == "Note is over the character limit"
1259 end
1260 end
1261
1262 describe "/relay/followers" do
1263 test "it returns relay followers", %{conn: conn} do
1264 relay_actor = Relay.get_actor()
1265 user = insert(:user)
1266 User.follow(user, relay_actor)
1267
1268 result =
1269 conn
1270 |> get("/relay/followers")
1271 |> json_response(200)
1272
1273 assert result["first"]["orderedItems"] == [user.ap_id]
1274 end
1275
1276 test "on non-federating instance, it returns 404", %{conn: conn} do
1277 clear_config([:instance, :federating], false)
1278 user = insert(:user)
1279
1280 conn
1281 |> assign(:user, user)
1282 |> get("/relay/followers")
1283 |> json_response(404)
1284 end
1285 end
1286
1287 describe "/relay/following" do
1288 test "it returns relay following", %{conn: conn} do
1289 result =
1290 conn
1291 |> get("/relay/following")
1292 |> json_response(200)
1293
1294 assert result["first"]["orderedItems"] == []
1295 end
1296
1297 test "on non-federating instance, it returns 404", %{conn: conn} do
1298 clear_config([:instance, :federating], false)
1299 user = insert(:user)
1300
1301 conn
1302 |> assign(:user, user)
1303 |> get("/relay/following")
1304 |> json_response(404)
1305 end
1306 end
1307
1308 describe "/users/:nickname/followers" do
1309 test "it returns the followers in a collection", %{conn: conn} do
1310 user = insert(:user)
1311 user_two = insert(:user)
1312 User.follow(user, user_two)
1313
1314 result =
1315 conn
1316 |> assign(:user, user_two)
1317 |> get("/users/#{user_two.nickname}/followers")
1318 |> json_response(200)
1319
1320 assert result["first"]["orderedItems"] == [user.ap_id]
1321 end
1322
1323 test "it returns a uri if the user has 'hide_followers' set", %{conn: conn} do
1324 user = insert(:user)
1325 user_two = insert(:user, hide_followers: true)
1326 User.follow(user, user_two)
1327
1328 result =
1329 conn
1330 |> assign(:user, user)
1331 |> get("/users/#{user_two.nickname}/followers")
1332 |> json_response(200)
1333
1334 assert is_binary(result["first"])
1335 end
1336
1337 test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is from another user",
1338 %{conn: conn} do
1339 user = insert(:user)
1340 other_user = insert(:user, hide_followers: true)
1341
1342 result =
1343 conn
1344 |> assign(:user, user)
1345 |> get("/users/#{other_user.nickname}/followers?page=1")
1346
1347 assert result.status == 403
1348 assert result.resp_body == ""
1349 end
1350
1351 test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user",
1352 %{conn: conn} do
1353 user = insert(:user, hide_followers: true)
1354 other_user = insert(:user)
1355 {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
1356
1357 result =
1358 conn
1359 |> assign(:user, user)
1360 |> get("/users/#{user.nickname}/followers?page=1")
1361 |> json_response(200)
1362
1363 assert result["totalItems"] == 1
1364 assert result["orderedItems"] == [other_user.ap_id]
1365 end
1366
1367 test "it works for more than 10 users", %{conn: conn} do
1368 user = insert(:user)
1369
1370 Enum.each(1..15, fn _ ->
1371 other_user = insert(:user)
1372 User.follow(other_user, user)
1373 end)
1374
1375 result =
1376 conn
1377 |> assign(:user, user)
1378 |> get("/users/#{user.nickname}/followers")
1379 |> json_response(200)
1380
1381 assert length(result["first"]["orderedItems"]) == 10
1382 assert result["first"]["totalItems"] == 15
1383 assert result["totalItems"] == 15
1384
1385 result =
1386 conn
1387 |> assign(:user, user)
1388 |> get("/users/#{user.nickname}/followers?page=2")
1389 |> json_response(200)
1390
1391 assert length(result["orderedItems"]) == 5
1392 assert result["totalItems"] == 15
1393 end
1394
1395 test "does not require authentication", %{conn: conn} do
1396 user = insert(:user)
1397
1398 conn
1399 |> get("/users/#{user.nickname}/followers")
1400 |> json_response(200)
1401 end
1402 end
1403
1404 describe "/users/:nickname/following" do
1405 test "it returns the following in a collection", %{conn: conn} do
1406 user = insert(:user)
1407 user_two = insert(:user)
1408 User.follow(user, user_two)
1409
1410 result =
1411 conn
1412 |> assign(:user, user)
1413 |> get("/users/#{user.nickname}/following")
1414 |> json_response(200)
1415
1416 assert result["first"]["orderedItems"] == [user_two.ap_id]
1417 end
1418
1419 test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do
1420 user = insert(:user)
1421 user_two = insert(:user, hide_follows: true)
1422 User.follow(user, user_two)
1423
1424 result =
1425 conn
1426 |> assign(:user, user)
1427 |> get("/users/#{user_two.nickname}/following")
1428 |> json_response(200)
1429
1430 assert is_binary(result["first"])
1431 end
1432
1433 test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is from another user",
1434 %{conn: conn} do
1435 user = insert(:user)
1436 user_two = insert(:user, hide_follows: true)
1437
1438 result =
1439 conn
1440 |> assign(:user, user)
1441 |> get("/users/#{user_two.nickname}/following?page=1")
1442
1443 assert result.status == 403
1444 assert result.resp_body == ""
1445 end
1446
1447 test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user",
1448 %{conn: conn} do
1449 user = insert(:user, hide_follows: true)
1450 other_user = insert(:user)
1451 {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
1452
1453 result =
1454 conn
1455 |> assign(:user, user)
1456 |> get("/users/#{user.nickname}/following?page=1")
1457 |> json_response(200)
1458
1459 assert result["totalItems"] == 1
1460 assert result["orderedItems"] == [other_user.ap_id]
1461 end
1462
1463 test "it works for more than 10 users", %{conn: conn} do
1464 user = insert(:user)
1465
1466 Enum.each(1..15, fn _ ->
1467 user = User.get_cached_by_id(user.id)
1468 other_user = insert(:user)
1469 User.follow(user, other_user)
1470 end)
1471
1472 result =
1473 conn
1474 |> assign(:user, user)
1475 |> get("/users/#{user.nickname}/following")
1476 |> json_response(200)
1477
1478 assert length(result["first"]["orderedItems"]) == 10
1479 assert result["first"]["totalItems"] == 15
1480 assert result["totalItems"] == 15
1481
1482 result =
1483 conn
1484 |> assign(:user, user)
1485 |> get("/users/#{user.nickname}/following?page=2")
1486 |> json_response(200)
1487
1488 assert length(result["orderedItems"]) == 5
1489 assert result["totalItems"] == 15
1490 end
1491
1492 test "does not require authentication", %{conn: conn} do
1493 user = insert(:user)
1494
1495 conn
1496 |> get("/users/#{user.nickname}/following")
1497 |> json_response(200)
1498 end
1499 end
1500
1501 describe "delivery tracking" do
1502 test "it tracks a signed object fetch", %{conn: conn} do
1503 user = insert(:user, local: false)
1504 activity = insert(:note_activity)
1505 object = Object.normalize(activity, fetch: false)
1506
1507 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1508
1509 conn
1510 |> put_req_header("accept", "application/activity+json")
1511 |> assign(:user, user)
1512 |> get(object_path)
1513 |> json_response(200)
1514
1515 assert Delivery.get(object.id, user.id)
1516 end
1517
1518 test "it tracks a signed activity fetch", %{conn: conn} do
1519 user = insert(:user, local: false)
1520 activity = insert(:note_activity)
1521 object = Object.normalize(activity, fetch: false)
1522
1523 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1524
1525 conn
1526 |> put_req_header("accept", "application/activity+json")
1527 |> assign(:user, user)
1528 |> get(activity_path)
1529 |> json_response(200)
1530
1531 assert Delivery.get(object.id, user.id)
1532 end
1533
1534 test "it tracks a signed object fetch when the json is cached", %{conn: conn} do
1535 user = insert(:user, local: false)
1536 other_user = insert(:user, local: false)
1537 activity = insert(:note_activity)
1538 object = Object.normalize(activity, fetch: false)
1539
1540 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1541
1542 conn
1543 |> put_req_header("accept", "application/activity+json")
1544 |> assign(:user, user)
1545 |> get(object_path)
1546 |> json_response(200)
1547
1548 build_conn()
1549 |> put_req_header("accept", "application/activity+json")
1550 |> assign(:user, other_user)
1551 |> get(object_path)
1552 |> json_response(200)
1553
1554 assert Delivery.get(object.id, user.id)
1555 assert Delivery.get(object.id, other_user.id)
1556 end
1557
1558 test "it tracks a signed activity fetch when the json is cached", %{conn: conn} do
1559 user = insert(:user, local: false)
1560 other_user = insert(:user, local: false)
1561 activity = insert(:note_activity)
1562 object = Object.normalize(activity, fetch: false)
1563
1564 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1565
1566 conn
1567 |> put_req_header("accept", "application/activity+json")
1568 |> assign(:user, user)
1569 |> get(activity_path)
1570 |> json_response(200)
1571
1572 build_conn()
1573 |> put_req_header("accept", "application/activity+json")
1574 |> assign(:user, other_user)
1575 |> get(activity_path)
1576 |> json_response(200)
1577
1578 assert Delivery.get(object.id, user.id)
1579 assert Delivery.get(object.id, other_user.id)
1580 end
1581 end
1582
1583 describe "Additional ActivityPub C2S endpoints" do
1584 test "GET /api/ap/whoami", %{conn: conn} do
1585 user = insert(:user)
1586
1587 conn =
1588 conn
1589 |> assign(:user, user)
1590 |> get("/api/ap/whoami")
1591
1592 user = User.get_cached_by_id(user.id)
1593
1594 assert UserView.render("user.json", %{user: user}) == json_response(conn, 200)
1595
1596 conn
1597 |> get("/api/ap/whoami")
1598 |> json_response(403)
1599 end
1600
1601 setup do: clear_config([:media_proxy])
1602 setup do: clear_config([Pleroma.Upload])
1603
1604 test "POST /api/ap/upload_media", %{conn: conn} do
1605 user = insert(:user)
1606
1607 desc = "Description of the image"
1608
1609 image = %Plug.Upload{
1610 content_type: "image/jpeg",
1611 path: Path.absname("test/fixtures/image.jpg"),
1612 filename: "an_image.jpg"
1613 }
1614
1615 object =
1616 conn
1617 |> assign(:user, user)
1618 |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
1619 |> json_response(:created)
1620
1621 assert object["name"] == desc
1622 assert object["type"] == "Document"
1623 assert object["actor"] == user.ap_id
1624 assert [%{"href" => object_href, "mediaType" => object_mediatype}] = object["url"]
1625 assert is_binary(object_href)
1626 assert object_mediatype == "image/jpeg"
1627 assert String.ends_with?(object_href, ".jpg")
1628
1629 activity_request = %{
1630 "@context" => "https://www.w3.org/ns/activitystreams",
1631 "type" => "Create",
1632 "object" => %{
1633 "type" => "Note",
1634 "content" => "AP C2S test, attachment",
1635 "attachment" => [object]
1636 },
1637 "to" => "https://www.w3.org/ns/activitystreams#Public",
1638 "cc" => []
1639 }
1640
1641 activity_response =
1642 conn
1643 |> assign(:user, user)
1644 |> post("/users/#{user.nickname}/outbox", activity_request)
1645 |> json_response(:created)
1646
1647 assert activity_response["id"]
1648 assert activity_response["object"]
1649 assert activity_response["actor"] == user.ap_id
1650
1651 assert %Object{data: %{"attachment" => [attachment]}} =
1652 Object.normalize(activity_response["object"], fetch: false)
1653
1654 assert attachment["type"] == "Document"
1655 assert attachment["name"] == desc
1656
1657 assert [
1658 %{
1659 "href" => ^object_href,
1660 "type" => "Link",
1661 "mediaType" => ^object_mediatype
1662 }
1663 ] = attachment["url"]
1664
1665 # Fails if unauthenticated
1666 conn
1667 |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
1668 |> json_response(403)
1669 end
1670 end
1671 end