AP C2S: Restrict creation to `Note`s for now.
[akkoma] / test / 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 import Pleroma.Factory
10 alias Pleroma.Activity
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.ObjectView
17 alias Pleroma.Web.ActivityPub.Relay
18 alias Pleroma.Web.ActivityPub.UserView
19 alias Pleroma.Web.ActivityPub.Utils
20 alias Pleroma.Web.CommonAPI
21 alias Pleroma.Workers.ReceiverWorker
22
23 setup_all do
24 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
25 :ok
26 end
27
28 clear_config_all([:instance, :federating],
29 do: Pleroma.Config.put([:instance, :federating], true)
30 )
31
32 describe "/relay" do
33 clear_config([:instance, :allow_relay])
34
35 test "with the relay active, it returns the relay user", %{conn: conn} do
36 res =
37 conn
38 |> get(activity_pub_path(conn, :relay))
39 |> json_response(200)
40
41 assert res["id"] =~ "/relay"
42 end
43
44 test "with the relay disabled, it returns 404", %{conn: conn} do
45 Pleroma.Config.put([:instance, :allow_relay], false)
46
47 conn
48 |> get(activity_pub_path(conn, :relay))
49 |> json_response(404)
50 |> assert
51 end
52 end
53
54 describe "/internal/fetch" do
55 test "it returns the internal fetch user", %{conn: conn} do
56 res =
57 conn
58 |> get(activity_pub_path(conn, :internal_fetch))
59 |> json_response(200)
60
61 assert res["id"] =~ "/fetch"
62 end
63 end
64
65 describe "/users/:nickname" do
66 test "it returns a json representation of the user with accept application/json", %{
67 conn: conn
68 } do
69 user = insert(:user)
70
71 conn =
72 conn
73 |> put_req_header("accept", "application/json")
74 |> get("/users/#{user.nickname}")
75
76 user = User.get_cached_by_id(user.id)
77
78 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
79 end
80
81 test "it returns a json representation of the user with accept application/activity+json", %{
82 conn: conn
83 } do
84 user = insert(:user)
85
86 conn =
87 conn
88 |> put_req_header("accept", "application/activity+json")
89 |> get("/users/#{user.nickname}")
90
91 user = User.get_cached_by_id(user.id)
92
93 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
94 end
95
96 test "it returns a json representation of the user with accept application/ld+json", %{
97 conn: conn
98 } do
99 user = insert(:user)
100
101 conn =
102 conn
103 |> put_req_header(
104 "accept",
105 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
106 )
107 |> get("/users/#{user.nickname}")
108
109 user = User.get_cached_by_id(user.id)
110
111 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
112 end
113
114 test "it returns 404 for remote users", %{
115 conn: conn
116 } do
117 user = insert(:user, local: false, nickname: "remoteuser@example.com")
118
119 conn =
120 conn
121 |> put_req_header("accept", "application/json")
122 |> get("/users/#{user.nickname}.json")
123
124 assert json_response(conn, 404)
125 end
126 end
127
128 describe "/object/:uuid" do
129 test "it returns a json representation of the object with accept application/json", %{
130 conn: conn
131 } do
132 note = insert(:note)
133 uuid = String.split(note.data["id"], "/") |> List.last()
134
135 conn =
136 conn
137 |> put_req_header("accept", "application/json")
138 |> get("/objects/#{uuid}")
139
140 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
141 end
142
143 test "it returns a json representation of the object with accept application/activity+json",
144 %{conn: conn} do
145 note = insert(:note)
146 uuid = String.split(note.data["id"], "/") |> List.last()
147
148 conn =
149 conn
150 |> put_req_header("accept", "application/activity+json")
151 |> get("/objects/#{uuid}")
152
153 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
154 end
155
156 test "it returns a json representation of the object with accept application/ld+json", %{
157 conn: conn
158 } do
159 note = insert(:note)
160 uuid = String.split(note.data["id"], "/") |> List.last()
161
162 conn =
163 conn
164 |> put_req_header(
165 "accept",
166 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
167 )
168 |> get("/objects/#{uuid}")
169
170 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
171 end
172
173 test "it returns 404 for non-public messages", %{conn: conn} do
174 note = insert(:direct_note)
175 uuid = String.split(note.data["id"], "/") |> List.last()
176
177 conn =
178 conn
179 |> put_req_header("accept", "application/activity+json")
180 |> get("/objects/#{uuid}")
181
182 assert json_response(conn, 404)
183 end
184
185 test "it returns 404 for tombstone objects", %{conn: conn} do
186 tombstone = insert(:tombstone)
187 uuid = String.split(tombstone.data["id"], "/") |> List.last()
188
189 conn =
190 conn
191 |> put_req_header("accept", "application/activity+json")
192 |> get("/objects/#{uuid}")
193
194 assert json_response(conn, 404)
195 end
196
197 test "it caches a response", %{conn: conn} do
198 note = insert(:note)
199 uuid = String.split(note.data["id"], "/") |> List.last()
200
201 conn1 =
202 conn
203 |> put_req_header("accept", "application/activity+json")
204 |> get("/objects/#{uuid}")
205
206 assert json_response(conn1, :ok)
207 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
208
209 conn2 =
210 conn
211 |> put_req_header("accept", "application/activity+json")
212 |> get("/objects/#{uuid}")
213
214 assert json_response(conn1, :ok) == json_response(conn2, :ok)
215 assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
216 end
217
218 test "cached purged after object deletion", %{conn: conn} do
219 note = insert(:note)
220 uuid = String.split(note.data["id"], "/") |> List.last()
221
222 conn1 =
223 conn
224 |> put_req_header("accept", "application/activity+json")
225 |> get("/objects/#{uuid}")
226
227 assert json_response(conn1, :ok)
228 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
229
230 Object.delete(note)
231
232 conn2 =
233 conn
234 |> put_req_header("accept", "application/activity+json")
235 |> get("/objects/#{uuid}")
236
237 assert "Not found" == json_response(conn2, :not_found)
238 end
239 end
240
241 describe "/activities/:uuid" do
242 test "it returns a json representation of the activity", %{conn: conn} do
243 activity = insert(:note_activity)
244 uuid = String.split(activity.data["id"], "/") |> List.last()
245
246 conn =
247 conn
248 |> put_req_header("accept", "application/activity+json")
249 |> get("/activities/#{uuid}")
250
251 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity})
252 end
253
254 test "it returns 404 for non-public activities", %{conn: conn} do
255 activity = insert(:direct_note_activity)
256 uuid = String.split(activity.data["id"], "/") |> List.last()
257
258 conn =
259 conn
260 |> put_req_header("accept", "application/activity+json")
261 |> get("/activities/#{uuid}")
262
263 assert json_response(conn, 404)
264 end
265
266 test "it caches a response", %{conn: conn} do
267 activity = insert(:note_activity)
268 uuid = String.split(activity.data["id"], "/") |> List.last()
269
270 conn1 =
271 conn
272 |> put_req_header("accept", "application/activity+json")
273 |> get("/activities/#{uuid}")
274
275 assert json_response(conn1, :ok)
276 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
277
278 conn2 =
279 conn
280 |> put_req_header("accept", "application/activity+json")
281 |> get("/activities/#{uuid}")
282
283 assert json_response(conn1, :ok) == json_response(conn2, :ok)
284 assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
285 end
286
287 test "cached purged after activity deletion", %{conn: conn} do
288 user = insert(:user)
289 {:ok, activity} = CommonAPI.post(user, %{"status" => "cofe"})
290
291 uuid = String.split(activity.data["id"], "/") |> List.last()
292
293 conn1 =
294 conn
295 |> put_req_header("accept", "application/activity+json")
296 |> get("/activities/#{uuid}")
297
298 assert json_response(conn1, :ok)
299 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
300
301 Activity.delete_all_by_object_ap_id(activity.object.data["id"])
302
303 conn2 =
304 conn
305 |> put_req_header("accept", "application/activity+json")
306 |> get("/activities/#{uuid}")
307
308 assert "Not found" == json_response(conn2, :not_found)
309 end
310 end
311
312 describe "/inbox" do
313 test "it inserts an incoming activity into the database", %{conn: conn} do
314 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
315
316 conn =
317 conn
318 |> assign(:valid_signature, true)
319 |> put_req_header("content-type", "application/activity+json")
320 |> post("/inbox", data)
321
322 assert "ok" == json_response(conn, 200)
323
324 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
325 assert Activity.get_by_ap_id(data["id"])
326 end
327
328 test "it clears `unreachable` federation status of the sender", %{conn: conn} do
329 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
330
331 sender_url = data["actor"]
332 Instances.set_consistently_unreachable(sender_url)
333 refute Instances.reachable?(sender_url)
334
335 conn =
336 conn
337 |> assign(:valid_signature, true)
338 |> put_req_header("content-type", "application/activity+json")
339 |> post("/inbox", data)
340
341 assert "ok" == json_response(conn, 200)
342 assert Instances.reachable?(sender_url)
343 end
344
345 test "accept follow activity", %{conn: conn} do
346 Pleroma.Config.put([:instance, :federating], true)
347 relay = Relay.get_actor()
348
349 assert {:ok, %Activity{} = activity} = Relay.follow("https://relay.mastodon.host/actor")
350
351 followed_relay = Pleroma.User.get_by_ap_id("https://relay.mastodon.host/actor")
352 relay = refresh_record(relay)
353
354 accept =
355 File.read!("test/fixtures/relay/accept-follow.json")
356 |> String.replace("{{ap_id}}", relay.ap_id)
357 |> String.replace("{{activity_id}}", activity.data["id"])
358
359 assert "ok" ==
360 conn
361 |> assign(:valid_signature, true)
362 |> put_req_header("content-type", "application/activity+json")
363 |> post("/inbox", accept)
364 |> json_response(200)
365
366 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
367
368 assert Pleroma.FollowingRelationship.following?(
369 relay,
370 followed_relay
371 )
372
373 Mix.shell(Mix.Shell.Process)
374
375 on_exit(fn ->
376 Mix.shell(Mix.Shell.IO)
377 end)
378
379 :ok = Mix.Tasks.Pleroma.Relay.run(["list"])
380 assert_receive {:mix_shell, :info, ["relay.mastodon.host"]}
381 end
382 end
383
384 describe "/users/:nickname/inbox" do
385 setup do
386 data =
387 File.read!("test/fixtures/mastodon-post-activity.json")
388 |> Poison.decode!()
389
390 [data: data]
391 end
392
393 test "it inserts an incoming activity into the database", %{conn: conn, data: data} do
394 user = insert(:user)
395 data = Map.put(data, "bcc", [user.ap_id])
396
397 conn =
398 conn
399 |> assign(:valid_signature, true)
400 |> put_req_header("content-type", "application/activity+json")
401 |> post("/users/#{user.nickname}/inbox", data)
402
403 assert "ok" == json_response(conn, 200)
404 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
405 assert Activity.get_by_ap_id(data["id"])
406 end
407
408 test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do
409 user = insert(:user)
410
411 data =
412 Map.put(data, "to", user.ap_id)
413 |> Map.delete("cc")
414
415 conn =
416 conn
417 |> assign(:valid_signature, true)
418 |> put_req_header("content-type", "application/activity+json")
419 |> post("/users/#{user.nickname}/inbox", data)
420
421 assert "ok" == json_response(conn, 200)
422 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
423 assert Activity.get_by_ap_id(data["id"])
424 end
425
426 test "it accepts messages with cc as string instead of array", %{conn: conn, data: data} do
427 user = insert(:user)
428
429 data =
430 Map.put(data, "cc", user.ap_id)
431 |> Map.delete("to")
432
433 conn =
434 conn
435 |> assign(:valid_signature, true)
436 |> put_req_header("content-type", "application/activity+json")
437 |> post("/users/#{user.nickname}/inbox", data)
438
439 assert "ok" == json_response(conn, 200)
440 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
441 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
442 assert user.ap_id in activity.recipients
443 end
444
445 test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do
446 user = insert(:user)
447
448 data =
449 Map.put(data, "bcc", user.ap_id)
450 |> Map.delete("to")
451 |> Map.delete("cc")
452
453 conn =
454 conn
455 |> assign(:valid_signature, true)
456 |> put_req_header("content-type", "application/activity+json")
457 |> post("/users/#{user.nickname}/inbox", data)
458
459 assert "ok" == json_response(conn, 200)
460 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
461 assert Activity.get_by_ap_id(data["id"])
462 end
463
464 test "it accepts announces with to as string instead of array", %{conn: conn} do
465 user = insert(:user)
466
467 data = %{
468 "@context" => "https://www.w3.org/ns/activitystreams",
469 "actor" => "http://mastodon.example.org/users/admin",
470 "id" => "http://mastodon.example.org/users/admin/statuses/19512778738411822/activity",
471 "object" => "https://mastodon.social/users/emelie/statuses/101849165031453009",
472 "to" => "https://www.w3.org/ns/activitystreams#Public",
473 "cc" => [user.ap_id],
474 "type" => "Announce"
475 }
476
477 conn =
478 conn
479 |> assign(:valid_signature, true)
480 |> put_req_header("content-type", "application/activity+json")
481 |> post("/users/#{user.nickname}/inbox", data)
482
483 assert "ok" == json_response(conn, 200)
484 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
485 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
486 assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients
487 end
488
489 test "it accepts messages from actors that are followed by the user", %{
490 conn: conn,
491 data: data
492 } do
493 recipient = insert(:user)
494 actor = insert(:user, %{ap_id: "http://mastodon.example.org/users/actor"})
495
496 {:ok, recipient} = User.follow(recipient, actor)
497
498 object =
499 data["object"]
500 |> Map.put("attributedTo", actor.ap_id)
501
502 data =
503 data
504 |> Map.put("actor", actor.ap_id)
505 |> Map.put("object", object)
506
507 conn =
508 conn
509 |> assign(:valid_signature, true)
510 |> put_req_header("content-type", "application/activity+json")
511 |> post("/users/#{recipient.nickname}/inbox", data)
512
513 assert "ok" == json_response(conn, 200)
514 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
515 assert Activity.get_by_ap_id(data["id"])
516 end
517
518 test "it rejects reads from other users", %{conn: conn} do
519 user = insert(:user)
520 otheruser = insert(:user)
521
522 conn =
523 conn
524 |> assign(:user, otheruser)
525 |> put_req_header("accept", "application/activity+json")
526 |> get("/users/#{user.nickname}/inbox")
527
528 assert json_response(conn, 403)
529 end
530
531 test "it doesn't crash without an authenticated user", %{conn: conn} do
532 user = insert(:user)
533
534 conn =
535 conn
536 |> put_req_header("accept", "application/activity+json")
537 |> get("/users/#{user.nickname}/inbox")
538
539 assert json_response(conn, 403)
540 end
541
542 test "it returns a note activity in a collection", %{conn: conn} do
543 note_activity = insert(:direct_note_activity)
544 note_object = Object.normalize(note_activity)
545 user = User.get_cached_by_ap_id(hd(note_activity.data["to"]))
546
547 conn =
548 conn
549 |> assign(:user, user)
550 |> put_req_header("accept", "application/activity+json")
551 |> get("/users/#{user.nickname}/inbox?page=true")
552
553 assert response(conn, 200) =~ note_object.data["content"]
554 end
555
556 test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do
557 user = insert(:user)
558 data = Map.put(data, "bcc", [user.ap_id])
559
560 sender_host = URI.parse(data["actor"]).host
561 Instances.set_consistently_unreachable(sender_host)
562 refute Instances.reachable?(sender_host)
563
564 conn =
565 conn
566 |> assign(:valid_signature, true)
567 |> put_req_header("content-type", "application/activity+json")
568 |> post("/users/#{user.nickname}/inbox", data)
569
570 assert "ok" == json_response(conn, 200)
571 assert Instances.reachable?(sender_host)
572 end
573
574 test "it removes all follower collections but actor's", %{conn: conn} do
575 [actor, recipient] = insert_pair(:user)
576
577 data =
578 File.read!("test/fixtures/activitypub-client-post-activity.json")
579 |> Poison.decode!()
580
581 object = Map.put(data["object"], "attributedTo", actor.ap_id)
582
583 data =
584 data
585 |> Map.put("id", Utils.generate_object_id())
586 |> Map.put("actor", actor.ap_id)
587 |> Map.put("object", object)
588 |> Map.put("cc", [
589 recipient.follower_address,
590 actor.follower_address
591 ])
592 |> Map.put("to", [
593 recipient.ap_id,
594 recipient.follower_address,
595 "https://www.w3.org/ns/activitystreams#Public"
596 ])
597
598 conn
599 |> assign(:valid_signature, true)
600 |> put_req_header("content-type", "application/activity+json")
601 |> post("/users/#{recipient.nickname}/inbox", data)
602 |> json_response(200)
603
604 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
605
606 activity = Activity.get_by_ap_id(data["id"])
607
608 assert activity.id
609 assert actor.follower_address in activity.recipients
610 assert actor.follower_address in activity.data["cc"]
611
612 refute recipient.follower_address in activity.recipients
613 refute recipient.follower_address in activity.data["cc"]
614 refute recipient.follower_address in activity.data["to"]
615 end
616 end
617
618 describe "/users/:nickname/outbox" do
619 test "it will not bomb when there is no activity", %{conn: conn} do
620 user = insert(:user)
621
622 conn =
623 conn
624 |> put_req_header("accept", "application/activity+json")
625 |> get("/users/#{user.nickname}/outbox")
626
627 result = json_response(conn, 200)
628 assert user.ap_id <> "/outbox" == result["id"]
629 end
630
631 test "it returns a note activity in a collection", %{conn: conn} do
632 note_activity = insert(:note_activity)
633 note_object = Object.normalize(note_activity)
634 user = User.get_cached_by_ap_id(note_activity.data["actor"])
635
636 conn =
637 conn
638 |> put_req_header("accept", "application/activity+json")
639 |> get("/users/#{user.nickname}/outbox?page=true")
640
641 assert response(conn, 200) =~ note_object.data["content"]
642 end
643
644 test "it returns an announce activity in a collection", %{conn: conn} do
645 announce_activity = insert(:announce_activity)
646 user = User.get_cached_by_ap_id(announce_activity.data["actor"])
647
648 conn =
649 conn
650 |> put_req_header("accept", "application/activity+json")
651 |> get("/users/#{user.nickname}/outbox?page=true")
652
653 assert response(conn, 200) =~ announce_activity.data["object"]
654 end
655 end
656
657 describe "POST /users/:nickname/outbox (C2S)" do
658 setup do
659 [
660 activity: %{
661 "@context" => "https://www.w3.org/ns/activitystreams",
662 "type" => "Create",
663 "object" => %{"type" => "Note", "content" => "AP C2S test"},
664 "to" => "https://www.w3.org/ns/activitystreams#Public",
665 "cc" => []
666 }
667 ]
668 end
669
670 test "it rejects posts from other users / unauthenticated users", %{
671 conn: conn,
672 activity: activity
673 } do
674 user = insert(:user)
675 otheruser = insert(:user)
676
677 conn =
678 conn
679 |> assign(:user, otheruser)
680 |> put_req_header("content-type", "application/activity+json")
681 |> post("/users/#{user.nickname}/outbox", activity)
682
683 assert json_response(conn, 403)
684 end
685
686 test "it inserts an incoming create activity into the database", %{
687 conn: conn,
688 activity: activity
689 } do
690 user = insert(:user)
691
692 result =
693 conn
694 |> assign(:user, user)
695 |> put_req_header("content-type", "application/activity+json")
696 |> post("/users/#{user.nickname}/outbox", activity)
697 |> json_response(201)
698
699 assert Activity.get_by_ap_id(result["id"])
700 assert result["object"]
701 assert %Object{data: object} = Object.normalize(result["object"])
702 assert object["content"] == activity["object"]["content"]
703 end
704
705 test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do
706 user = insert(:user)
707
708 activity =
709 activity
710 |> put_in(["object", "type"], "Benis")
711
712 _result =
713 conn
714 |> assign(:user, user)
715 |> put_req_header("content-type", "application/activity+json")
716 |> post("/users/#{user.nickname}/outbox", activity)
717 |> json_response(400)
718 end
719
720 test "it inserts an incoming sensitive activity into the database", %{
721 conn: conn,
722 activity: activity
723 } do
724 user = insert(:user)
725 object = Map.put(activity["object"], "sensitive", true)
726 activity = Map.put(activity, "object", object)
727
728 result =
729 conn
730 |> assign(:user, user)
731 |> put_req_header("content-type", "application/activity+json")
732 |> post("/users/#{user.nickname}/outbox", activity)
733 |> json_response(201)
734
735 assert Activity.get_by_ap_id(result["id"])
736 assert result["object"]
737 assert %Object{data: object} = Object.normalize(result["object"])
738 assert object["sensitive"] == activity["object"]["sensitive"]
739 assert object["content"] == activity["object"]["content"]
740 end
741
742 test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do
743 user = insert(:user)
744 activity = Map.put(activity, "type", "BadType")
745
746 conn =
747 conn
748 |> assign(:user, user)
749 |> put_req_header("content-type", "application/activity+json")
750 |> post("/users/#{user.nickname}/outbox", activity)
751
752 assert json_response(conn, 400)
753 end
754
755 test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
756 note_activity = insert(:note_activity)
757 note_object = Object.normalize(note_activity)
758 user = User.get_cached_by_ap_id(note_activity.data["actor"])
759
760 data = %{
761 type: "Delete",
762 object: %{
763 id: note_object.data["id"]
764 }
765 }
766
767 conn =
768 conn
769 |> assign(:user, user)
770 |> put_req_header("content-type", "application/activity+json")
771 |> post("/users/#{user.nickname}/outbox", data)
772
773 result = json_response(conn, 201)
774 assert Activity.get_by_ap_id(result["id"])
775
776 assert object = Object.get_by_ap_id(note_object.data["id"])
777 assert object.data["type"] == "Tombstone"
778 end
779
780 test "it rejects delete activity of object from other actor", %{conn: conn} do
781 note_activity = insert(:note_activity)
782 note_object = Object.normalize(note_activity)
783 user = insert(:user)
784
785 data = %{
786 type: "Delete",
787 object: %{
788 id: note_object.data["id"]
789 }
790 }
791
792 conn =
793 conn
794 |> assign(:user, user)
795 |> put_req_header("content-type", "application/activity+json")
796 |> post("/users/#{user.nickname}/outbox", data)
797
798 assert json_response(conn, 400)
799 end
800
801 test "it increases like count when receiving a like action", %{conn: conn} do
802 note_activity = insert(:note_activity)
803 note_object = Object.normalize(note_activity)
804 user = User.get_cached_by_ap_id(note_activity.data["actor"])
805
806 data = %{
807 type: "Like",
808 object: %{
809 id: note_object.data["id"]
810 }
811 }
812
813 conn =
814 conn
815 |> assign(:user, user)
816 |> put_req_header("content-type", "application/activity+json")
817 |> post("/users/#{user.nickname}/outbox", data)
818
819 result = json_response(conn, 201)
820 assert Activity.get_by_ap_id(result["id"])
821
822 assert object = Object.get_by_ap_id(note_object.data["id"])
823 assert object.data["like_count"] == 1
824 end
825 end
826
827 describe "/relay/followers" do
828 test "it returns relay followers", %{conn: conn} do
829 relay_actor = Relay.get_actor()
830 user = insert(:user)
831 User.follow(user, relay_actor)
832
833 result =
834 conn
835 |> assign(:relay, true)
836 |> get("/relay/followers")
837 |> json_response(200)
838
839 assert result["first"]["orderedItems"] == [user.ap_id]
840 end
841 end
842
843 describe "/relay/following" do
844 test "it returns relay following", %{conn: conn} do
845 result =
846 conn
847 |> assign(:relay, true)
848 |> get("/relay/following")
849 |> json_response(200)
850
851 assert result["first"]["orderedItems"] == []
852 end
853 end
854
855 describe "/users/:nickname/followers" do
856 test "it returns the followers in a collection", %{conn: conn} do
857 user = insert(:user)
858 user_two = insert(:user)
859 User.follow(user, user_two)
860
861 result =
862 conn
863 |> get("/users/#{user_two.nickname}/followers")
864 |> json_response(200)
865
866 assert result["first"]["orderedItems"] == [user.ap_id]
867 end
868
869 test "it returns returns a uri if the user has 'hide_followers' set", %{conn: conn} do
870 user = insert(:user)
871 user_two = insert(:user, hide_followers: true)
872 User.follow(user, user_two)
873
874 result =
875 conn
876 |> get("/users/#{user_two.nickname}/followers")
877 |> json_response(200)
878
879 assert is_binary(result["first"])
880 end
881
882 test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is not authenticated",
883 %{conn: conn} do
884 user = insert(:user, hide_followers: true)
885
886 result =
887 conn
888 |> get("/users/#{user.nickname}/followers?page=1")
889
890 assert result.status == 403
891 assert result.resp_body == ""
892 end
893
894 test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user",
895 %{conn: conn} do
896 user = insert(:user, hide_followers: true)
897 other_user = insert(:user)
898 {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
899
900 result =
901 conn
902 |> assign(:user, user)
903 |> get("/users/#{user.nickname}/followers?page=1")
904 |> json_response(200)
905
906 assert result["totalItems"] == 1
907 assert result["orderedItems"] == [other_user.ap_id]
908 end
909
910 test "it works for more than 10 users", %{conn: conn} do
911 user = insert(:user)
912
913 Enum.each(1..15, fn _ ->
914 other_user = insert(:user)
915 User.follow(other_user, user)
916 end)
917
918 result =
919 conn
920 |> get("/users/#{user.nickname}/followers")
921 |> json_response(200)
922
923 assert length(result["first"]["orderedItems"]) == 10
924 assert result["first"]["totalItems"] == 15
925 assert result["totalItems"] == 15
926
927 result =
928 conn
929 |> get("/users/#{user.nickname}/followers?page=2")
930 |> json_response(200)
931
932 assert length(result["orderedItems"]) == 5
933 assert result["totalItems"] == 15
934 end
935 end
936
937 describe "/users/:nickname/following" do
938 test "it returns the following in a collection", %{conn: conn} do
939 user = insert(:user)
940 user_two = insert(:user)
941 User.follow(user, user_two)
942
943 result =
944 conn
945 |> get("/users/#{user.nickname}/following")
946 |> json_response(200)
947
948 assert result["first"]["orderedItems"] == [user_two.ap_id]
949 end
950
951 test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do
952 user = insert(:user, hide_follows: true)
953 user_two = insert(:user)
954 User.follow(user, user_two)
955
956 result =
957 conn
958 |> get("/users/#{user.nickname}/following")
959 |> json_response(200)
960
961 assert is_binary(result["first"])
962 end
963
964 test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is not authenticated",
965 %{conn: conn} do
966 user = insert(:user, hide_follows: true)
967
968 result =
969 conn
970 |> get("/users/#{user.nickname}/following?page=1")
971
972 assert result.status == 403
973 assert result.resp_body == ""
974 end
975
976 test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user",
977 %{conn: conn} do
978 user = insert(:user, hide_follows: true)
979 other_user = insert(:user)
980 {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
981
982 result =
983 conn
984 |> assign(:user, user)
985 |> get("/users/#{user.nickname}/following?page=1")
986 |> json_response(200)
987
988 assert result["totalItems"] == 1
989 assert result["orderedItems"] == [other_user.ap_id]
990 end
991
992 test "it works for more than 10 users", %{conn: conn} do
993 user = insert(:user)
994
995 Enum.each(1..15, fn _ ->
996 user = User.get_cached_by_id(user.id)
997 other_user = insert(:user)
998 User.follow(user, other_user)
999 end)
1000
1001 result =
1002 conn
1003 |> get("/users/#{user.nickname}/following")
1004 |> json_response(200)
1005
1006 assert length(result["first"]["orderedItems"]) == 10
1007 assert result["first"]["totalItems"] == 15
1008 assert result["totalItems"] == 15
1009
1010 result =
1011 conn
1012 |> get("/users/#{user.nickname}/following?page=2")
1013 |> json_response(200)
1014
1015 assert length(result["orderedItems"]) == 5
1016 assert result["totalItems"] == 15
1017 end
1018 end
1019
1020 describe "delivery tracking" do
1021 test "it tracks a signed object fetch", %{conn: conn} do
1022 user = insert(:user, local: false)
1023 activity = insert(:note_activity)
1024 object = Object.normalize(activity)
1025
1026 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1027
1028 conn
1029 |> put_req_header("accept", "application/activity+json")
1030 |> assign(:user, user)
1031 |> get(object_path)
1032 |> json_response(200)
1033
1034 assert Delivery.get(object.id, user.id)
1035 end
1036
1037 test "it tracks a signed activity fetch", %{conn: conn} do
1038 user = insert(:user, local: false)
1039 activity = insert(:note_activity)
1040 object = Object.normalize(activity)
1041
1042 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1043
1044 conn
1045 |> put_req_header("accept", "application/activity+json")
1046 |> assign(:user, user)
1047 |> get(activity_path)
1048 |> json_response(200)
1049
1050 assert Delivery.get(object.id, user.id)
1051 end
1052
1053 test "it tracks a signed object fetch when the json is cached", %{conn: conn} do
1054 user = insert(:user, local: false)
1055 other_user = insert(:user, local: false)
1056 activity = insert(:note_activity)
1057 object = Object.normalize(activity)
1058
1059 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1060
1061 conn
1062 |> put_req_header("accept", "application/activity+json")
1063 |> assign(:user, user)
1064 |> get(object_path)
1065 |> json_response(200)
1066
1067 build_conn()
1068 |> put_req_header("accept", "application/activity+json")
1069 |> assign(:user, other_user)
1070 |> get(object_path)
1071 |> json_response(200)
1072
1073 assert Delivery.get(object.id, user.id)
1074 assert Delivery.get(object.id, other_user.id)
1075 end
1076
1077 test "it tracks a signed activity fetch when the json is cached", %{conn: conn} do
1078 user = insert(:user, local: false)
1079 other_user = insert(:user, local: false)
1080 activity = insert(:note_activity)
1081 object = Object.normalize(activity)
1082
1083 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1084
1085 conn
1086 |> put_req_header("accept", "application/activity+json")
1087 |> assign(:user, user)
1088 |> get(activity_path)
1089 |> json_response(200)
1090
1091 build_conn()
1092 |> put_req_header("accept", "application/activity+json")
1093 |> assign(:user, other_user)
1094 |> get(activity_path)
1095 |> json_response(200)
1096
1097 assert Delivery.get(object.id, user.id)
1098 assert Delivery.get(object.id, other_user.id)
1099 end
1100 end
1101
1102 describe "Additionnal ActivityPub C2S endpoints" do
1103 test "/api/ap/whoami", %{conn: conn} do
1104 user = insert(:user)
1105
1106 conn =
1107 conn
1108 |> assign(:user, user)
1109 |> get("/api/ap/whoami")
1110
1111 user = User.get_cached_by_id(user.id)
1112
1113 assert UserView.render("user.json", %{user: user}) == json_response(conn, 200)
1114 end
1115
1116 clear_config([:media_proxy])
1117 clear_config([Pleroma.Upload])
1118
1119 test "uploadMedia", %{conn: conn} do
1120 user = insert(:user)
1121
1122 desc = "Description of the image"
1123
1124 image = %Plug.Upload{
1125 content_type: "image/jpg",
1126 path: Path.absname("test/fixtures/image.jpg"),
1127 filename: "an_image.jpg"
1128 }
1129
1130 conn =
1131 conn
1132 |> assign(:user, user)
1133 |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
1134
1135 assert object = json_response(conn, :created)
1136 assert object["name"] == desc
1137 assert object["type"] == "Document"
1138 assert object["actor"] == user.ap_id
1139 end
1140 end
1141 end