From 3cd201c365e10cb54466a3b8827c5c38f8ba651e Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Sat, 31 Oct 2026 15:53:41 +0400 Subject: [PATCH] fix(activitypub): refetch a stale Move target once A Move usually follows right after the alias was added, and the cached remote target may not have it yet. Refetch the target when the alias is missing instead of rejecting the Move. Same issue as Akkoma f18e2ba42. --- changelog.d/move-refetch-target.fix | 1 + .../web/activity_pub/transmogrifier.ex | 18 ++++++- .../web/activity_pub/transmogrifier_test.exs | 47 +++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 changelog.d/move-refetch-target.fix diff --git a/changelog.d/move-refetch-target.fix b/changelog.d/move-refetch-target.fix new file mode 100644 index 000000000..1c200568e --- /dev/null +++ b/changelog.d/move-refetch-target.fix @@ -0,0 +1 @@ +Accept a Move when the cached remote target does not list the alias yet, by refetching the target once diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 33ab62ca3..0d4b15288 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -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 diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs index bedb466a4..0e4a4c2db 100644 --- a/test/pleroma/web/activity_pub/transmogrifier_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -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")