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