Merge pull request 'Federation: refetch a stale Move target once' (#8051) from lambadalambda/pleroma:fix/move-refetch-target into develop

Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/8051
This commit is contained in:
lain 2026-11-01 10:56:52 +00:00
commit 356daab6af
3 changed files with 65 additions and 1 deletions

View file

@ -0,0 +1 @@
Accept a Move when the cached remote target does not list the alias yet, by refetching the target once

View file

@ -799,7 +799,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
) do
with %User{} = origin_user <- User.get_cached_by_ap_id(origin_actor),
{:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_actor),
true <- origin_actor in target_user.also_known_as do
{:ok, %User{} = target_user} <- refetch_unless_aliased(target_user, origin_actor) do
ActivityPub.move(origin_user, target_user, false)
else
_e -> :error
@ -1204,4 +1204,20 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def maybe_fix_user_url(data), do: data
def maybe_fix_user_object(data), do: maybe_fix_user_url(data)
# A Move usually follows right after the alias was added, which the cached
# remote target may not have yet.
defp refetch_unless_aliased(%User{} = target, origin_ap_id, refetch? \\ true) do
cond do
origin_ap_id in target.also_known_as ->
{:ok, target}
refetch? and not target.local ->
with {:ok, %User{} = target} <- User.fetch_by_ap_id(target.ap_id),
do: refetch_unless_aliased(target, origin_ap_id, false)
true ->
:error
end
end
end

View file

@ -147,6 +147,53 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert activity.data["type"] == "Move"
end
test "it refetches a cached remote Move target that lacks the alias" do
old_user = insert(:user, local: false, ap_id: "https://old.example/users/old")
new_user =
insert(:user,
local: false,
ap_id: "https://new.example/users/new",
nickname: "new@new.example",
last_refreshed_at: NaiveDateTime.utc_now()
)
actor = %{
"@context" => "https://www.w3.org/ns/activitystreams",
"id" => new_user.ap_id,
"type" => "Person",
"preferredUsername" => "new",
"inbox" => new_user.ap_id <> "/inbox",
"outbox" => new_user.ap_id <> "/outbox",
"followers" => new_user.ap_id <> "/followers",
"following" => new_user.ap_id <> "/following",
"alsoKnownAs" => [old_user.ap_id]
}
Tesla.Mock.mock(fn
%{method: :get, url: "https://new.example/users/new"} ->
%Tesla.Env{
status: 200,
headers: [{"content-type", "application/activity+json"}],
body: Jason.encode!(actor)
}
_ ->
%Tesla.Env{status: 404}
end)
message = %{
"@context" => "https://www.w3.org/ns/activitystreams",
"type" => "Move",
"actor" => old_user.ap_id,
"object" => old_user.ap_id,
"target" => new_user.ap_id
}
assert {:ok, %Activity{data: %{"type" => "Move"}}} = Transmogrifier.handle_incoming(message)
assert old_user.ap_id in User.get_cached_by_ap_id(new_user.ap_id).also_known_as
end
test "it fixes both the Create and object contexts in a reply" do
insert(:user, ap_id: "https://mk.absturztau.be/users/8ozbzjs3o8")
insert(:user, ap_id: "https://p.helene.moe/users/helene")