Merge branch 'phnt/mastoapi-misattribution-3381' into release/2.10-sec

This commit is contained in:
Lain Soykaf 2025-12-29 09:47:54 +04:00
commit 6c73ebe484
7 changed files with 651 additions and 44 deletions

View file

@ -0,0 +1 @@
MastodonAPI: Fix misattribution of statuses when fetched via non-Announce Activity ID

View file

@ -0,0 +1 @@
Fix bypass of the restrict unauthenticated setting by requesting local Activities

View file

@ -73,6 +73,22 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|> Pleroma.List.member?(user) |> Pleroma.List.member?(user)
end end
def visible_for_user?(%Activity{object: %Object{} = object} = activity, nil) do
activity_visibility? = restrict_unauthenticated_access?(activity)
activity_public? = public?(activity) and not local_public?(activity)
object_visibility? = restrict_unauthenticated_access?(object)
object_public? = public?(object) and not local_public?(object)
# Activity could be local, but object might not (Announce/Like)
cond do
activity_visibility? or object_visibility? ->
false
true ->
activity_public? and object_public?
end
end
def visible_for_user?(%{__struct__: module} = message, nil) def visible_for_user?(%{__struct__: module} = message, nil)
when module in [Activity, Object] do when module in [Activity, Object] do
if restrict_unauthenticated_access?(message), if restrict_unauthenticated_access?(message),

View file

@ -240,7 +240,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
object = Object.normalize(activity, fetch: false) object = Object.normalize(activity, fetch: false)
user = CommonAPI.get_user(activity.data["actor"]) user = CommonAPI.get_user(object.data["actor"])
user_follower_address = user.follower_address user_follower_address = user.follower_address
like_count = object.data["like_count"] || 0 like_count = object.data["like_count"] || 0

View file

@ -261,23 +261,27 @@ defmodule Pleroma.ActivityTest do
test "add_by_params_query/3" do test "add_by_params_query/3" do
user = insert(:user) user = insert(:user)
note = insert(:note_activity, user: user) note_activity = insert(:note_activity, user: user)
insert(:add_activity, user: user, note: note) insert(:add_activity, user: user, note_activity: note_activity)
insert(:add_activity, user: user, note: note) insert(:add_activity, user: user, note_activity: note_activity)
insert(:add_activity, user: user) insert(:add_activity, user: user)
assert Repo.aggregate(Activity, :count, :id) == 4 assert Repo.aggregate(Activity, :count, :id) == 5
add_query = add_query =
Activity.add_by_params_query(note.data["object"], user.ap_id, user.featured_address) Activity.add_by_params_query(
note_activity.data["object"],
user.ap_id,
user.featured_address
)
assert Repo.aggregate(add_query, :count, :id) == 2 assert Repo.aggregate(add_query, :count, :id) == 2
Repo.delete_all(add_query) Repo.delete_all(add_query)
assert Repo.aggregate(add_query, :count, :id) == 0 assert Repo.aggregate(add_query, :count, :id) == 0
assert Repo.aggregate(Activity, :count, :id) == 2 assert Repo.aggregate(Activity, :count, :id) == 3
end end
describe "associated_object_id() sql function" do describe "associated_object_id() sql function" do

View file

@ -827,9 +827,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
end end
defp local_and_remote_activities do defp local_and_remote_activities do
remote_user = insert(:user, local: false, domain: "example.com")
announce_user = insert(:user)
local = insert(:note_activity) local = insert(:note_activity)
remote = insert(:note_activity, local: false) remote = insert(:note_activity, local: false, object_local: false, user: remote_user)
{:ok, local: local, remote: remote} remote_note = insert(:note_activity, local: false, object_local: false)
local_activity_remote_object =
insert(:announce_activity, note_activity: remote_note, user: announce_user)
{:ok,
local: local, remote: remote, local_activity_remote_object: local_activity_remote_object}
end end
defp local_and_remote_context_activities do defp local_and_remote_context_activities do
@ -872,7 +880,64 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
{:ok, job} = Pleroma.Web.Federator.incoming_ap_doc(params) {:ok, job} = Pleroma.Web.Federator.incoming_ap_doc(params)
{:ok, remote_activity} = ObanHelpers.perform(job) {:ok, remote_activity} = ObanHelpers.perform(job)
%{locals: [id1, id2], remote: remote_activity.id, context: context} {:ok, %{id: local_to_local_react_id}} = CommonAPI.react_with_emoji(id1, local_user_2, "🦊")
{:ok, %{id: local_to_remote_react_id}} =
CommonAPI.react_with_emoji(remote_activity.id, local_user_1, "🦊")
{:ok, %{id: remote_to_local_react_id}} = CommonAPI.react_with_emoji(id1, remote_user, "🦊")
{:ok, %{id: remote_to_remote_react_id}} =
CommonAPI.react_with_emoji(remote_activity.id, remote_user, "🦊")
%{
locals: [id1, id2],
remote: remote_activity.id,
local_interactions: [local_to_local_react_id, local_to_remote_react_id],
remote_interactions: [remote_to_local_react_id, remote_to_remote_react_id],
context: context
}
end
defp extract_activity_ids_from_response(list) when is_list(list) do
list
|> Enum.map(& &1["id"])
end
defp all_ids_included?(checked, authority) when is_list(checked) and is_list(authority) do
set1 = MapSet.new(checked)
set2 = MapSet.new(authority)
MapSet.equal?(set1, set2)
end
defp local_interactions_to_remote do
interacted_user = insert(:user, local: false, domain: "example.com")
interacting_user = insert(:user)
announced_post =
insert(:note_activity, local: false, object_local: false, user: interacted_user)
emoji_reacted_post =
insert(:note_activity, local: false, object_local: false, user: interacted_user)
favorited_post =
insert(:note_activity, local: false, object_local: false, user: interacted_user)
announce = insert(:announce_activity, note_activity: announced_post, user: interacting_user)
emoji_react =
insert(:emoji_react_activity, note_activity: emoji_reacted_post, user: interacting_user)
{:ok, favorite} = CommonAPI.favorite(favorited_post.id, interacting_user)
{
:ok,
announce: announce,
emoji_react: emoji_react,
favorite: favorite,
interacted: interacted_user,
interacter: interacting_user
}
end end
describe "status with restrict unauthenticated activities for local and remote" do describe "status with restrict unauthenticated activities for local and remote" do
@ -882,7 +947,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true) setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do test "if user is unauthenticated", %{
conn: conn,
local: local,
remote: remote,
local_activity_remote_object: local_activity_remote_object
} do
res_conn = get(conn, "/api/v1/statuses/#{local.id}") res_conn = get(conn, "/api/v1/statuses/#{local.id}")
assert json_response_and_validate_schema(res_conn, :not_found) == %{ assert json_response_and_validate_schema(res_conn, :not_found) == %{
@ -891,18 +961,31 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
res_conn = get(conn, "/api/v1/statuses/#{remote.id}") res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
assert json_response_and_validate_schema(res_conn, :not_found) == %{
"error" => "Record not found"
}
res_conn = get(conn, "/api/v1/statuses/#{local_activity_remote_object.id}")
assert json_response_and_validate_schema(res_conn, :not_found) == %{ assert json_response_and_validate_schema(res_conn, :not_found) == %{
"error" => "Record not found" "error" => "Record not found"
} }
end end
test "if user is authenticated", %{local: local, remote: remote} do test "if user is authenticated", %{
local: local,
remote: remote,
local_activity_remote_object: local_activity_remote_object
} do
%{conn: conn} = oauth_access(["read"]) %{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/statuses/#{local.id}") res_conn = get(conn, "/api/v1/statuses/#{local.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
res_conn = get(conn, "/api/v1/statuses/#{remote.id}") res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
res_conn = get(conn, "/api/v1/statuses/#{local_activity_remote_object.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
end end
end end
@ -911,9 +994,20 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
setup do: clear_config([:restrict_unauthenticated, :activities, :local], true) setup do: clear_config([:restrict_unauthenticated, :activities, :local], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do test "if user is unauthenticated", %{
conn: conn,
local: local,
remote: remote,
local_activity_remote_object: local_activity_remote_object
} do
res_conn = get(conn, "/api/v1/statuses/#{local.id}") res_conn = get(conn, "/api/v1/statuses/#{local.id}")
assert json_response_and_validate_schema(res_conn, :not_found) == %{
"error" => "Record not found"
}
res_conn = get(conn, "/api/v1/statuses/#{local_activity_remote_object.id}")
assert json_response_and_validate_schema(res_conn, :not_found) == %{ assert json_response_and_validate_schema(res_conn, :not_found) == %{
"error" => "Record not found" "error" => "Record not found"
} }
@ -922,13 +1016,20 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
end end
test "if user is authenticated", %{local: local, remote: remote} do test "if user is authenticated", %{
local: local,
remote: remote,
local_activity_remote_object: local_activity_remote_object
} do
%{conn: conn} = oauth_access(["read"]) %{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/statuses/#{local.id}") res_conn = get(conn, "/api/v1/statuses/#{local.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
res_conn = get(conn, "/api/v1/statuses/#{remote.id}") res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
res_conn = get(conn, "/api/v1/statuses/#{local_activity_remote_object.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
end end
end end
@ -937,24 +1038,177 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true) setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do test "if user is unauthenticated", %{
conn: conn,
local: local,
remote: remote,
local_activity_remote_object: local_activity_remote_object
} do
res_conn = get(conn, "/api/v1/statuses/#{local.id}") res_conn = get(conn, "/api/v1/statuses/#{local.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
res_conn = get(conn, "/api/v1/statuses/#{remote.id}") res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
assert json_response_and_validate_schema(res_conn, :not_found) == %{
"error" => "Record not found"
}
res_conn = get(conn, "/api/v1/statuses/#{local_activity_remote_object.id}")
assert json_response_and_validate_schema(res_conn, :not_found) == %{ assert json_response_and_validate_schema(res_conn, :not_found) == %{
"error" => "Record not found" "error" => "Record not found"
} }
end end
test "if user is authenticated", %{local: local, remote: remote} do test "if user is authenticated", %{
local: local,
remote: remote,
local_activity_remote_object: local_activity_remote_object
} do
%{conn: conn} = oauth_access(["read"]) %{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/statuses/#{local.id}") res_conn = get(conn, "/api/v1/statuses/#{local.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
res_conn = get(conn, "/api/v1/statuses/#{remote.id}") res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
res_conn = get(conn, "/api/v1/statuses/#{local_activity_remote_object.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
end
end
# Note: Activities of type Flag,Follow,Delete,Accept,Reject,Undo return 404
describe "status has correct attribution when fetching as" do
setup do: local_interactions_to_remote()
test "Add/Remove activity", %{conn: conn, interacter: user} do
add = insert(:add_activity, user: user)
add_id = add.id
remove = insert(:remove_activity, user: user)
remove_id = remove.id
user_id = user.id
# Schema validation fails:
# null value where integer expected at /pleroma/conversation_id
result1 =
conn
|> get("/api/v1/statuses/#{add_id}")
|> json_response(200)
assert match?(%{"id" => ^add_id, "account" => %{"id" => ^user_id}}, result1)
result2 =
conn
|> get("/api/v1/statuses/#{remove_id}")
|> json_response(200)
assert match?(%{"id" => ^remove_id, "account" => %{"id" => ^user_id}}, result2)
end
test "Announce activity", %{
conn: conn,
announce: activity,
interacter: interacter,
interacted: user
} do
announce_id = activity.id
announced_activity = Pleroma.Activity.get_create_by_object_ap_id(activity.data["object"])
announced_id = announced_activity.id
interacter_ap_id = interacter.id
user_id = user.id
result =
conn
|> get("/api/v1/statuses/#{activity.id}")
|> json_response_and_validate_schema(200)
assert match?(
%{
"id" => ^announce_id,
"account" => %{"id" => ^interacter_ap_id},
"reblog" => %{"account" => %{"id" => ^user_id}, "id" => ^announced_id}
},
result
)
end
test "Create activity", %{conn: conn, interacted: user} do
note_activity = insert(:note_activity, local: false, object_local: false, user: user)
note_activity_id = note_activity.id
user_id = user.id
result =
conn
|> get("/api/v1/statuses/#{note_activity_id}")
|> json_response_and_validate_schema(200)
assert match?(%{"id" => ^note_activity_id, "account" => %{"id" => ^user_id}}, result)
end
test "EmojiReact activity", %{conn: conn, emoji_react: activity, interacted: user} do
emoji_react_id = activity.id
user_id = user.id
result =
conn
|> get("/api/v1/statuses/#{emoji_react_id}")
|> json_response_and_validate_schema(200)
assert match?(%{"id" => ^emoji_react_id, "account" => %{"id" => ^user_id}}, result)
end
test "Like activity", %{conn: conn, favorite: activity, interacted: user} do
like_id = activity.id
user_id = user.id
result =
conn
|> get("/api/v1/statuses/#{like_id}")
|> json_response_and_validate_schema(200)
assert match?(%{"id" => ^like_id, "account" => %{"id" => ^user_id}}, result)
end
test "Update activity" do
%{conn: conn, user: user} = oauth_access(["write:statuses"])
user_id = user.id
{:ok, activity} = CommonAPI.post(user, %{status: "This will be edited"})
{:ok, updated_activity} = CommonAPI.update(activity, user, %{status: "edited"})
activity_id = activity.id
updated_activity_id = updated_activity.id
result1 =
conn
|> get("/api/v1/statuses/#{activity_id}")
|> json_response_and_validate_schema(200)
# Even though we ask for the original Create activity, updated post is served
assert match?(
%{
"id" => ^activity_id,
"account" => %{"id" => ^user_id},
"content" => "edited"
},
result1
)
# Schema validation fails:
# null value where integer expected at /pleroma/conversation_id
result2 =
conn
|> get("/api/v1/statuses/#{updated_activity_id}")
|> json_response(200)
assert match?(
%{
"id" => ^updated_activity_id,
"account" => %{"id" => ^user_id},
"content" => "edited"
},
result2
)
end end
end end
@ -1014,18 +1268,35 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true) setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do test "if user is unauthenticated", %{
res_conn = get(conn, "/api/v1/statuses?id[]=#{local.id}&id[]=#{remote.id}") conn: conn,
local: local,
remote: remote,
local_activity_remote_object: local_activity_remote_object
} do
res_conn =
get(
conn,
"/api/v1/statuses?id[]=#{local.id}&id[]=#{remote.id}&id[]=#{local_activity_remote_object.id}"
)
assert json_response_and_validate_schema(res_conn, 200) == [] assert json_response_and_validate_schema(res_conn, 200) == []
end end
test "if user is authenticated", %{local: local, remote: remote} do test "if user is authenticated", %{
local: local,
remote: remote,
local_activity_remote_object: local_activity_remote_object
} do
%{conn: conn} = oauth_access(["read"]) %{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/statuses?id[]=#{local.id}&id[]=#{remote.id}") res_conn =
get(
conn,
"/api/v1/statuses?id[]=#{local.id}&id[]=#{remote.id}&id[]=#{local_activity_remote_object.id}"
)
assert length(json_response_and_validate_schema(res_conn, 200)) == 2 assert length(json_response_and_validate_schema(res_conn, 200)) == 3
end end
end end
@ -1034,19 +1305,36 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
setup do: clear_config([:restrict_unauthenticated, :activities, :local], true) setup do: clear_config([:restrict_unauthenticated, :activities, :local], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do test "if user is unauthenticated", %{
res_conn = get(conn, "/api/v1/statuses?id[]=#{local.id}&id[]=#{remote.id}") conn: conn,
local: local,
remote: remote,
local_activity_remote_object: local_activity_remote_object
} do
res_conn =
get(
conn,
"/api/v1/statuses?id[]=#{local.id}&id[]=#{remote.id}&id[]=#{local_activity_remote_object.id}"
)
remote_id = remote.id remote_id = remote.id
assert [%{"id" => ^remote_id}] = json_response_and_validate_schema(res_conn, 200) assert [%{"id" => ^remote_id}] = json_response_and_validate_schema(res_conn, 200)
end end
test "if user is authenticated", %{local: local, remote: remote} do test "if user is authenticated", %{
local: local,
remote: remote,
local_activity_remote_object: local_activity_remote_object
} do
%{conn: conn} = oauth_access(["read"]) %{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/statuses?id[]=#{local.id}&id[]=#{remote.id}") res_conn =
get(
conn,
"/api/v1/statuses?id[]=#{local.id}&id[]=#{remote.id}&id[]=#{local_activity_remote_object.id}"
)
assert length(json_response_and_validate_schema(res_conn, 200)) == 2 assert length(json_response_and_validate_schema(res_conn, 200)) == 3
end end
end end
@ -1055,22 +1343,40 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true) setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do test "if user is unauthenticated", %{
res_conn = get(conn, "/api/v1/statuses?id[]=#{local.id}&id[]=#{remote.id}") conn: conn,
local: local,
remote: remote,
local_activity_remote_object: local_activity_remote_object
} do
res_conn =
get(
conn,
"/api/v1/statuses?id[]=#{local.id}&id[]=#{remote.id}&id[]=#{local_activity_remote_object.id}"
)
local_id = local.id local_id = local.id
assert [%{"id" => ^local_id}] = json_response_and_validate_schema(res_conn, 200) assert [%{"id" => ^local_id}] = json_response_and_validate_schema(res_conn, 200)
end end
test "if user is authenticated", %{local: local, remote: remote} do test "if user is authenticated", %{
local: local,
remote: remote,
local_activity_remote_object: local_activity_remote_object
} do
%{conn: conn} = oauth_access(["read"]) %{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/statuses?id[]=#{local.id}&id[]=#{remote.id}") res_conn =
get(
conn,
"/api/v1/statuses?id[]=#{local.id}&id[]=#{remote.id}&id[]=#{local_activity_remote_object.id}"
)
assert length(json_response_and_validate_schema(res_conn, 200)) == 2 assert length(json_response_and_validate_schema(res_conn, 200)) == 3
end end
end end
# Note: Context route on EmojiReact/Announce activities puts everything into the ancestors field
describe "getting status contexts restricted unauthenticated for local and remote" do describe "getting status contexts restricted unauthenticated for local and remote" do
setup do: local_and_remote_context_activities() setup do: local_and_remote_context_activities()
@ -1096,6 +1402,40 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
} }
end end
test "if user is unauthenticated Activity interactions", %{
conn: conn,
local_interactions: [local_to_local, local_to_remote],
remote_interactions: [remote_to_local, remote_to_remote]
} do
res_conn = get(conn, "/api/v1/statuses/#{local_to_local}/context")
assert json_response_and_validate_schema(res_conn, 200) == %{
"ancestors" => [],
"descendants" => []
}
res_conn = get(conn, "/api/v1/statuses/#{local_to_remote}/context")
assert json_response_and_validate_schema(res_conn, 200) == %{
"ancestors" => [],
"descendants" => []
}
res_conn = get(conn, "/api/v1/statuses/#{remote_to_local}/context")
assert json_response_and_validate_schema(res_conn, 200) == %{
"ancestors" => [],
"descendants" => []
}
res_conn = get(conn, "/api/v1/statuses/#{remote_to_remote}/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 test "if user is authenticated", %{locals: [post_id, reply_id], remote: remote_reply_id} do
%{conn: conn} = oauth_access(["read"]) %{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/statuses/#{post_id}/context") res_conn = get(conn, "/api/v1/statuses/#{post_id}/context")
@ -1129,6 +1469,47 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
assert post_id in ancestor_ids assert post_id in ancestor_ids
assert remote_reply_id in descendant_ids assert remote_reply_id in descendant_ids
end end
test "if user is authenticated Activity interactions", %{
locals: [post_id, reply_id],
remote: remote_reply_id,
local_interactions: [local_to_local, local_to_remote],
remote_interactions: [remote_to_local, remote_to_remote]
} do
%{conn: conn} = oauth_access(["read"])
all_ids = [post_id, reply_id, remote_reply_id]
res_conn = get(conn, "/api/v1/statuses/#{local_to_local}/context")
%{"ancestors" => ancestors1, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids1 = extract_activity_ids_from_response(ancestors1)
assert all_ids_included?(ancestor_ids1, all_ids)
res_conn = get(conn, "/api/v1/statuses/#{local_to_remote}/context")
%{"ancestors" => ancestors2, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids2 = extract_activity_ids_from_response(ancestors2)
assert all_ids_included?(ancestor_ids2, all_ids)
res_conn = get(conn, "/api/v1/statuses/#{remote_to_local}/context")
%{"ancestors" => ancestors3, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids3 = extract_activity_ids_from_response(ancestors3)
assert all_ids_included?(ancestor_ids3, all_ids)
res_conn = get(conn, "/api/v1/statuses/#{remote_to_remote}/context")
%{"ancestors" => ancestors4, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids4 = extract_activity_ids_from_response(ancestors4)
assert all_ids_included?(ancestor_ids4, all_ids)
end
end end
describe "getting status contexts restricted unauthenticated for local" do describe "getting status contexts restricted unauthenticated for local" do
@ -1178,6 +1559,45 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
assert remote_reply_id in descendant_ids assert remote_reply_id in descendant_ids
end end
test "if user is unauthenticated Activity interactions", %{
conn: conn,
remote: remote_reply_id,
local_interactions: [local_to_local, local_to_remote],
remote_interactions: [remote_to_local, remote_to_remote]
} do
res_conn = get(conn, "/api/v1/statuses/#{local_to_local}/context")
%{"ancestors" => ancestors1, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids1 = extract_activity_ids_from_response(ancestors1)
assert all_ids_included?(ancestor_ids1, [remote_reply_id])
res_conn = get(conn, "/api/v1/statuses/#{local_to_remote}/context")
%{"ancestors" => ancestors2, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids2 = extract_activity_ids_from_response(ancestors2)
assert all_ids_included?(ancestor_ids2, [remote_reply_id])
res_conn = get(conn, "/api/v1/statuses/#{remote_to_local}/context")
%{"ancestors" => ancestors3, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids3 = extract_activity_ids_from_response(ancestors3)
assert all_ids_included?(ancestor_ids3, [remote_reply_id])
res_conn = get(conn, "/api/v1/statuses/#{remote_to_remote}/context")
%{"ancestors" => ancestors4, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids4 = extract_activity_ids_from_response(ancestors4)
assert all_ids_included?(ancestor_ids4, [remote_reply_id])
end
test "if user is authenticated", %{locals: [post_id, reply_id], remote: remote_reply_id} do test "if user is authenticated", %{locals: [post_id, reply_id], remote: remote_reply_id} do
%{conn: conn} = oauth_access(["read"]) %{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/statuses/#{post_id}/context") res_conn = get(conn, "/api/v1/statuses/#{post_id}/context")
@ -1211,6 +1631,47 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
assert post_id in ancestor_ids assert post_id in ancestor_ids
assert remote_reply_id in descendant_ids assert remote_reply_id in descendant_ids
end end
test "if user is authenticated Activity interactions", %{
locals: [post_id, reply_id],
remote: remote_reply_id,
local_interactions: [local_to_local, local_to_remote],
remote_interactions: [remote_to_local, remote_to_remote]
} do
%{conn: conn} = oauth_access(["read"])
all_ids = [post_id, reply_id, remote_reply_id]
res_conn = get(conn, "/api/v1/statuses/#{local_to_local}/context")
%{"ancestors" => ancestors1, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids1 = extract_activity_ids_from_response(ancestors1)
assert all_ids_included?(ancestor_ids1, all_ids)
res_conn = get(conn, "/api/v1/statuses/#{local_to_remote}/context")
%{"ancestors" => ancestors2, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids2 = extract_activity_ids_from_response(ancestors2)
assert all_ids_included?(ancestor_ids2, all_ids)
res_conn = get(conn, "/api/v1/statuses/#{remote_to_local}/context")
%{"ancestors" => ancestors3, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids3 = extract_activity_ids_from_response(ancestors3)
assert all_ids_included?(ancestor_ids3, all_ids)
res_conn = get(conn, "/api/v1/statuses/#{remote_to_remote}/context")
%{"ancestors" => ancestors4, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids4 = extract_activity_ids_from_response(ancestors4)
assert all_ids_included?(ancestor_ids4, all_ids)
end
end end
describe "getting status contexts restricted unauthenticated for remote" do describe "getting status contexts restricted unauthenticated for remote" do
@ -1260,6 +1721,46 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
assert remote_reply_id not in descendant_ids assert remote_reply_id not in descendant_ids
end end
test "if user is unauthenticated Activity interactions", %{
conn: conn,
locals: [post_id, reply_id],
local_interactions: [local_to_local, local_to_remote],
remote_interactions: [remote_to_local, remote_to_remote]
} do
res_conn = get(conn, "/api/v1/statuses/#{local_to_local}/context")
all_ids = [post_id, reply_id]
%{"ancestors" => ancestors1, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids1 = extract_activity_ids_from_response(ancestors1)
assert all_ids_included?(ancestor_ids1, all_ids)
res_conn = get(conn, "/api/v1/statuses/#{local_to_remote}/context")
%{"ancestors" => ancestors2, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids2 = extract_activity_ids_from_response(ancestors2)
assert all_ids_included?(ancestor_ids2, all_ids)
res_conn = get(conn, "/api/v1/statuses/#{remote_to_local}/context")
%{"ancestors" => ancestors3, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids3 = extract_activity_ids_from_response(ancestors3)
assert all_ids_included?(ancestor_ids3, all_ids)
res_conn = get(conn, "/api/v1/statuses/#{remote_to_remote}/context")
%{"ancestors" => ancestors4, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids4 = extract_activity_ids_from_response(ancestors4)
assert all_ids_included?(ancestor_ids4, all_ids)
end
test "if user is authenticated", %{locals: [post_id, reply_id], remote: remote_reply_id} do test "if user is authenticated", %{locals: [post_id, reply_id], remote: remote_reply_id} do
%{conn: conn} = oauth_access(["read"]) %{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/statuses/#{post_id}/context") res_conn = get(conn, "/api/v1/statuses/#{post_id}/context")
@ -1293,6 +1794,47 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
assert post_id in ancestor_ids assert post_id in ancestor_ids
assert remote_reply_id in descendant_ids assert remote_reply_id in descendant_ids
end end
test "if user is authenticated Activity interactions", %{
locals: [post_id, reply_id],
remote: remote_reply_id,
local_interactions: [local_to_local, local_to_remote],
remote_interactions: [remote_to_local, remote_to_remote]
} do
%{conn: conn} = oauth_access(["read"])
all_ids = [post_id, reply_id, remote_reply_id]
res_conn = get(conn, "/api/v1/statuses/#{local_to_local}/context")
%{"ancestors" => ancestors1, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids1 = extract_activity_ids_from_response(ancestors1)
assert all_ids_included?(ancestor_ids1, all_ids)
res_conn = get(conn, "/api/v1/statuses/#{local_to_remote}/context")
%{"ancestors" => ancestors2, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids2 = extract_activity_ids_from_response(ancestors2)
assert all_ids_included?(ancestor_ids2, all_ids)
res_conn = get(conn, "/api/v1/statuses/#{remote_to_local}/context")
%{"ancestors" => ancestors3, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids3 = extract_activity_ids_from_response(ancestors3)
assert all_ids_included?(ancestor_ids3, all_ids)
res_conn = get(conn, "/api/v1/statuses/#{remote_to_remote}/context")
%{"ancestors" => ancestors4, "descendants" => []} =
json_response_and_validate_schema(res_conn, 200)
ancestor_ids4 = extract_activity_ids_from_response(ancestors4)
assert all_ids_included?(ancestor_ids4, all_ids)
end
end end
describe "deleting a status" do describe "deleting a status" do

View file

@ -102,11 +102,19 @@ defmodule Pleroma.Factory do
user = attrs[:user] || insert(:user) user = attrs[:user] || insert(:user)
object_id =
if attrs[:object_local] == false do
# Must not match our Endpoint URL in the test env
"https://example.com/objects/#{Ecto.UUID.generate()}"
else
Pleroma.Web.ActivityPub.Utils.generate_object_id()
end
data = %{ data = %{
"type" => "Note", "type" => "Note",
"content" => text, "content" => text,
"source" => text, "source" => text,
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(), "id" => object_id,
"actor" => user.ap_id, "actor" => user.ap_id,
"to" => ["https://www.w3.org/ns/activitystreams#Public"], "to" => ["https://www.w3.org/ns/activitystreams#Public"],
"published" => DateTime.utc_now() |> DateTime.to_iso8601(), "published" => DateTime.utc_now() |> DateTime.to_iso8601(),
@ -297,27 +305,27 @@ defmodule Pleroma.Factory do
featured_collection_activity(attrs, "Add") featured_collection_activity(attrs, "Add")
end end
def remove_activity_factor(attrs \\ %{}) do def remove_activity_factory(attrs \\ %{}) do
featured_collection_activity(attrs, "Remove") featured_collection_activity(attrs, "Remove")
end end
defp featured_collection_activity(attrs, type) do defp featured_collection_activity(attrs, type) do
user = attrs[:user] || insert(:user) user = attrs[:user] || insert(:user)
note = attrs[:note] || insert(:note, user: user) note_activity = attrs[:note_activity] || insert(:note_activity, user: user)
data_attrs = data_attrs =
attrs attrs
|> Map.get(:data_attrs, %{}) |> Map.get(:data_attrs, %{})
|> Map.put(:type, type) |> Map.put(:type, type)
attrs = Map.drop(attrs, [:user, :note, :data_attrs]) attrs = Map.drop(attrs, [:user, :note_activity, :data_attrs])
data = data =
%{ %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"target" => user.featured_address, "target" => user.featured_address,
"object" => note.data["object"], "object" => note_activity.data["object"],
"actor" => note.data["actor"], "actor" => note_activity.data["actor"],
"type" => "Add", "type" => "Add",
"to" => [Pleroma.Constants.as_public()], "to" => [Pleroma.Constants.as_public()],
"cc" => [user.follower_address] "cc" => [user.follower_address]
@ -361,14 +369,25 @@ defmodule Pleroma.Factory do
def note_activity_factory(attrs \\ %{}) do def note_activity_factory(attrs \\ %{}) do
user = attrs[:user] || insert(:user) user = attrs[:user] || insert(:user)
note = attrs[:note] || insert(:note, user: user) object_local = if attrs[:object_local] == false, do: false, else: true
note = attrs[:note] || insert(:note, user: user, object_local: object_local)
activity_id =
if attrs[:local] == false do
# Same domain as in note Object factory, it doesn't make sense
# to create mismatched Create Activities with an ID coming from
# a different domain than the Object
"https://example.com/activities/#{Ecto.UUID.generate()}"
else
Pleroma.Web.ActivityPub.Utils.generate_activity_id()
end
data_attrs = attrs[:data_attrs] || %{} data_attrs = attrs[:data_attrs] || %{}
attrs = Map.drop(attrs, [:user, :note, :data_attrs]) attrs = Map.drop(attrs, [:user, :note, :data_attrs, :object_local])
data = data =
%{ %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), "id" => activity_id,
"type" => "Create", "type" => "Create",
"actor" => note.data["actor"], "actor" => note.data["actor"],
"to" => note.data["to"], "to" => note.data["to"],
@ -408,15 +427,17 @@ defmodule Pleroma.Factory do
def announce_activity_factory(attrs \\ %{}) do def announce_activity_factory(attrs \\ %{}) do
note_activity = attrs[:note_activity] || insert(:note_activity) note_activity = attrs[:note_activity] || insert(:note_activity)
object = Object.normalize(note_activity, fetch: false)
user = attrs[:user] || insert(:user) user = attrs[:user] || insert(:user)
data = %{ data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"type" => "Announce", "type" => "Announce",
"actor" => note_activity.actor, "actor" => user.ap_id,
"object" => note_activity.data["id"], "object" => object.data["id"],
"to" => [user.follower_address, note_activity.data["actor"]], "to" => [user.follower_address, object.data["actor"]],
"cc" => ["https://www.w3.org/ns/activitystreams#Public"], "cc" => ["https://www.w3.org/ns/activitystreams#Public"],
"context" => note_activity.data["context"] "context" => object.data["context"]
} }
%Pleroma.Activity{ %Pleroma.Activity{
@ -426,6 +447,28 @@ defmodule Pleroma.Factory do
} }
end end
def emoji_react_activity_factory(attrs \\ %{}) do
note_activity = attrs[:note_activity] || insert(:note_activity)
object = Object.normalize(note_activity, fetch: false)
user = attrs[:user] || insert(:user)
data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"actor" => user.ap_id,
"type" => "EmojiReact",
"object" => object.data["id"],
"to" => [user.follower_address, object.data["actor"]],
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
"published_at" => DateTime.utc_now() |> DateTime.to_iso8601(),
"context" => object.data["context"],
"content" => "😀"
}
%Pleroma.Activity{
data: data
}
end
def like_activity_factory(attrs \\ %{}) do def like_activity_factory(attrs \\ %{}) do
note_activity = attrs[:note_activity] || insert(:note_activity) note_activity = attrs[:note_activity] || insert(:note_activity)
object = Object.normalize(note_activity, fetch: false) object = Object.normalize(note_activity, fetch: false)