Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
[akkoma] / test / pleroma / web / activity_pub / activity_pub_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 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, 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)
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)
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 end
1027
1028 describe "POST /users/:nickname/outbox (C2S)" do
1029 setup do: clear_config([:instance, :limit])
1030
1031 setup do
1032 [
1033 activity: %{
1034 "@context" => "https://www.w3.org/ns/activitystreams",
1035 "type" => "Create",
1036 "object" => %{"type" => "Note", "content" => "AP C2S test"},
1037 "to" => "https://www.w3.org/ns/activitystreams#Public",
1038 "cc" => []
1039 }
1040 ]
1041 end
1042
1043 test "it rejects posts from other users / unauthenticated users", %{
1044 conn: conn,
1045 activity: activity
1046 } do
1047 user = insert(:user)
1048 other_user = insert(:user)
1049 conn = put_req_header(conn, "content-type", "application/activity+json")
1050
1051 conn
1052 |> post("/users/#{user.nickname}/outbox", activity)
1053 |> json_response(403)
1054
1055 conn
1056 |> assign(:user, other_user)
1057 |> post("/users/#{user.nickname}/outbox", activity)
1058 |> json_response(403)
1059 end
1060
1061 test "it inserts an incoming create activity into the database", %{
1062 conn: conn,
1063 activity: activity
1064 } do
1065 user = insert(:user)
1066
1067 result =
1068 conn
1069 |> assign(:user, user)
1070 |> put_req_header("content-type", "application/activity+json")
1071 |> post("/users/#{user.nickname}/outbox", activity)
1072 |> json_response(201)
1073
1074 assert Activity.get_by_ap_id(result["id"])
1075 assert result["object"]
1076 assert %Object{data: object} = Object.normalize(result["object"])
1077 assert object["content"] == activity["object"]["content"]
1078 end
1079
1080 test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do
1081 user = insert(:user)
1082
1083 activity =
1084 activity
1085 |> put_in(["object", "type"], "Benis")
1086
1087 _result =
1088 conn
1089 |> assign(:user, user)
1090 |> put_req_header("content-type", "application/activity+json")
1091 |> post("/users/#{user.nickname}/outbox", activity)
1092 |> json_response(400)
1093 end
1094
1095 test "it inserts an incoming sensitive activity into the database", %{
1096 conn: conn,
1097 activity: activity
1098 } do
1099 user = insert(:user)
1100 conn = assign(conn, :user, user)
1101 object = Map.put(activity["object"], "sensitive", true)
1102 activity = Map.put(activity, "object", object)
1103
1104 response =
1105 conn
1106 |> put_req_header("content-type", "application/activity+json")
1107 |> post("/users/#{user.nickname}/outbox", activity)
1108 |> json_response(201)
1109
1110 assert Activity.get_by_ap_id(response["id"])
1111 assert response["object"]
1112 assert %Object{data: response_object} = Object.normalize(response["object"])
1113 assert response_object["sensitive"] == true
1114 assert response_object["content"] == activity["object"]["content"]
1115
1116 representation =
1117 conn
1118 |> put_req_header("accept", "application/activity+json")
1119 |> get(response["id"])
1120 |> json_response(200)
1121
1122 assert representation["object"]["sensitive"] == true
1123 end
1124
1125 test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do
1126 user = insert(:user)
1127 activity = Map.put(activity, "type", "BadType")
1128
1129 conn =
1130 conn
1131 |> assign(:user, user)
1132 |> put_req_header("content-type", "application/activity+json")
1133 |> post("/users/#{user.nickname}/outbox", activity)
1134
1135 assert json_response(conn, 400)
1136 end
1137
1138 test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
1139 note_activity = insert(:note_activity)
1140 note_object = Object.normalize(note_activity)
1141 user = User.get_cached_by_ap_id(note_activity.data["actor"])
1142
1143 data = %{
1144 type: "Delete",
1145 object: %{
1146 id: note_object.data["id"]
1147 }
1148 }
1149
1150 conn =
1151 conn
1152 |> assign(:user, user)
1153 |> put_req_header("content-type", "application/activity+json")
1154 |> post("/users/#{user.nickname}/outbox", data)
1155
1156 result = json_response(conn, 201)
1157 assert Activity.get_by_ap_id(result["id"])
1158
1159 assert object = Object.get_by_ap_id(note_object.data["id"])
1160 assert object.data["type"] == "Tombstone"
1161 end
1162
1163 test "it rejects delete activity of object from other actor", %{conn: conn} do
1164 note_activity = insert(:note_activity)
1165 note_object = Object.normalize(note_activity)
1166 user = insert(:user)
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 assert json_response(conn, 400)
1182 end
1183
1184 test "it increases like count when receiving a like action", %{conn: conn} do
1185 note_activity = insert(:note_activity)
1186 note_object = Object.normalize(note_activity)
1187 user = User.get_cached_by_ap_id(note_activity.data["actor"])
1188
1189 data = %{
1190 type: "Like",
1191 object: %{
1192 id: note_object.data["id"]
1193 }
1194 }
1195
1196 conn =
1197 conn
1198 |> assign(:user, user)
1199 |> put_req_header("content-type", "application/activity+json")
1200 |> post("/users/#{user.nickname}/outbox", data)
1201
1202 result = json_response(conn, 201)
1203 assert Activity.get_by_ap_id(result["id"])
1204
1205 assert object = Object.get_by_ap_id(note_object.data["id"])
1206 assert object.data["like_count"] == 1
1207 end
1208
1209 test "it doesn't spreads faulty attributedTo or actor fields", %{
1210 conn: conn,
1211 activity: activity
1212 } do
1213 reimu = insert(:user, nickname: "reimu")
1214 cirno = insert(:user, nickname: "cirno")
1215
1216 assert reimu.ap_id
1217 assert cirno.ap_id
1218
1219 activity =
1220 activity
1221 |> put_in(["object", "actor"], reimu.ap_id)
1222 |> put_in(["object", "attributedTo"], reimu.ap_id)
1223 |> put_in(["actor"], reimu.ap_id)
1224 |> put_in(["attributedTo"], reimu.ap_id)
1225
1226 _reimu_outbox =
1227 conn
1228 |> assign(:user, cirno)
1229 |> put_req_header("content-type", "application/activity+json")
1230 |> post("/users/#{reimu.nickname}/outbox", activity)
1231 |> json_response(403)
1232
1233 cirno_outbox =
1234 conn
1235 |> assign(:user, cirno)
1236 |> put_req_header("content-type", "application/activity+json")
1237 |> post("/users/#{cirno.nickname}/outbox", activity)
1238 |> json_response(201)
1239
1240 assert cirno_outbox["attributedTo"] == nil
1241 assert cirno_outbox["actor"] == cirno.ap_id
1242
1243 assert cirno_object = Object.normalize(cirno_outbox["object"])
1244 assert cirno_object.data["actor"] == cirno.ap_id
1245 assert cirno_object.data["attributedTo"] == cirno.ap_id
1246 end
1247
1248 test "Character limitation", %{conn: conn, activity: activity} do
1249 Pleroma.Config.put([:instance, :limit], 5)
1250 user = insert(:user)
1251
1252 result =
1253 conn
1254 |> assign(:user, user)
1255 |> put_req_header("content-type", "application/activity+json")
1256 |> post("/users/#{user.nickname}/outbox", activity)
1257 |> json_response(400)
1258
1259 assert result == "Note is over the character limit"
1260 end
1261 end
1262
1263 describe "/relay/followers" do
1264 test "it returns relay followers", %{conn: conn} do
1265 relay_actor = Relay.get_actor()
1266 user = insert(:user)
1267 User.follow(user, relay_actor)
1268
1269 result =
1270 conn
1271 |> get("/relay/followers")
1272 |> json_response(200)
1273
1274 assert result["first"]["orderedItems"] == [user.ap_id]
1275 end
1276
1277 test "on non-federating instance, it returns 404", %{conn: conn} do
1278 Config.put([:instance, :federating], false)
1279 user = insert(:user)
1280
1281 conn
1282 |> assign(:user, user)
1283 |> get("/relay/followers")
1284 |> json_response(404)
1285 end
1286 end
1287
1288 describe "/relay/following" do
1289 test "it returns relay following", %{conn: conn} do
1290 result =
1291 conn
1292 |> get("/relay/following")
1293 |> json_response(200)
1294
1295 assert result["first"]["orderedItems"] == []
1296 end
1297
1298 test "on non-federating instance, it returns 404", %{conn: conn} do
1299 Config.put([:instance, :federating], false)
1300 user = insert(:user)
1301
1302 conn
1303 |> assign(:user, user)
1304 |> get("/relay/following")
1305 |> json_response(404)
1306 end
1307 end
1308
1309 describe "/users/:nickname/followers" do
1310 test "it returns the followers in a collection", %{conn: conn} do
1311 user = insert(:user)
1312 user_two = insert(:user)
1313 User.follow(user, user_two)
1314
1315 result =
1316 conn
1317 |> assign(:user, user_two)
1318 |> get("/users/#{user_two.nickname}/followers")
1319 |> json_response(200)
1320
1321 assert result["first"]["orderedItems"] == [user.ap_id]
1322 end
1323
1324 test "it returns a uri if the user has 'hide_followers' set", %{conn: conn} do
1325 user = insert(:user)
1326 user_two = insert(:user, hide_followers: true)
1327 User.follow(user, user_two)
1328
1329 result =
1330 conn
1331 |> assign(:user, user)
1332 |> get("/users/#{user_two.nickname}/followers")
1333 |> json_response(200)
1334
1335 assert is_binary(result["first"])
1336 end
1337
1338 test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is from another user",
1339 %{conn: conn} do
1340 user = insert(:user)
1341 other_user = insert(:user, hide_followers: true)
1342
1343 result =
1344 conn
1345 |> assign(:user, user)
1346 |> get("/users/#{other_user.nickname}/followers?page=1")
1347
1348 assert result.status == 403
1349 assert result.resp_body == ""
1350 end
1351
1352 test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user",
1353 %{conn: conn} do
1354 user = insert(:user, hide_followers: true)
1355 other_user = insert(:user)
1356 {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
1357
1358 result =
1359 conn
1360 |> assign(:user, user)
1361 |> get("/users/#{user.nickname}/followers?page=1")
1362 |> json_response(200)
1363
1364 assert result["totalItems"] == 1
1365 assert result["orderedItems"] == [other_user.ap_id]
1366 end
1367
1368 test "it works for more than 10 users", %{conn: conn} do
1369 user = insert(:user)
1370
1371 Enum.each(1..15, fn _ ->
1372 other_user = insert(:user)
1373 User.follow(other_user, user)
1374 end)
1375
1376 result =
1377 conn
1378 |> assign(:user, user)
1379 |> get("/users/#{user.nickname}/followers")
1380 |> json_response(200)
1381
1382 assert length(result["first"]["orderedItems"]) == 10
1383 assert result["first"]["totalItems"] == 15
1384 assert result["totalItems"] == 15
1385
1386 result =
1387 conn
1388 |> assign(:user, user)
1389 |> get("/users/#{user.nickname}/followers?page=2")
1390 |> json_response(200)
1391
1392 assert length(result["orderedItems"]) == 5
1393 assert result["totalItems"] == 15
1394 end
1395
1396 test "does not require authentication", %{conn: conn} do
1397 user = insert(:user)
1398
1399 conn
1400 |> get("/users/#{user.nickname}/followers")
1401 |> json_response(200)
1402 end
1403 end
1404
1405 describe "/users/:nickname/following" do
1406 test "it returns the following in a collection", %{conn: conn} do
1407 user = insert(:user)
1408 user_two = insert(:user)
1409 User.follow(user, user_two)
1410
1411 result =
1412 conn
1413 |> assign(:user, user)
1414 |> get("/users/#{user.nickname}/following")
1415 |> json_response(200)
1416
1417 assert result["first"]["orderedItems"] == [user_two.ap_id]
1418 end
1419
1420 test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do
1421 user = insert(:user)
1422 user_two = insert(:user, hide_follows: true)
1423 User.follow(user, user_two)
1424
1425 result =
1426 conn
1427 |> assign(:user, user)
1428 |> get("/users/#{user_two.nickname}/following")
1429 |> json_response(200)
1430
1431 assert is_binary(result["first"])
1432 end
1433
1434 test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is from another user",
1435 %{conn: conn} do
1436 user = insert(:user)
1437 user_two = insert(:user, hide_follows: true)
1438
1439 result =
1440 conn
1441 |> assign(:user, user)
1442 |> get("/users/#{user_two.nickname}/following?page=1")
1443
1444 assert result.status == 403
1445 assert result.resp_body == ""
1446 end
1447
1448 test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user",
1449 %{conn: conn} do
1450 user = insert(:user, hide_follows: true)
1451 other_user = insert(:user)
1452 {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
1453
1454 result =
1455 conn
1456 |> assign(:user, user)
1457 |> get("/users/#{user.nickname}/following?page=1")
1458 |> json_response(200)
1459
1460 assert result["totalItems"] == 1
1461 assert result["orderedItems"] == [other_user.ap_id]
1462 end
1463
1464 test "it works for more than 10 users", %{conn: conn} do
1465 user = insert(:user)
1466
1467 Enum.each(1..15, fn _ ->
1468 user = User.get_cached_by_id(user.id)
1469 other_user = insert(:user)
1470 User.follow(user, other_user)
1471 end)
1472
1473 result =
1474 conn
1475 |> assign(:user, user)
1476 |> get("/users/#{user.nickname}/following")
1477 |> json_response(200)
1478
1479 assert length(result["first"]["orderedItems"]) == 10
1480 assert result["first"]["totalItems"] == 15
1481 assert result["totalItems"] == 15
1482
1483 result =
1484 conn
1485 |> assign(:user, user)
1486 |> get("/users/#{user.nickname}/following?page=2")
1487 |> json_response(200)
1488
1489 assert length(result["orderedItems"]) == 5
1490 assert result["totalItems"] == 15
1491 end
1492
1493 test "does not require authentication", %{conn: conn} do
1494 user = insert(:user)
1495
1496 conn
1497 |> get("/users/#{user.nickname}/following")
1498 |> json_response(200)
1499 end
1500 end
1501
1502 describe "delivery tracking" do
1503 test "it tracks a signed object fetch", %{conn: conn} do
1504 user = insert(:user, local: false)
1505 activity = insert(:note_activity)
1506 object = Object.normalize(activity)
1507
1508 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1509
1510 conn
1511 |> put_req_header("accept", "application/activity+json")
1512 |> assign(:user, user)
1513 |> get(object_path)
1514 |> json_response(200)
1515
1516 assert Delivery.get(object.id, user.id)
1517 end
1518
1519 test "it tracks a signed activity fetch", %{conn: conn} do
1520 user = insert(:user, local: false)
1521 activity = insert(:note_activity)
1522 object = Object.normalize(activity)
1523
1524 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1525
1526 conn
1527 |> put_req_header("accept", "application/activity+json")
1528 |> assign(:user, user)
1529 |> get(activity_path)
1530 |> json_response(200)
1531
1532 assert Delivery.get(object.id, user.id)
1533 end
1534
1535 test "it tracks a signed object fetch when the json is cached", %{conn: conn} do
1536 user = insert(:user, local: false)
1537 other_user = insert(:user, local: false)
1538 activity = insert(:note_activity)
1539 object = Object.normalize(activity)
1540
1541 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1542
1543 conn
1544 |> put_req_header("accept", "application/activity+json")
1545 |> assign(:user, user)
1546 |> get(object_path)
1547 |> json_response(200)
1548
1549 build_conn()
1550 |> put_req_header("accept", "application/activity+json")
1551 |> assign(:user, other_user)
1552 |> get(object_path)
1553 |> json_response(200)
1554
1555 assert Delivery.get(object.id, user.id)
1556 assert Delivery.get(object.id, other_user.id)
1557 end
1558
1559 test "it tracks a signed activity fetch when the json is cached", %{conn: conn} do
1560 user = insert(:user, local: false)
1561 other_user = insert(:user, local: false)
1562 activity = insert(:note_activity)
1563 object = Object.normalize(activity)
1564
1565 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1566
1567 conn
1568 |> put_req_header("accept", "application/activity+json")
1569 |> assign(:user, user)
1570 |> get(activity_path)
1571 |> json_response(200)
1572
1573 build_conn()
1574 |> put_req_header("accept", "application/activity+json")
1575 |> assign(:user, other_user)
1576 |> get(activity_path)
1577 |> json_response(200)
1578
1579 assert Delivery.get(object.id, user.id)
1580 assert Delivery.get(object.id, other_user.id)
1581 end
1582 end
1583
1584 describe "Additional ActivityPub C2S endpoints" do
1585 test "GET /api/ap/whoami", %{conn: conn} do
1586 user = insert(:user)
1587
1588 conn =
1589 conn
1590 |> assign(:user, user)
1591 |> get("/api/ap/whoami")
1592
1593 user = User.get_cached_by_id(user.id)
1594
1595 assert UserView.render("user.json", %{user: user}) == json_response(conn, 200)
1596
1597 conn
1598 |> get("/api/ap/whoami")
1599 |> json_response(403)
1600 end
1601
1602 setup do: clear_config([:media_proxy])
1603 setup do: clear_config([Pleroma.Upload])
1604
1605 test "POST /api/ap/upload_media", %{conn: conn} do
1606 user = insert(:user)
1607
1608 desc = "Description of the image"
1609
1610 image = %Plug.Upload{
1611 content_type: "bad/content-type",
1612 path: Path.absname("test/fixtures/image.jpg"),
1613 filename: "an_image.png"
1614 }
1615
1616 object =
1617 conn
1618 |> assign(:user, user)
1619 |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
1620 |> json_response(:created)
1621
1622 assert object["name"] == desc
1623 assert object["type"] == "Document"
1624 assert object["actor"] == user.ap_id
1625 assert [%{"href" => object_href, "mediaType" => object_mediatype}] = object["url"]
1626 assert is_binary(object_href)
1627 assert object_mediatype == "image/jpeg"
1628 assert String.ends_with?(object_href, ".jpg")
1629
1630 activity_request = %{
1631 "@context" => "https://www.w3.org/ns/activitystreams",
1632 "type" => "Create",
1633 "object" => %{
1634 "type" => "Note",
1635 "content" => "AP C2S test, attachment",
1636 "attachment" => [object]
1637 },
1638 "to" => "https://www.w3.org/ns/activitystreams#Public",
1639 "cc" => []
1640 }
1641
1642 activity_response =
1643 conn
1644 |> assign(:user, user)
1645 |> post("/users/#{user.nickname}/outbox", activity_request)
1646 |> json_response(:created)
1647
1648 assert activity_response["id"]
1649 assert activity_response["object"]
1650 assert activity_response["actor"] == user.ap_id
1651
1652 assert %Object{data: %{"attachment" => [attachment]}} =
1653 Object.normalize(activity_response["object"])
1654
1655 assert attachment["type"] == "Document"
1656 assert attachment["name"] == desc
1657
1658 assert [
1659 %{
1660 "href" => ^object_href,
1661 "type" => "Link",
1662 "mediaType" => ^object_mediatype
1663 }
1664 ] = attachment["url"]
1665
1666 # Fails if unauthenticated
1667 conn
1668 |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
1669 |> json_response(403)
1670 end
1671 end
1672 end