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