c3d4fcca7de2151529cdb5e128167fd784c40518
[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") |> Poison.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 |> Poison.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") |> Poison.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") |> Poison.decode!()
538 non_create_data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.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 |> Poison.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} = 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 |> Poison.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 test "forwarded report", %{conn: conn} do
804 admin = insert(:user, is_admin: true)
805 actor = insert(:user, local: false)
806 remote_domain = URI.parse(actor.ap_id).host
807 reported_user = insert(:user)
808
809 note = insert(:note_activity, user: reported_user)
810
811 data = %{
812 "@context" => [
813 "https://www.w3.org/ns/activitystreams",
814 "https://#{remote_domain}/schemas/litepub-0.1.jsonld",
815 %{
816 "@language" => "und"
817 }
818 ],
819 "actor" => actor.ap_id,
820 "cc" => [
821 reported_user.ap_id
822 ],
823 "content" => "test",
824 "context" => "context",
825 "id" => "http://#{remote_domain}/activities/02be56cf-35e3-46b4-b2c6-47ae08dfee9e",
826 "nickname" => reported_user.nickname,
827 "object" => [
828 reported_user.ap_id,
829 %{
830 "actor" => %{
831 "actor_type" => "Person",
832 "approval_pending" => false,
833 "avatar" => "",
834 "confirmation_pending" => false,
835 "deactivated" => false,
836 "display_name" => "test user",
837 "id" => reported_user.id,
838 "local" => false,
839 "nickname" => reported_user.nickname,
840 "registration_reason" => nil,
841 "roles" => %{
842 "admin" => false,
843 "moderator" => false
844 },
845 "tags" => [],
846 "url" => reported_user.ap_id
847 },
848 "content" => "",
849 "id" => note.data["id"],
850 "published" => note.data["published"],
851 "type" => "Note"
852 }
853 ],
854 "published" => note.data["published"],
855 "state" => "open",
856 "to" => [],
857 "type" => "Flag"
858 }
859
860 conn
861 |> assign(:valid_signature, true)
862 |> put_req_header("content-type", "application/activity+json")
863 |> post("/users/#{reported_user.nickname}/inbox", data)
864 |> json_response(200)
865
866 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
867
868 assert Pleroma.Repo.aggregate(Activity, :count, :id) == 2
869
870 ObanHelpers.perform_all()
871
872 Swoosh.TestAssertions.assert_email_sent(
873 to: {admin.name, admin.email},
874 html_body: ~r/Reported Account:/i
875 )
876 end
877
878 test "forwarded report from mastodon", %{conn: conn} do
879 admin = insert(:user, is_admin: true)
880 actor = insert(:user, local: false)
881 remote_domain = URI.parse(actor.ap_id).host
882 remote_actor = "https://#{remote_domain}/actor"
883 reported_user = insert(:user)
884
885 note = insert(:note_activity, user: reported_user)
886
887 mock_json_body =
888 "test/fixtures/mastodon/application_actor.json"
889 |> File.read!()
890 |> String.replace("{{DOMAIN}}", remote_domain)
891
892 Tesla.Mock.mock(fn %{url: ^remote_actor} ->
893 %Tesla.Env{
894 status: 200,
895 body: mock_json_body,
896 headers: [{"content-type", "application/activity+json"}]
897 }
898 end)
899
900 data = %{
901 "@context" => "https://www.w3.org/ns/activitystreams",
902 "actor" => remote_actor,
903 "content" => "test report",
904 "id" => "https://#{remote_domain}/e3b12fd1-948c-446e-b93b-a5e67edbe1d8",
905 "nickname" => reported_user.nickname,
906 "object" => [
907 reported_user.ap_id,
908 note.data["object"]
909 ],
910 "type" => "Flag"
911 }
912
913 conn
914 |> assign(:valid_signature, true)
915 |> put_req_header("content-type", "application/activity+json")
916 |> post("/users/#{reported_user.nickname}/inbox", data)
917 |> json_response(200)
918
919 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
920
921 assert Pleroma.Repo.aggregate(Activity, :count, :id) == 2
922
923 flag_activity = "Flag" |> Pleroma.Activity.Queries.by_type() |> Pleroma.Repo.one()
924 reported_user_ap_id = reported_user.ap_id
925
926 [^reported_user_ap_id, flag_data] = flag_activity.data["object"]
927
928 Enum.each(~w(actor content id published type), &Map.has_key?(flag_data, &1))
929 ObanHelpers.perform_all()
930
931 Swoosh.TestAssertions.assert_email_sent(
932 to: {admin.name, admin.email},
933 html_body: ~r/#{note.data["object"]}/i
934 )
935 end
936 end
937
938 describe "GET /users/:nickname/outbox" do
939 test "it paginates correctly", %{conn: conn} do
940 user = insert(:user)
941 conn = assign(conn, :user, user)
942 outbox_endpoint = user.ap_id <> "/outbox"
943
944 _posts =
945 for i <- 0..25 do
946 {:ok, activity} = CommonAPI.post(user, %{status: "post #{i}"})
947 activity
948 end
949
950 result =
951 conn
952 |> put_req_header("accept", "application/activity+json")
953 |> get(outbox_endpoint <> "?page=true")
954 |> json_response(200)
955
956 result_ids = Enum.map(result["orderedItems"], fn x -> x["id"] end)
957 assert length(result["orderedItems"]) == 20
958 assert length(result_ids) == 20
959 assert result["next"]
960 assert String.starts_with?(result["next"], outbox_endpoint)
961
962 result_next =
963 conn
964 |> put_req_header("accept", "application/activity+json")
965 |> get(result["next"])
966 |> json_response(200)
967
968 result_next_ids = Enum.map(result_next["orderedItems"], fn x -> x["id"] end)
969 assert length(result_next["orderedItems"]) == 6
970 assert length(result_next_ids) == 6
971 refute Enum.find(result_next_ids, fn x -> x in result_ids end)
972 refute Enum.find(result_ids, fn x -> x in result_next_ids end)
973 assert String.starts_with?(result["id"], outbox_endpoint)
974
975 result_next_again =
976 conn
977 |> put_req_header("accept", "application/activity+json")
978 |> get(result_next["id"])
979 |> json_response(200)
980
981 assert result_next == result_next_again
982 end
983
984 test "it returns 200 even if there're no activities", %{conn: conn} do
985 user = insert(:user)
986 outbox_endpoint = user.ap_id <> "/outbox"
987
988 conn =
989 conn
990 |> assign(:user, user)
991 |> put_req_header("accept", "application/activity+json")
992 |> get(outbox_endpoint)
993
994 result = json_response(conn, 200)
995 assert outbox_endpoint == result["id"]
996 end
997
998 test "it returns a note activity in a collection", %{conn: conn} do
999 note_activity = insert(:note_activity)
1000 note_object = Object.normalize(note_activity)
1001 user = User.get_cached_by_ap_id(note_activity.data["actor"])
1002
1003 conn =
1004 conn
1005 |> assign(:user, user)
1006 |> put_req_header("accept", "application/activity+json")
1007 |> get("/users/#{user.nickname}/outbox?page=true")
1008
1009 assert response(conn, 200) =~ note_object.data["content"]
1010 end
1011
1012 test "it returns an announce activity in a collection", %{conn: conn} do
1013 announce_activity = insert(:announce_activity)
1014 user = User.get_cached_by_ap_id(announce_activity.data["actor"])
1015
1016 conn =
1017 conn
1018 |> assign(:user, user)
1019 |> put_req_header("accept", "application/activity+json")
1020 |> get("/users/#{user.nickname}/outbox?page=true")
1021
1022 assert response(conn, 200) =~ announce_activity.data["object"]
1023 end
1024 end
1025
1026 describe "POST /users/:nickname/outbox (C2S)" do
1027 setup do: clear_config([:instance, :limit])
1028
1029 setup do
1030 [
1031 activity: %{
1032 "@context" => "https://www.w3.org/ns/activitystreams",
1033 "type" => "Create",
1034 "object" => %{"type" => "Note", "content" => "AP C2S test"},
1035 "to" => "https://www.w3.org/ns/activitystreams#Public",
1036 "cc" => []
1037 }
1038 ]
1039 end
1040
1041 test "it rejects posts from other users / unauthenticated users", %{
1042 conn: conn,
1043 activity: activity
1044 } do
1045 user = insert(:user)
1046 other_user = insert(:user)
1047 conn = put_req_header(conn, "content-type", "application/activity+json")
1048
1049 conn
1050 |> post("/users/#{user.nickname}/outbox", activity)
1051 |> json_response(403)
1052
1053 conn
1054 |> assign(:user, other_user)
1055 |> post("/users/#{user.nickname}/outbox", activity)
1056 |> json_response(403)
1057 end
1058
1059 test "it inserts an incoming create activity into the database", %{
1060 conn: conn,
1061 activity: activity
1062 } do
1063 user = insert(:user)
1064
1065 result =
1066 conn
1067 |> assign(:user, user)
1068 |> put_req_header("content-type", "application/activity+json")
1069 |> post("/users/#{user.nickname}/outbox", activity)
1070 |> json_response(201)
1071
1072 assert Activity.get_by_ap_id(result["id"])
1073 assert result["object"]
1074 assert %Object{data: object} = Object.normalize(result["object"])
1075 assert object["content"] == activity["object"]["content"]
1076 end
1077
1078 test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do
1079 user = insert(:user)
1080
1081 activity =
1082 activity
1083 |> put_in(["object", "type"], "Benis")
1084
1085 _result =
1086 conn
1087 |> assign(:user, user)
1088 |> put_req_header("content-type", "application/activity+json")
1089 |> post("/users/#{user.nickname}/outbox", activity)
1090 |> json_response(400)
1091 end
1092
1093 test "it inserts an incoming sensitive activity into the database", %{
1094 conn: conn,
1095 activity: activity
1096 } do
1097 user = insert(:user)
1098 conn = assign(conn, :user, user)
1099 object = Map.put(activity["object"], "sensitive", true)
1100 activity = Map.put(activity, "object", object)
1101
1102 response =
1103 conn
1104 |> put_req_header("content-type", "application/activity+json")
1105 |> post("/users/#{user.nickname}/outbox", activity)
1106 |> json_response(201)
1107
1108 assert Activity.get_by_ap_id(response["id"])
1109 assert response["object"]
1110 assert %Object{data: response_object} = Object.normalize(response["object"])
1111 assert response_object["sensitive"] == true
1112 assert response_object["content"] == activity["object"]["content"]
1113
1114 representation =
1115 conn
1116 |> put_req_header("accept", "application/activity+json")
1117 |> get(response["id"])
1118 |> json_response(200)
1119
1120 assert representation["object"]["sensitive"] == true
1121 end
1122
1123 test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do
1124 user = insert(:user)
1125 activity = Map.put(activity, "type", "BadType")
1126
1127 conn =
1128 conn
1129 |> assign(:user, user)
1130 |> put_req_header("content-type", "application/activity+json")
1131 |> post("/users/#{user.nickname}/outbox", activity)
1132
1133 assert json_response(conn, 400)
1134 end
1135
1136 test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
1137 note_activity = insert(:note_activity)
1138 note_object = Object.normalize(note_activity)
1139 user = User.get_cached_by_ap_id(note_activity.data["actor"])
1140
1141 data = %{
1142 type: "Delete",
1143 object: %{
1144 id: note_object.data["id"]
1145 }
1146 }
1147
1148 conn =
1149 conn
1150 |> assign(:user, user)
1151 |> put_req_header("content-type", "application/activity+json")
1152 |> post("/users/#{user.nickname}/outbox", data)
1153
1154 result = json_response(conn, 201)
1155 assert Activity.get_by_ap_id(result["id"])
1156
1157 assert object = Object.get_by_ap_id(note_object.data["id"])
1158 assert object.data["type"] == "Tombstone"
1159 end
1160
1161 test "it rejects delete activity of object from other actor", %{conn: conn} do
1162 note_activity = insert(:note_activity)
1163 note_object = Object.normalize(note_activity)
1164 user = insert(:user)
1165
1166 data = %{
1167 type: "Delete",
1168 object: %{
1169 id: note_object.data["id"]
1170 }
1171 }
1172
1173 conn =
1174 conn
1175 |> assign(:user, user)
1176 |> put_req_header("content-type", "application/activity+json")
1177 |> post("/users/#{user.nickname}/outbox", data)
1178
1179 assert json_response(conn, 400)
1180 end
1181
1182 test "it increases like count when receiving a like action", %{conn: conn} do
1183 note_activity = insert(:note_activity)
1184 note_object = Object.normalize(note_activity)
1185 user = User.get_cached_by_ap_id(note_activity.data["actor"])
1186
1187 data = %{
1188 type: "Like",
1189 object: %{
1190 id: note_object.data["id"]
1191 }
1192 }
1193
1194 conn =
1195 conn
1196 |> assign(:user, user)
1197 |> put_req_header("content-type", "application/activity+json")
1198 |> post("/users/#{user.nickname}/outbox", data)
1199
1200 result = json_response(conn, 201)
1201 assert Activity.get_by_ap_id(result["id"])
1202
1203 assert object = Object.get_by_ap_id(note_object.data["id"])
1204 assert object.data["like_count"] == 1
1205 end
1206
1207 test "it doesn't spreads faulty attributedTo or actor fields", %{
1208 conn: conn,
1209 activity: activity
1210 } do
1211 reimu = insert(:user, nickname: "reimu")
1212 cirno = insert(:user, nickname: "cirno")
1213
1214 assert reimu.ap_id
1215 assert cirno.ap_id
1216
1217 activity =
1218 activity
1219 |> put_in(["object", "actor"], reimu.ap_id)
1220 |> put_in(["object", "attributedTo"], reimu.ap_id)
1221 |> put_in(["actor"], reimu.ap_id)
1222 |> put_in(["attributedTo"], reimu.ap_id)
1223
1224 _reimu_outbox =
1225 conn
1226 |> assign(:user, cirno)
1227 |> put_req_header("content-type", "application/activity+json")
1228 |> post("/users/#{reimu.nickname}/outbox", activity)
1229 |> json_response(403)
1230
1231 cirno_outbox =
1232 conn
1233 |> assign(:user, cirno)
1234 |> put_req_header("content-type", "application/activity+json")
1235 |> post("/users/#{cirno.nickname}/outbox", activity)
1236 |> json_response(201)
1237
1238 assert cirno_outbox["attributedTo"] == nil
1239 assert cirno_outbox["actor"] == cirno.ap_id
1240
1241 assert cirno_object = Object.normalize(cirno_outbox["object"])
1242 assert cirno_object.data["actor"] == cirno.ap_id
1243 assert cirno_object.data["attributedTo"] == cirno.ap_id
1244 end
1245
1246 test "Character limitation", %{conn: conn, activity: activity} do
1247 Pleroma.Config.put([:instance, :limit], 5)
1248 user = insert(:user)
1249
1250 result =
1251 conn
1252 |> assign(:user, user)
1253 |> put_req_header("content-type", "application/activity+json")
1254 |> post("/users/#{user.nickname}/outbox", activity)
1255 |> json_response(400)
1256
1257 assert result == "Note is over the character limit"
1258 end
1259 end
1260
1261 describe "/relay/followers" do
1262 test "it returns relay followers", %{conn: conn} do
1263 relay_actor = Relay.get_actor()
1264 user = insert(:user)
1265 User.follow(user, relay_actor)
1266
1267 result =
1268 conn
1269 |> get("/relay/followers")
1270 |> json_response(200)
1271
1272 assert result["first"]["orderedItems"] == [user.ap_id]
1273 end
1274
1275 test "on non-federating instance, it returns 404", %{conn: conn} do
1276 Config.put([:instance, :federating], false)
1277 user = insert(:user)
1278
1279 conn
1280 |> assign(:user, user)
1281 |> get("/relay/followers")
1282 |> json_response(404)
1283 end
1284 end
1285
1286 describe "/relay/following" do
1287 test "it returns relay following", %{conn: conn} do
1288 result =
1289 conn
1290 |> get("/relay/following")
1291 |> json_response(200)
1292
1293 assert result["first"]["orderedItems"] == []
1294 end
1295
1296 test "on non-federating instance, it returns 404", %{conn: conn} do
1297 Config.put([:instance, :federating], false)
1298 user = insert(:user)
1299
1300 conn
1301 |> assign(:user, user)
1302 |> get("/relay/following")
1303 |> json_response(404)
1304 end
1305 end
1306
1307 describe "/users/:nickname/followers" do
1308 test "it returns the followers in a collection", %{conn: conn} do
1309 user = insert(:user)
1310 user_two = insert(:user)
1311 User.follow(user, user_two)
1312
1313 result =
1314 conn
1315 |> assign(:user, user_two)
1316 |> get("/users/#{user_two.nickname}/followers")
1317 |> json_response(200)
1318
1319 assert result["first"]["orderedItems"] == [user.ap_id]
1320 end
1321
1322 test "it returns a uri if the user has 'hide_followers' set", %{conn: conn} do
1323 user = insert(:user)
1324 user_two = insert(:user, hide_followers: true)
1325 User.follow(user, user_two)
1326
1327 result =
1328 conn
1329 |> assign(:user, user)
1330 |> get("/users/#{user_two.nickname}/followers")
1331 |> json_response(200)
1332
1333 assert is_binary(result["first"])
1334 end
1335
1336 test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is from another user",
1337 %{conn: conn} do
1338 user = insert(:user)
1339 other_user = insert(:user, hide_followers: true)
1340
1341 result =
1342 conn
1343 |> assign(:user, user)
1344 |> get("/users/#{other_user.nickname}/followers?page=1")
1345
1346 assert result.status == 403
1347 assert result.resp_body == ""
1348 end
1349
1350 test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user",
1351 %{conn: conn} do
1352 user = insert(:user, hide_followers: true)
1353 other_user = insert(:user)
1354 {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
1355
1356 result =
1357 conn
1358 |> assign(:user, user)
1359 |> get("/users/#{user.nickname}/followers?page=1")
1360 |> json_response(200)
1361
1362 assert result["totalItems"] == 1
1363 assert result["orderedItems"] == [other_user.ap_id]
1364 end
1365
1366 test "it works for more than 10 users", %{conn: conn} do
1367 user = insert(:user)
1368
1369 Enum.each(1..15, fn _ ->
1370 other_user = insert(:user)
1371 User.follow(other_user, user)
1372 end)
1373
1374 result =
1375 conn
1376 |> assign(:user, user)
1377 |> get("/users/#{user.nickname}/followers")
1378 |> json_response(200)
1379
1380 assert length(result["first"]["orderedItems"]) == 10
1381 assert result["first"]["totalItems"] == 15
1382 assert result["totalItems"] == 15
1383
1384 result =
1385 conn
1386 |> assign(:user, user)
1387 |> get("/users/#{user.nickname}/followers?page=2")
1388 |> json_response(200)
1389
1390 assert length(result["orderedItems"]) == 5
1391 assert result["totalItems"] == 15
1392 end
1393
1394 test "does not require authentication", %{conn: conn} do
1395 user = insert(:user)
1396
1397 conn
1398 |> get("/users/#{user.nickname}/followers")
1399 |> json_response(200)
1400 end
1401 end
1402
1403 describe "/users/:nickname/following" do
1404 test "it returns the following in a collection", %{conn: conn} do
1405 user = insert(:user)
1406 user_two = insert(:user)
1407 User.follow(user, user_two)
1408
1409 result =
1410 conn
1411 |> assign(:user, user)
1412 |> get("/users/#{user.nickname}/following")
1413 |> json_response(200)
1414
1415 assert result["first"]["orderedItems"] == [user_two.ap_id]
1416 end
1417
1418 test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do
1419 user = insert(:user)
1420 user_two = insert(:user, hide_follows: true)
1421 User.follow(user, user_two)
1422
1423 result =
1424 conn
1425 |> assign(:user, user)
1426 |> get("/users/#{user_two.nickname}/following")
1427 |> json_response(200)
1428
1429 assert is_binary(result["first"])
1430 end
1431
1432 test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is from another user",
1433 %{conn: conn} do
1434 user = insert(:user)
1435 user_two = insert(:user, hide_follows: true)
1436
1437 result =
1438 conn
1439 |> assign(:user, user)
1440 |> get("/users/#{user_two.nickname}/following?page=1")
1441
1442 assert result.status == 403
1443 assert result.resp_body == ""
1444 end
1445
1446 test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user",
1447 %{conn: conn} do
1448 user = insert(:user, hide_follows: true)
1449 other_user = insert(:user)
1450 {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
1451
1452 result =
1453 conn
1454 |> assign(:user, user)
1455 |> get("/users/#{user.nickname}/following?page=1")
1456 |> json_response(200)
1457
1458 assert result["totalItems"] == 1
1459 assert result["orderedItems"] == [other_user.ap_id]
1460 end
1461
1462 test "it works for more than 10 users", %{conn: conn} do
1463 user = insert(:user)
1464
1465 Enum.each(1..15, fn _ ->
1466 user = User.get_cached_by_id(user.id)
1467 other_user = insert(:user)
1468 User.follow(user, other_user)
1469 end)
1470
1471 result =
1472 conn
1473 |> assign(:user, user)
1474 |> get("/users/#{user.nickname}/following")
1475 |> json_response(200)
1476
1477 assert length(result["first"]["orderedItems"]) == 10
1478 assert result["first"]["totalItems"] == 15
1479 assert result["totalItems"] == 15
1480
1481 result =
1482 conn
1483 |> assign(:user, user)
1484 |> get("/users/#{user.nickname}/following?page=2")
1485 |> json_response(200)
1486
1487 assert length(result["orderedItems"]) == 5
1488 assert result["totalItems"] == 15
1489 end
1490
1491 test "does not require authentication", %{conn: conn} do
1492 user = insert(:user)
1493
1494 conn
1495 |> get("/users/#{user.nickname}/following")
1496 |> json_response(200)
1497 end
1498 end
1499
1500 describe "delivery tracking" do
1501 test "it tracks a signed object fetch", %{conn: conn} do
1502 user = insert(:user, local: false)
1503 activity = insert(:note_activity)
1504 object = Object.normalize(activity)
1505
1506 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1507
1508 conn
1509 |> put_req_header("accept", "application/activity+json")
1510 |> assign(:user, user)
1511 |> get(object_path)
1512 |> json_response(200)
1513
1514 assert Delivery.get(object.id, user.id)
1515 end
1516
1517 test "it tracks a signed activity fetch", %{conn: conn} do
1518 user = insert(:user, local: false)
1519 activity = insert(:note_activity)
1520 object = Object.normalize(activity)
1521
1522 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1523
1524 conn
1525 |> put_req_header("accept", "application/activity+json")
1526 |> assign(:user, user)
1527 |> get(activity_path)
1528 |> json_response(200)
1529
1530 assert Delivery.get(object.id, user.id)
1531 end
1532
1533 test "it tracks a signed object fetch when the json is cached", %{conn: conn} do
1534 user = insert(:user, local: false)
1535 other_user = insert(:user, local: false)
1536 activity = insert(:note_activity)
1537 object = Object.normalize(activity)
1538
1539 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1540
1541 conn
1542 |> put_req_header("accept", "application/activity+json")
1543 |> assign(:user, user)
1544 |> get(object_path)
1545 |> json_response(200)
1546
1547 build_conn()
1548 |> put_req_header("accept", "application/activity+json")
1549 |> assign(:user, other_user)
1550 |> get(object_path)
1551 |> json_response(200)
1552
1553 assert Delivery.get(object.id, user.id)
1554 assert Delivery.get(object.id, other_user.id)
1555 end
1556
1557 test "it tracks a signed activity fetch when the json is cached", %{conn: conn} do
1558 user = insert(:user, local: false)
1559 other_user = insert(:user, local: false)
1560 activity = insert(:note_activity)
1561 object = Object.normalize(activity)
1562
1563 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1564
1565 conn
1566 |> put_req_header("accept", "application/activity+json")
1567 |> assign(:user, user)
1568 |> get(activity_path)
1569 |> json_response(200)
1570
1571 build_conn()
1572 |> put_req_header("accept", "application/activity+json")
1573 |> assign(:user, other_user)
1574 |> get(activity_path)
1575 |> json_response(200)
1576
1577 assert Delivery.get(object.id, user.id)
1578 assert Delivery.get(object.id, other_user.id)
1579 end
1580 end
1581
1582 describe "Additional ActivityPub C2S endpoints" do
1583 test "GET /api/ap/whoami", %{conn: conn} do
1584 user = insert(:user)
1585
1586 conn =
1587 conn
1588 |> assign(:user, user)
1589 |> get("/api/ap/whoami")
1590
1591 user = User.get_cached_by_id(user.id)
1592
1593 assert UserView.render("user.json", %{user: user}) == json_response(conn, 200)
1594
1595 conn
1596 |> get("/api/ap/whoami")
1597 |> json_response(403)
1598 end
1599
1600 setup do: clear_config([:media_proxy])
1601 setup do: clear_config([Pleroma.Upload])
1602
1603 test "POST /api/ap/upload_media", %{conn: conn} do
1604 user = insert(:user)
1605
1606 desc = "Description of the image"
1607
1608 image = %Plug.Upload{
1609 content_type: "bad/content-type",
1610 path: Path.absname("test/fixtures/image.jpg"),
1611 filename: "an_image.png"
1612 }
1613
1614 object =
1615 conn
1616 |> assign(:user, user)
1617 |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
1618 |> json_response(:created)
1619
1620 assert object["name"] == desc
1621 assert object["type"] == "Document"
1622 assert object["actor"] == user.ap_id
1623 assert [%{"href" => object_href, "mediaType" => object_mediatype}] = object["url"]
1624 assert is_binary(object_href)
1625 assert object_mediatype == "image/jpeg"
1626 assert String.ends_with?(object_href, ".jpg")
1627
1628 activity_request = %{
1629 "@context" => "https://www.w3.org/ns/activitystreams",
1630 "type" => "Create",
1631 "object" => %{
1632 "type" => "Note",
1633 "content" => "AP C2S test, attachment",
1634 "attachment" => [object]
1635 },
1636 "to" => "https://www.w3.org/ns/activitystreams#Public",
1637 "cc" => []
1638 }
1639
1640 activity_response =
1641 conn
1642 |> assign(:user, user)
1643 |> post("/users/#{user.nickname}/outbox", activity_request)
1644 |> json_response(:created)
1645
1646 assert activity_response["id"]
1647 assert activity_response["object"]
1648 assert activity_response["actor"] == user.ap_id
1649
1650 assert %Object{data: %{"attachment" => [attachment]}} =
1651 Object.normalize(activity_response["object"])
1652
1653 assert attachment["type"] == "Document"
1654 assert attachment["name"] == desc
1655
1656 assert [
1657 %{
1658 "href" => ^object_href,
1659 "type" => "Link",
1660 "mediaType" => ^object_mediatype
1661 }
1662 ] = attachment["url"]
1663
1664 # Fails if unauthenticated
1665 conn
1666 |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
1667 |> json_response(403)
1668 end
1669 end
1670 end