commit: dc4de79d43ba941d8aa16f7c14887faae9f65e9f
parent 93ad16cca0a5b6acc1308027f798e347f44de4f8
Author: faried nawaz <faried@gmail.com>
Date: Wed, 7 Dec 2022 22:37:50 +0500
status context: perform visibility check on activities around a status
issue #2927
Diffstat:
2 files changed, 290 insertions(+), 0 deletions(-)
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -455,6 +455,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> maybe_preload_objects(opts)
|> maybe_preload_bookmarks(opts)
|> maybe_set_thread_muted_field(opts)
+ |> restrict_unauthenticated(opts[:user])
|> restrict_blocked(opts)
|> restrict_blockers_visibility(opts)
|> restrict_recipients(recipients, opts[:user])
@@ -1215,6 +1216,28 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_filtered(query, _), do: query
+ defp restrict_unauthenticated(query, nil) do
+ local = Config.restrict_unauthenticated_access?(:activities, :local)
+ remote = Config.restrict_unauthenticated_access?(:activities, :remote)
+
+ cond do
+ local and remote ->
+ # is there a better way to handle this?
+ from(activity in query, where: 1 == 0)
+
+ local ->
+ from(activity in query, where: activity.local == false)
+
+ remote ->
+ from(activity in query, where: activity.local == true)
+
+ true ->
+ query
+ end
+ end
+
+ defp restrict_unauthenticated(query, _), do: query
+
defp exclude_poll_votes(query, %{include_poll_votes: true}), do: query
defp exclude_poll_votes(query, _) do
diff --git a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs
@@ -771,6 +771,49 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
{:ok, local: local, remote: remote}
end
+ defp local_and_remote_context_activities do
+ local_user_1 = insert(:user)
+ local_user_2 = insert(:user)
+ remote_user = insert(:user, local: false)
+
+ {:ok, %{id: id1, data: %{"context" => context}}} =
+ CommonAPI.post(local_user_1, %{status: "post"})
+
+ {:ok, %{id: id2} = post} =
+ CommonAPI.post(local_user_2, %{status: "local reply", in_reply_to_status_id: id1})
+
+ params = %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "actor" => remote_user.ap_id,
+ "type" => "Create",
+ "context" => context,
+ "id" => "#{remote_user.ap_id}/activities/1",
+ "inReplyTo" => post.data["id"],
+ "object" => %{
+ "type" => "Note",
+ "content" => "remote reply",
+ "context" => context,
+ "id" => "#{remote_user.ap_id}/objects/1",
+ "attributedTo" => remote_user.ap_id,
+ "to" => [
+ local_user_1.ap_id,
+ local_user_2.ap_id,
+ "https://www.w3.org/ns/activitystreams#Public"
+ ]
+ },
+ "to" => [
+ local_user_1.ap_id,
+ local_user_2.ap_id,
+ "https://www.w3.org/ns/activitystreams#Public"
+ ]
+ }
+
+ {:ok, job} = Pleroma.Web.Federator.incoming_ap_doc(params)
+ {:ok, remote_activity} = ObanHelpers.perform(job)
+
+ %{locals: [id1, id2], remote: remote_activity.id, context: context}
+ end
+
describe "status with restrict unauthenticated activities for local and remote" do
setup do: local_and_remote_activities()
@@ -957,6 +1000,230 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
end
end
+ describe "getting status contexts restricted unauthenticated for local and remote" do
+ setup do: local_and_remote_context_activities()
+
+ setup do: clear_config([:restrict_unauthenticated, :activities, :local], true)
+
+ setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true)
+
+ test "if user is unauthenticated", %{conn: conn, locals: [post_id, _]} do
+ res_conn = get(conn, "/api/v1/statuses/#{post_id}/context")
+
+ assert json_response_and_validate_schema(res_conn, 200) == %{
+ "ancestors" => [],
+ "descendants" => []
+ }
+ end
+
+ test "if user is unauthenticated reply", %{conn: conn, locals: [_, reply_id]} do
+ res_conn = get(conn, "/api/v1/statuses/#{reply_id}/context")
+
+ assert json_response_and_validate_schema(res_conn, 200) == %{
+ "ancestors" => [],
+ "descendants" => []
+ }
+ end
+
+ test "if user is authenticated", %{locals: [post_id, reply_id], remote: remote_reply_id} do
+ %{conn: conn} = oauth_access(["read"])
+ res_conn = get(conn, "/api/v1/statuses/#{post_id}/context")
+
+ %{"ancestors" => [], "descendants" => descendants} =
+ json_response_and_validate_schema(res_conn, 200)
+
+ descendant_ids =
+ descendants
+ |> Enum.map(& &1["id"])
+
+ assert reply_id in descendant_ids
+ assert remote_reply_id in descendant_ids
+ end
+
+ test "if user is authenticated reply", %{locals: [post_id, reply_id], remote: remote_reply_id} do
+ %{conn: conn} = oauth_access(["read"])
+ res_conn = get(conn, "/api/v1/statuses/#{reply_id}/context")
+
+ %{"ancestors" => ancestors, "descendants" => descendants} =
+ json_response_and_validate_schema(res_conn, 200)
+
+ ancestor_ids =
+ ancestors
+ |> Enum.map(& &1["id"])
+
+ descendant_ids =
+ descendants
+ |> Enum.map(& &1["id"])
+
+ assert post_id in ancestor_ids
+ assert remote_reply_id in descendant_ids
+ end
+ end
+
+ describe "getting status contexts restricted unauthenticated for local" do
+ setup do: local_and_remote_context_activities()
+
+ setup do: clear_config([:restrict_unauthenticated, :activities, :local], true)
+
+ setup do: clear_config([:restrict_unauthenticated, :activities, :remote], false)
+
+ test "if user is unauthenticated", %{
+ conn: conn,
+ locals: [post_id, reply_id],
+ remote: remote_reply_id
+ } do
+ res_conn = get(conn, "/api/v1/statuses/#{post_id}/context")
+
+ %{"ancestors" => [], "descendants" => descendants} =
+ json_response_and_validate_schema(res_conn, 200)
+
+ descendant_ids =
+ descendants
+ |> Enum.map(& &1["id"])
+
+ assert reply_id not in descendant_ids
+ assert remote_reply_id in descendant_ids
+ end
+
+ test "if user is unauthenticated reply", %{
+ conn: conn,
+ locals: [post_id, reply_id],
+ remote: remote_reply_id
+ } do
+ res_conn = get(conn, "/api/v1/statuses/#{reply_id}/context")
+
+ %{"ancestors" => ancestors, "descendants" => descendants} =
+ json_response_and_validate_schema(res_conn, 200)
+
+ ancestor_ids =
+ ancestors
+ |> Enum.map(& &1["id"])
+
+ descendant_ids =
+ descendants
+ |> Enum.map(& &1["id"])
+
+ assert post_id not in ancestor_ids
+ assert remote_reply_id in descendant_ids
+ end
+
+ test "if user is authenticated", %{locals: [post_id, reply_id], remote: remote_reply_id} do
+ %{conn: conn} = oauth_access(["read"])
+ res_conn = get(conn, "/api/v1/statuses/#{post_id}/context")
+
+ %{"ancestors" => [], "descendants" => descendants} =
+ json_response_and_validate_schema(res_conn, 200)
+
+ descendant_ids =
+ descendants
+ |> Enum.map(& &1["id"])
+
+ assert reply_id in descendant_ids
+ assert remote_reply_id in descendant_ids
+ end
+
+ test "if user is authenticated reply", %{locals: [post_id, reply_id], remote: remote_reply_id} do
+ %{conn: conn} = oauth_access(["read"])
+ res_conn = get(conn, "/api/v1/statuses/#{reply_id}/context")
+
+ %{"ancestors" => ancestors, "descendants" => descendants} =
+ json_response_and_validate_schema(res_conn, 200)
+
+ ancestor_ids =
+ ancestors
+ |> Enum.map(& &1["id"])
+
+ descendant_ids =
+ descendants
+ |> Enum.map(& &1["id"])
+
+ assert post_id in ancestor_ids
+ assert remote_reply_id in descendant_ids
+ end
+ end
+
+ describe "getting status contexts restricted unauthenticated for remote" do
+ setup do: local_and_remote_context_activities()
+
+ setup do: clear_config([:restrict_unauthenticated, :activities, :local], false)
+
+ setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true)
+
+ test "if user is unauthenticated", %{
+ conn: conn,
+ locals: [post_id, reply_id],
+ remote: remote_reply_id
+ } do
+ res_conn = get(conn, "/api/v1/statuses/#{post_id}/context")
+
+ %{"ancestors" => [], "descendants" => descendants} =
+ json_response_and_validate_schema(res_conn, 200)
+
+ descendant_ids =
+ descendants
+ |> Enum.map(& &1["id"])
+
+ assert reply_id in descendant_ids
+ assert remote_reply_id not in descendant_ids
+ end
+
+ test "if user is unauthenticated reply", %{
+ conn: conn,
+ locals: [post_id, reply_id],
+ remote: remote_reply_id
+ } do
+ res_conn = get(conn, "/api/v1/statuses/#{reply_id}/context")
+
+ %{"ancestors" => ancestors, "descendants" => descendants} =
+ json_response_and_validate_schema(res_conn, 200)
+
+ ancestor_ids =
+ ancestors
+ |> Enum.map(& &1["id"])
+
+ descendant_ids =
+ descendants
+ |> Enum.map(& &1["id"])
+
+ assert post_id in ancestor_ids
+ assert remote_reply_id not in descendant_ids
+ end
+
+ test "if user is authenticated", %{locals: [post_id, reply_id], remote: remote_reply_id} do
+ %{conn: conn} = oauth_access(["read"])
+ res_conn = get(conn, "/api/v1/statuses/#{post_id}/context")
+
+ %{"ancestors" => [], "descendants" => descendants} =
+ json_response_and_validate_schema(res_conn, 200)
+
+ reply_ids =
+ descendants
+ |> Enum.map(& &1["id"])
+
+ assert reply_id in reply_ids
+ assert remote_reply_id in reply_ids
+ end
+
+ test "if user is authenticated reply", %{locals: [post_id, reply_id], remote: remote_reply_id} do
+ %{conn: conn} = oauth_access(["read"])
+ res_conn = get(conn, "/api/v1/statuses/#{reply_id}/context")
+
+ %{"ancestors" => ancestors, "descendants" => descendants} =
+ json_response_and_validate_schema(res_conn, 200)
+
+ ancestor_ids =
+ ancestors
+ |> Enum.map(& &1["id"])
+
+ descendant_ids =
+ descendants
+ |> Enum.map(& &1["id"])
+
+ assert post_id in ancestor_ids
+ assert remote_reply_id in descendant_ids
+ end
+ end
+
describe "deleting a status" do
test "when you created it" do
%{user: author, conn: conn} = oauth_access(["write:statuses"])