diff options
Diffstat (limited to 'test/web/activity_pub/activity_pub_controller_test.exs')
-rw-r--r-- | test/web/activity_pub/activity_pub_controller_test.exs | 1534 |
1 files changed, 0 insertions, 1534 deletions
diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs deleted file mode 100644 index ed900d8f8..000000000 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ /dev/null @@ -1,1534 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do - use Pleroma.Web.ConnCase - use Oban.Testing, repo: Pleroma.Repo - - alias Pleroma.Activity - alias Pleroma.Config - alias Pleroma.Delivery - alias Pleroma.Instances - alias Pleroma.Object - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.ObjectView - alias Pleroma.Web.ActivityPub.Relay - alias Pleroma.Web.ActivityPub.UserView - alias Pleroma.Web.ActivityPub.Utils - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.Endpoint - alias Pleroma.Workers.ReceiverWorker - - import Pleroma.Factory - - require Pleroma.Constants - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - setup do: clear_config([:instance, :federating], true) - - describe "/relay" do - setup do: clear_config([:instance, :allow_relay]) - - test "with the relay active, it returns the relay user", %{conn: conn} do - res = - conn - |> get(activity_pub_path(conn, :relay)) - |> json_response(200) - - assert res["id"] =~ "/relay" - end - - test "with the relay disabled, it returns 404", %{conn: conn} do - Config.put([:instance, :allow_relay], false) - - conn - |> get(activity_pub_path(conn, :relay)) - |> json_response(404) - end - - test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) - user = insert(:user) - - conn - |> assign(:user, user) - |> get(activity_pub_path(conn, :relay)) - |> json_response(404) - end - end - - describe "/internal/fetch" do - test "it returns the internal fetch user", %{conn: conn} do - res = - conn - |> get(activity_pub_path(conn, :internal_fetch)) - |> json_response(200) - - assert res["id"] =~ "/fetch" - end - - test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) - user = insert(:user) - - conn - |> assign(:user, user) - |> get(activity_pub_path(conn, :internal_fetch)) - |> json_response(404) - end - end - - describe "/users/:nickname" do - test "it returns a json representation of the user with accept application/json", %{ - conn: conn - } do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/users/#{user.nickname}") - - user = User.get_cached_by_id(user.id) - - assert json_response(conn, 200) == UserView.render("user.json", %{user: user}) - end - - test "it returns a json representation of the user with accept application/activity+json", %{ - conn: conn - } do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}") - - user = User.get_cached_by_id(user.id) - - assert json_response(conn, 200) == UserView.render("user.json", %{user: user}) - end - - test "it returns a json representation of the user with accept application/ld+json", %{ - conn: conn - } do - user = insert(:user) - - conn = - conn - |> put_req_header( - "accept", - "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - ) - |> get("/users/#{user.nickname}") - - user = User.get_cached_by_id(user.id) - - assert json_response(conn, 200) == UserView.render("user.json", %{user: user}) - end - - test "it returns 404 for remote users", %{ - conn: conn - } do - user = insert(:user, local: false, nickname: "remoteuser@example.com") - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/users/#{user.nickname}.json") - - assert json_response(conn, 404) - end - - test "it returns error when user is not found", %{conn: conn} do - response = - conn - |> put_req_header("accept", "application/json") - |> get("/users/jimm") - |> json_response(404) - - assert response == "Not found" - end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - - conn = - put_req_header( - conn, - "accept", - "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - ) - - ensure_federating_or_authenticated(conn, "/users/#{user.nickname}.json", user) - end - end - - describe "mastodon compatibility routes" do - test "it returns a json representation of the object with accept application/json", %{ - conn: conn - } do - {:ok, object} = - %{ - "type" => "Note", - "content" => "hey", - "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999", - "actor" => Endpoint.url() <> "/users/raymoo", - "to" => [Pleroma.Constants.as_public()] - } - |> Object.create() - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/users/raymoo/statuses/999999999") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: object}) - end - - test "it returns a json representation of the activity with accept application/json", %{ - conn: conn - } do - {:ok, object} = - %{ - "type" => "Note", - "content" => "hey", - "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999", - "actor" => Endpoint.url() <> "/users/raymoo", - "to" => [Pleroma.Constants.as_public()] - } - |> Object.create() - - {:ok, activity, _} = - %{ - "id" => object.data["id"] <> "/activity", - "type" => "Create", - "object" => object.data["id"], - "actor" => object.data["actor"], - "to" => object.data["to"] - } - |> ActivityPub.persist(local: true) - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/users/raymoo/statuses/999999999/activity") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity}) - end - end - - describe "/objects/:uuid" do - test "it returns a json representation of the object with accept application/json", %{ - conn: conn - } do - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/objects/#{uuid}") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note}) - end - - test "it returns a json representation of the object with accept application/activity+json", - %{conn: conn} do - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note}) - end - - test "it returns a json representation of the object with accept application/ld+json", %{ - conn: conn - } do - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header( - "accept", - "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - ) - |> get("/objects/#{uuid}") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note}) - end - - test "it returns 404 for non-public messages", %{conn: conn} do - note = insert(:direct_note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn, 404) - end - - test "it returns 404 for tombstone objects", %{conn: conn} do - tombstone = insert(:tombstone) - uuid = String.split(tombstone.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn, 404) - end - - test "it caches a response", %{conn: conn} do - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn1 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn1, :ok) - assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) - - conn2 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn1, :ok) == json_response(conn2, :ok) - assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"})) - end - - test "cached purged after object deletion", %{conn: conn} do - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn1 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn1, :ok) - assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) - - Object.delete(note) - - conn2 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert "Not found" == json_response(conn2, :not_found) - end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = put_req_header(conn, "accept", "application/activity+json") - - ensure_federating_or_authenticated(conn, "/objects/#{uuid}", user) - end - end - - describe "/activities/:uuid" do - test "it returns a json representation of the activity", %{conn: conn} do - activity = insert(:note_activity) - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity}) - end - - test "it returns 404 for non-public activities", %{conn: conn} do - activity = insert(:direct_note_activity) - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert json_response(conn, 404) - end - - test "it caches a response", %{conn: conn} do - activity = insert(:note_activity) - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn1 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert json_response(conn1, :ok) - assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) - - conn2 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert json_response(conn1, :ok) == json_response(conn2, :ok) - assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"})) - end - - test "cached purged after activity deletion", %{conn: conn} do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "cofe"}) - - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn1 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert json_response(conn1, :ok) - assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) - - Activity.delete_all_by_object_ap_id(activity.object.data["id"]) - - conn2 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert "Not found" == json_response(conn2, :not_found) - end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - activity = insert(:note_activity) - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn = put_req_header(conn, "accept", "application/activity+json") - - ensure_federating_or_authenticated(conn, "/activities/#{uuid}", user) - end - end - - describe "/inbox" do - test "it inserts an incoming activity into the database", %{conn: conn} do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/inbox", data) - - assert "ok" == json_response(conn, 200) - - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - @tag capture_log: true - test "it inserts an incoming activity into the database" <> - "even if we can't fetch the user but have it in our db", - %{conn: conn} do - user = - insert(:user, - ap_id: "https://mastodon.example.org/users/raymoo", - ap_enabled: true, - local: false, - last_refreshed_at: nil - ) - - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("actor", user.ap_id) - |> put_in(["object", "attridbutedTo"], user.ap_id) - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/inbox", data) - - assert "ok" == json_response(conn, 200) - - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - test "it clears `unreachable` federation status of the sender", %{conn: conn} do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - - sender_url = data["actor"] - Instances.set_consistently_unreachable(sender_url) - refute Instances.reachable?(sender_url) - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/inbox", data) - - assert "ok" == json_response(conn, 200) - assert Instances.reachable?(sender_url) - end - - test "accept follow activity", %{conn: conn} do - Pleroma.Config.put([:instance, :federating], true) - relay = Relay.get_actor() - - assert {:ok, %Activity{} = activity} = Relay.follow("https://relay.mastodon.host/actor") - - followed_relay = Pleroma.User.get_by_ap_id("https://relay.mastodon.host/actor") - relay = refresh_record(relay) - - accept = - File.read!("test/fixtures/relay/accept-follow.json") - |> String.replace("{{ap_id}}", relay.ap_id) - |> String.replace("{{activity_id}}", activity.data["id"]) - - assert "ok" == - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/inbox", accept) - |> json_response(200) - - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - - assert Pleroma.FollowingRelationship.following?( - relay, - followed_relay - ) - - Mix.shell(Mix.Shell.Process) - - on_exit(fn -> - Mix.shell(Mix.Shell.IO) - end) - - :ok = Mix.Tasks.Pleroma.Relay.run(["list"]) - assert_receive {:mix_shell, :info, ["relay.mastodon.host"]} - end - - @tag capture_log: true - test "without valid signature, " <> - "it only accepts Create activities and requires enabled federation", - %{conn: conn} do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - non_create_data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!() - - conn = put_req_header(conn, "content-type", "application/activity+json") - - Config.put([:instance, :federating], false) - - conn - |> post("/inbox", data) - |> json_response(403) - - conn - |> post("/inbox", non_create_data) - |> json_response(403) - - Config.put([:instance, :federating], true) - - ret_conn = post(conn, "/inbox", data) - assert "ok" == json_response(ret_conn, 200) - - conn - |> post("/inbox", non_create_data) - |> json_response(400) - end - end - - describe "/users/:nickname/inbox" do - setup do - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - - [data: data] - end - - test "it inserts an incoming activity into the database", %{conn: conn, data: data} do - user = insert(:user) - data = Map.put(data, "bcc", [user.ap_id]) - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do - user = insert(:user) - - data = - Map.put(data, "to", user.ap_id) - |> Map.delete("cc") - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - test "it accepts messages with cc as string instead of array", %{conn: conn, data: data} do - user = insert(:user) - - data = - Map.put(data, "cc", user.ap_id) - |> Map.delete("to") - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - %Activity{} = activity = Activity.get_by_ap_id(data["id"]) - assert user.ap_id in activity.recipients - end - - test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do - user = insert(:user) - - data = - Map.put(data, "bcc", user.ap_id) - |> Map.delete("to") - |> Map.delete("cc") - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - test "it accepts announces with to as string instead of array", %{conn: conn} do - user = insert(:user) - - {:ok, post} = CommonAPI.post(user, %{status: "hey"}) - announcer = insert(:user, local: false) - - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "actor" => announcer.ap_id, - "id" => "#{announcer.ap_id}/statuses/19512778738411822/activity", - "object" => post.data["object"], - "to" => "https://www.w3.org/ns/activitystreams#Public", - "cc" => [user.ap_id], - "type" => "Announce" - } - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - %Activity{} = activity = Activity.get_by_ap_id(data["id"]) - assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients - end - - test "it accepts messages from actors that are followed by the user", %{ - conn: conn, - data: data - } do - recipient = insert(:user) - actor = insert(:user, %{ap_id: "http://mastodon.example.org/users/actor"}) - - {:ok, recipient} = User.follow(recipient, actor) - - object = - data["object"] - |> Map.put("attributedTo", actor.ap_id) - - data = - data - |> Map.put("actor", actor.ap_id) - |> Map.put("object", object) - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{recipient.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - test "it rejects reads from other users", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - conn = - conn - |> assign(:user, other_user) - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}/inbox") - - assert json_response(conn, 403) - end - - test "it returns a note activity in a collection", %{conn: conn} do - note_activity = insert(:direct_note_activity) - note_object = Object.normalize(note_activity) - user = User.get_cached_by_ap_id(hd(note_activity.data["to"])) - - conn = - conn - |> assign(:user, user) - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}/inbox?page=true") - - assert response(conn, 200) =~ note_object.data["content"] - end - - test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do - user = insert(:user) - data = Map.put(data, "bcc", [user.ap_id]) - - sender_host = URI.parse(data["actor"]).host - Instances.set_consistently_unreachable(sender_host) - refute Instances.reachable?(sender_host) - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - assert Instances.reachable?(sender_host) - end - - test "it removes all follower collections but actor's", %{conn: conn} do - [actor, recipient] = insert_pair(:user) - - data = - File.read!("test/fixtures/activitypub-client-post-activity.json") - |> Poison.decode!() - - object = Map.put(data["object"], "attributedTo", actor.ap_id) - - data = - data - |> Map.put("id", Utils.generate_object_id()) - |> Map.put("actor", actor.ap_id) - |> Map.put("object", object) - |> Map.put("cc", [ - recipient.follower_address, - actor.follower_address - ]) - |> Map.put("to", [ - recipient.ap_id, - recipient.follower_address, - "https://www.w3.org/ns/activitystreams#Public" - ]) - - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{recipient.nickname}/inbox", data) - |> json_response(200) - - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - - activity = Activity.get_by_ap_id(data["id"]) - - assert activity.id - assert actor.follower_address in activity.recipients - assert actor.follower_address in activity.data["cc"] - - refute recipient.follower_address in activity.recipients - refute recipient.follower_address in activity.data["cc"] - refute recipient.follower_address in activity.data["to"] - end - - test "it requires authentication", %{conn: conn} do - user = insert(:user) - conn = put_req_header(conn, "accept", "application/activity+json") - - ret_conn = get(conn, "/users/#{user.nickname}/inbox") - assert json_response(ret_conn, 403) - - ret_conn = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/inbox") - - assert json_response(ret_conn, 200) - end - end - - describe "GET /users/:nickname/outbox" do - test "it paginates correctly", %{conn: conn} do - user = insert(:user) - conn = assign(conn, :user, user) - outbox_endpoint = user.ap_id <> "/outbox" - - _posts = - for i <- 0..25 do - {:ok, activity} = CommonAPI.post(user, %{status: "post #{i}"}) - activity - end - - result = - conn - |> put_req_header("accept", "application/activity+json") - |> get(outbox_endpoint <> "?page=true") - |> json_response(200) - - result_ids = Enum.map(result["orderedItems"], fn x -> x["id"] end) - assert length(result["orderedItems"]) == 20 - assert length(result_ids) == 20 - assert result["next"] - assert String.starts_with?(result["next"], outbox_endpoint) - - result_next = - conn - |> put_req_header("accept", "application/activity+json") - |> get(result["next"]) - |> json_response(200) - - result_next_ids = Enum.map(result_next["orderedItems"], fn x -> x["id"] end) - assert length(result_next["orderedItems"]) == 6 - assert length(result_next_ids) == 6 - refute Enum.find(result_next_ids, fn x -> x in result_ids end) - refute Enum.find(result_ids, fn x -> x in result_next_ids end) - assert String.starts_with?(result["id"], outbox_endpoint) - - result_next_again = - conn - |> put_req_header("accept", "application/activity+json") - |> get(result_next["id"]) - |> json_response(200) - - assert result_next == result_next_again - end - - test "it returns 200 even if there're no activities", %{conn: conn} do - user = insert(:user) - outbox_endpoint = user.ap_id <> "/outbox" - - conn = - conn - |> assign(:user, user) - |> put_req_header("accept", "application/activity+json") - |> get(outbox_endpoint) - - result = json_response(conn, 200) - assert outbox_endpoint == result["id"] - end - - test "it returns a note activity in a collection", %{conn: conn} do - note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - conn = - conn - |> assign(:user, user) - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}/outbox?page=true") - - assert response(conn, 200) =~ note_object.data["content"] - end - - test "it returns an announce activity in a collection", %{conn: conn} do - announce_activity = insert(:announce_activity) - user = User.get_cached_by_ap_id(announce_activity.data["actor"]) - - conn = - conn - |> assign(:user, user) - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}/outbox?page=true") - - assert response(conn, 200) =~ announce_activity.data["object"] - end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - conn = put_req_header(conn, "accept", "application/activity+json") - - ensure_federating_or_authenticated(conn, "/users/#{user.nickname}/outbox", user) - end - end - - describe "POST /users/:nickname/outbox (C2S)" do - setup do - [ - activity: %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Create", - "object" => %{"type" => "Note", "content" => "AP C2S test"}, - "to" => "https://www.w3.org/ns/activitystreams#Public", - "cc" => [] - } - ] - end - - test "it rejects posts from other users / unauthenticated users", %{ - conn: conn, - activity: activity - } do - user = insert(:user) - other_user = insert(:user) - conn = put_req_header(conn, "content-type", "application/activity+json") - - conn - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(403) - - conn - |> assign(:user, other_user) - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(403) - end - - test "it inserts an incoming create activity into the database", %{ - conn: conn, - activity: activity - } do - user = insert(:user) - - result = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(201) - - assert Activity.get_by_ap_id(result["id"]) - assert result["object"] - assert %Object{data: object} = Object.normalize(result["object"]) - assert object["content"] == activity["object"]["content"] - end - - test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do - user = insert(:user) - - activity = - activity - |> put_in(["object", "type"], "Benis") - - _result = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(400) - end - - test "it inserts an incoming sensitive activity into the database", %{ - conn: conn, - activity: activity - } do - user = insert(:user) - conn = assign(conn, :user, user) - object = Map.put(activity["object"], "sensitive", true) - activity = Map.put(activity, "object", object) - - response = - conn - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(201) - - assert Activity.get_by_ap_id(response["id"]) - assert response["object"] - assert %Object{data: response_object} = Object.normalize(response["object"]) - assert response_object["sensitive"] == true - assert response_object["content"] == activity["object"]["content"] - - representation = - conn - |> put_req_header("accept", "application/activity+json") - |> get(response["id"]) - |> json_response(200) - - assert representation["object"]["sensitive"] == true - end - - test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do - user = insert(:user) - activity = Map.put(activity, "type", "BadType") - - conn = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", activity) - - assert json_response(conn, 400) - end - - test "it erects a tombstone when receiving a delete activity", %{conn: conn} do - note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - data = %{ - type: "Delete", - object: %{ - id: note_object.data["id"] - } - } - - conn = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", data) - - result = json_response(conn, 201) - assert Activity.get_by_ap_id(result["id"]) - - assert object = Object.get_by_ap_id(note_object.data["id"]) - assert object.data["type"] == "Tombstone" - end - - test "it rejects delete activity of object from other actor", %{conn: conn} do - note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) - user = insert(:user) - - data = %{ - type: "Delete", - object: %{ - id: note_object.data["id"] - } - } - - conn = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", data) - - assert json_response(conn, 400) - end - - test "it increases like count when receiving a like action", %{conn: conn} do - note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - data = %{ - type: "Like", - object: %{ - id: note_object.data["id"] - } - } - - conn = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", data) - - result = json_response(conn, 201) - assert Activity.get_by_ap_id(result["id"]) - - assert object = Object.get_by_ap_id(note_object.data["id"]) - assert object.data["like_count"] == 1 - end - - test "it doesn't spreads faulty attributedTo or actor fields", %{ - conn: conn, - activity: activity - } do - reimu = insert(:user, nickname: "reimu") - cirno = insert(:user, nickname: "cirno") - - assert reimu.ap_id - assert cirno.ap_id - - activity = - activity - |> put_in(["object", "actor"], reimu.ap_id) - |> put_in(["object", "attributedTo"], reimu.ap_id) - |> put_in(["actor"], reimu.ap_id) - |> put_in(["attributedTo"], reimu.ap_id) - - _reimu_outbox = - conn - |> assign(:user, cirno) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{reimu.nickname}/outbox", activity) - |> json_response(403) - - cirno_outbox = - conn - |> assign(:user, cirno) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{cirno.nickname}/outbox", activity) - |> json_response(201) - - assert cirno_outbox["attributedTo"] == nil - assert cirno_outbox["actor"] == cirno.ap_id - - assert cirno_object = Object.normalize(cirno_outbox["object"]) - assert cirno_object.data["actor"] == cirno.ap_id - assert cirno_object.data["attributedTo"] == cirno.ap_id - end - end - - describe "/relay/followers" do - test "it returns relay followers", %{conn: conn} do - relay_actor = Relay.get_actor() - user = insert(:user) - User.follow(user, relay_actor) - - result = - conn - |> get("/relay/followers") - |> json_response(200) - - assert result["first"]["orderedItems"] == [user.ap_id] - end - - test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) - user = insert(:user) - - conn - |> assign(:user, user) - |> get("/relay/followers") - |> json_response(404) - end - end - - describe "/relay/following" do - test "it returns relay following", %{conn: conn} do - result = - conn - |> get("/relay/following") - |> json_response(200) - - assert result["first"]["orderedItems"] == [] - end - - test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) - user = insert(:user) - - conn - |> assign(:user, user) - |> get("/relay/following") - |> json_response(404) - end - end - - describe "/users/:nickname/followers" do - test "it returns the followers in a collection", %{conn: conn} do - user = insert(:user) - user_two = insert(:user) - User.follow(user, user_two) - - result = - conn - |> assign(:user, user_two) - |> get("/users/#{user_two.nickname}/followers") - |> json_response(200) - - assert result["first"]["orderedItems"] == [user.ap_id] - end - - test "it returns a uri if the user has 'hide_followers' set", %{conn: conn} do - user = insert(:user) - user_two = insert(:user, hide_followers: true) - User.follow(user, user_two) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user_two.nickname}/followers") - |> json_response(200) - - assert is_binary(result["first"]) - end - - test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is from another user", - %{conn: conn} do - user = insert(:user) - other_user = insert(:user, hide_followers: true) - - result = - conn - |> assign(:user, user) - |> get("/users/#{other_user.nickname}/followers?page=1") - - assert result.status == 403 - assert result.resp_body == "" - end - - test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user", - %{conn: conn} do - user = insert(:user, hide_followers: true) - other_user = insert(:user) - {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/followers?page=1") - |> json_response(200) - - assert result["totalItems"] == 1 - assert result["orderedItems"] == [other_user.ap_id] - end - - test "it works for more than 10 users", %{conn: conn} do - user = insert(:user) - - Enum.each(1..15, fn _ -> - other_user = insert(:user) - User.follow(other_user, user) - end) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/followers") - |> json_response(200) - - assert length(result["first"]["orderedItems"]) == 10 - assert result["first"]["totalItems"] == 15 - assert result["totalItems"] == 15 - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/followers?page=2") - |> json_response(200) - - assert length(result["orderedItems"]) == 5 - assert result["totalItems"] == 15 - end - - test "does not require authentication", %{conn: conn} do - user = insert(:user) - - conn - |> get("/users/#{user.nickname}/followers") - |> json_response(200) - end - end - - describe "/users/:nickname/following" do - test "it returns the following in a collection", %{conn: conn} do - user = insert(:user) - user_two = insert(:user) - User.follow(user, user_two) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/following") - |> json_response(200) - - assert result["first"]["orderedItems"] == [user_two.ap_id] - end - - test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do - user = insert(:user) - user_two = insert(:user, hide_follows: true) - User.follow(user, user_two) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user_two.nickname}/following") - |> json_response(200) - - assert is_binary(result["first"]) - end - - test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is from another user", - %{conn: conn} do - user = insert(:user) - user_two = insert(:user, hide_follows: true) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user_two.nickname}/following?page=1") - - assert result.status == 403 - assert result.resp_body == "" - end - - test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user", - %{conn: conn} do - user = insert(:user, hide_follows: true) - other_user = insert(:user) - {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/following?page=1") - |> json_response(200) - - assert result["totalItems"] == 1 - assert result["orderedItems"] == [other_user.ap_id] - end - - test "it works for more than 10 users", %{conn: conn} do - user = insert(:user) - - Enum.each(1..15, fn _ -> - user = User.get_cached_by_id(user.id) - other_user = insert(:user) - User.follow(user, other_user) - end) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/following") - |> json_response(200) - - assert length(result["first"]["orderedItems"]) == 10 - assert result["first"]["totalItems"] == 15 - assert result["totalItems"] == 15 - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/following?page=2") - |> json_response(200) - - assert length(result["orderedItems"]) == 5 - assert result["totalItems"] == 15 - end - - test "does not require authentication", %{conn: conn} do - user = insert(:user) - - conn - |> get("/users/#{user.nickname}/following") - |> json_response(200) - end - end - - describe "delivery tracking" do - test "it tracks a signed object fetch", %{conn: conn} do - user = insert(:user, local: false) - activity = insert(:note_activity) - object = Object.normalize(activity) - - object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url()) - - conn - |> put_req_header("accept", "application/activity+json") - |> assign(:user, user) - |> get(object_path) - |> json_response(200) - - assert Delivery.get(object.id, user.id) - end - - test "it tracks a signed activity fetch", %{conn: conn} do - user = insert(:user, local: false) - activity = insert(:note_activity) - object = Object.normalize(activity) - - activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url()) - - conn - |> put_req_header("accept", "application/activity+json") - |> assign(:user, user) - |> get(activity_path) - |> json_response(200) - - assert Delivery.get(object.id, user.id) - end - - test "it tracks a signed object fetch when the json is cached", %{conn: conn} do - user = insert(:user, local: false) - other_user = insert(:user, local: false) - activity = insert(:note_activity) - object = Object.normalize(activity) - - object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url()) - - conn - |> put_req_header("accept", "application/activity+json") - |> assign(:user, user) - |> get(object_path) - |> json_response(200) - - build_conn() - |> put_req_header("accept", "application/activity+json") - |> assign(:user, other_user) - |> get(object_path) - |> json_response(200) - - assert Delivery.get(object.id, user.id) - assert Delivery.get(object.id, other_user.id) - end - - test "it tracks a signed activity fetch when the json is cached", %{conn: conn} do - user = insert(:user, local: false) - other_user = insert(:user, local: false) - activity = insert(:note_activity) - object = Object.normalize(activity) - - activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url()) - - conn - |> put_req_header("accept", "application/activity+json") - |> assign(:user, user) - |> get(activity_path) - |> json_response(200) - - build_conn() - |> put_req_header("accept", "application/activity+json") - |> assign(:user, other_user) - |> get(activity_path) - |> json_response(200) - - assert Delivery.get(object.id, user.id) - assert Delivery.get(object.id, other_user.id) - end - end - - describe "Additional ActivityPub C2S endpoints" do - test "GET /api/ap/whoami", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> get("/api/ap/whoami") - - user = User.get_cached_by_id(user.id) - - assert UserView.render("user.json", %{user: user}) == json_response(conn, 200) - - conn - |> get("/api/ap/whoami") - |> json_response(403) - end - - setup do: clear_config([:media_proxy]) - setup do: clear_config([Pleroma.Upload]) - - test "POST /api/ap/upload_media", %{conn: conn} do - user = insert(:user) - - desc = "Description of the image" - - image = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - object = - conn - |> assign(:user, user) - |> post("/api/ap/upload_media", %{"file" => image, "description" => desc}) - |> json_response(:created) - - assert object["name"] == desc - assert object["type"] == "Document" - assert object["actor"] == user.ap_id - assert [%{"href" => object_href, "mediaType" => object_mediatype}] = object["url"] - assert is_binary(object_href) - assert object_mediatype == "image/jpeg" - - activity_request = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Create", - "object" => %{ - "type" => "Note", - "content" => "AP C2S test, attachment", - "attachment" => [object] - }, - "to" => "https://www.w3.org/ns/activitystreams#Public", - "cc" => [] - } - - activity_response = - conn - |> assign(:user, user) - |> post("/users/#{user.nickname}/outbox", activity_request) - |> json_response(:created) - - assert activity_response["id"] - assert activity_response["object"] - assert activity_response["actor"] == user.ap_id - - assert %Object{data: %{"attachment" => [attachment]}} = - Object.normalize(activity_response["object"]) - - assert attachment["type"] == "Document" - assert attachment["name"] == desc - - assert [ - %{ - "href" => ^object_href, - "type" => "Link", - "mediaType" => ^object_mediatype - } - ] = attachment["url"] - - # Fails if unauthenticated - conn - |> post("/api/ap/upload_media", %{"file" => image, "description" => desc}) - |> json_response(403) - end - end -end |