From e56779dd8d1668177afa199aaa836bea70e68420 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Thu, 10 Sep 2020 11:09:11 +0200 Subject: [PATCH 01/13] Transmogrifier: Simplify fix_explicit_addressing and fix_implicit_addressing --- .../web/activity_pub/transmogrifier.ex | 51 ++++++------------- .../web/activity_pub/transmogrifier_test.exs | 6 +-- 2 files changed, 19 insertions(+), 38 deletions(-) diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 4070ed14d..047f23918 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -72,17 +72,21 @@ def fix_addressing_list(map, field) do end end - def fix_explicit_addressing( - %{"to" => to, "cc" => cc} = object, - explicit_mentions, - follower_collection - ) do - explicit_to = Enum.filter(to, fn x -> x in explicit_mentions end) + # if directMessage flag is set to true, leave the addressing alone + def fix_explicit_addressing(%{"directMessage" => true} = object, _follower_collection), + do: object + def fix_explicit_addressing(%{"to" => to, "cc" => cc} = object, follower_collection) do + explicit_mentions = + Utils.determine_explicit_mentions(object) ++ + [Pleroma.Constants.as_public(), follower_collection] + + explicit_to = Enum.filter(to, fn x -> x in explicit_mentions end) explicit_cc = Enum.filter(to, fn x -> x not in explicit_mentions end) final_cc = (cc ++ explicit_cc) + |> Enum.filter(& &1) |> Enum.reject(fn x -> String.ends_with?(x, "/followers") and x != follower_collection end) |> Enum.uniq() @@ -91,29 +95,6 @@ def fix_explicit_addressing( |> Map.put("cc", final_cc) end - def fix_explicit_addressing(object, _explicit_mentions, _followers_collection), do: object - - # if directMessage flag is set to true, leave the addressing alone - def fix_explicit_addressing(%{"directMessage" => true} = object), do: object - - def fix_explicit_addressing(object) do - explicit_mentions = Utils.determine_explicit_mentions(object) - - %User{follower_address: follower_collection} = - object - |> Containment.get_actor() - |> User.get_cached_by_ap_id() - - explicit_mentions = - explicit_mentions ++ - [ - Pleroma.Constants.as_public(), - follower_collection - ] - - fix_explicit_addressing(object, explicit_mentions, follower_collection) - end - # if as:Public is addressed, then make sure the followers collection is also addressed # so that the activities will be delivered to local users. def fix_implicit_addressing(%{"to" => to, "cc" => cc} = object, followers_collection) do @@ -137,19 +118,19 @@ def fix_implicit_addressing(%{"to" => to, "cc" => cc} = object, followers_collec end end - def fix_implicit_addressing(object, _), do: object - def fix_addressing(object) do - {:ok, %User{} = user} = User.get_or_fetch_by_ap_id(object["actor"]) - followers_collection = User.ap_followers(user) + {:ok, %User{follower_address: follower_collection}} = + object + |> Containment.get_actor() + |> User.get_or_fetch_by_ap_id() object |> fix_addressing_list("to") |> fix_addressing_list("cc") |> fix_addressing_list("bto") |> fix_addressing_list("bcc") - |> fix_explicit_addressing() - |> fix_implicit_addressing(followers_collection) + |> fix_explicit_addressing(follower_collection) + |> fix_implicit_addressing(follower_collection) end def fix_actor(%{"attributedTo" => actor} = object) do diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs index 4c3fcb44a..bb0b58e4d 100644 --- a/test/pleroma/web/activity_pub/transmogrifier_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -446,7 +446,7 @@ test "moves non-explicitly mentioned actors to cc", %{user: user} do end) } - fixed_object = Transmogrifier.fix_explicit_addressing(object) + fixed_object = Transmogrifier.fix_explicit_addressing(object, user.follower_address) assert Enum.all?(explicitly_mentioned_actors, &(&1 in fixed_object["to"])) refute "https://social.beepboop.ga/users/dirb" in fixed_object["to"] assert "https://social.beepboop.ga/users/dirb" in fixed_object["cc"] @@ -459,7 +459,7 @@ test "does not move actor's follower collection to cc", %{user: user} do "cc" => [] } - fixed_object = Transmogrifier.fix_explicit_addressing(object) + fixed_object = Transmogrifier.fix_explicit_addressing(object, user.follower_address) assert user.follower_address in fixed_object["to"] refute user.follower_address in fixed_object["cc"] end @@ -473,7 +473,7 @@ test "removes recipient's follower collection from cc", %{user: user} do "cc" => [user.follower_address, recipient.follower_address] } - fixed_object = Transmogrifier.fix_explicit_addressing(object) + fixed_object = Transmogrifier.fix_explicit_addressing(object, user.follower_address) assert user.follower_address in fixed_object["cc"] refute recipient.follower_address in fixed_object["cc"] From e2a3365b5ce86293a5fed28c06b2e7d9dd97c9d1 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Thu, 10 Sep 2020 11:08:05 +0200 Subject: [PATCH 02/13] ObjectValidator.CommonFixes: Introduce fix_objects_defaults and fix_activity_defaults --- .../object_validators/recipients.ex | 22 +++++++++------ .../article_note_validator.ex | 3 +- .../audio_video_validator.ex | 3 +- .../object_validators/common_fixes.ex | 28 +++++++++++++++---- .../create_generic_validator.ex | 12 +------- .../object_validators/event_validator.ex | 4 +-- .../object_validators/question_validator.ex | 4 +-- .../object_validators/recipients_test.exs | 2 +- .../transmogrifier/audio_handling_test.exs | 6 +++- .../transmogrifier/event_handling_test.exs | 2 +- 10 files changed, 50 insertions(+), 36 deletions(-) diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex index af4b0e527..b76547e75 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex @@ -15,19 +15,23 @@ def cast(object) when is_binary(object) do def cast(data) when is_list(data) do data - |> Enum.reduce_while({:ok, []}, fn element, {:ok, list} -> - case ObjectID.cast(element) do - {:ok, id} -> - {:cont, {:ok, [id | list]}} + |> Enum.reduce_while({:ok, []}, fn + nil, {:ok, list} -> + {:cont, {:ok, list}} - _ -> - {:halt, :error} - end + element, {:ok, list} -> + case ObjectID.cast(element) do + {:ok, id} -> + {:cont, {:ok, [id | list]}} + + _ -> + {:halt, {:error, element}} + end end) end - def cast(_) do - :error + def cast(data) do + {:error, data} end def dump(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex b/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex index 39ef6dc29..d2026b5ea 100644 --- a/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex @@ -79,9 +79,8 @@ defp fix_url(data), do: data defp fix(data) do data - |> CommonFixes.fix_defaults() - |> CommonFixes.fix_attribution() |> CommonFixes.fix_actor() + |> CommonFixes.fix_object_defaults() |> fix_url() |> Transmogrifier.fix_emoji() end diff --git a/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex b/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex index 8a5a60526..8ee432947 100644 --- a/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex @@ -120,9 +120,8 @@ defp fix_content(data), do: data defp fix(data) do data - |> CommonFixes.fix_defaults() - |> CommonFixes.fix_attribution() |> CommonFixes.fix_actor() + |> CommonFixes.fix_object_defaults() |> Transmogrifier.fix_emoji() |> fix_url() |> fix_content() diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex index 5f2c633bc..950eb1494 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -3,26 +3,44 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do + alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Object.Containment + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils - # based on Pleroma.Web.ActivityPub.Utils.lazy_put_objects_defaults - def fix_defaults(data) do + def fix_object_defaults(data) do %{data: %{"id" => context}, id: context_id} = Utils.create_context(data["context"] || data["conversation"]) + %User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["attributedTo"]) + {:ok, to} = ObjectValidators.Recipients.cast(data["to"] || []) + {:ok, cc} = ObjectValidators.Recipients.cast(data["cc"] || []) + data |> Map.put("context", context) |> Map.put("context_id", context_id) + |> Map.put("to", to) + |> Map.put("cc", cc) + |> Transmogrifier.fix_explicit_addressing(follower_collection) + |> Transmogrifier.fix_implicit_addressing(follower_collection) end - def fix_attribution(data) do + def fix_activity_defaults(data, meta) do + object = meta[:object_data] || %{} + data - |> Map.put_new("actor", data["attributedTo"]) + |> Map.put_new("to", object["to"] || []) + |> Map.put_new("cc", object["cc"] || []) + |> Map.put_new("bto", object["bto"] || []) + |> Map.put_new("bcc", object["bcc"] || []) end def fix_actor(data) do - actor = Containment.get_actor(data) + actor = + data + |> Map.put_new("actor", data["attributedTo"]) + |> Containment.get_actor() data |> Map.put("actor", actor) diff --git a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex index e06e442f4..99e8dc6c7 100644 --- a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex @@ -62,21 +62,11 @@ defp fix_context(data, meta) do end end - defp fix_addressing(data, meta) do - if object = meta[:object_data] do - data - |> Map.put_new("to", object["to"] || []) - |> Map.put_new("cc", object["cc"] || []) - else - data - end - end - defp fix(data, meta) do data |> fix_context(meta) - |> fix_addressing(meta) |> CommonFixes.fix_actor() + |> CommonFixes.fix_activity_defaults(meta) end defp validate_data(cng, meta) do diff --git a/lib/pleroma/web/activity_pub/object_validators/event_validator.ex b/lib/pleroma/web/activity_pub/object_validators/event_validator.ex index d42458ef5..fee2e997a 100644 --- a/lib/pleroma/web/activity_pub/object_validators/event_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/event_validator.ex @@ -72,8 +72,8 @@ def cast_data(data) do defp fix(data) do data - |> CommonFixes.fix_defaults() - |> CommonFixes.fix_attribution() + |> CommonFixes.fix_actor() + |> CommonFixes.fix_object_defaults() |> Transmogrifier.fix_emoji() end diff --git a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex index 7012e2e1d..083d08ec4 100644 --- a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex @@ -83,8 +83,8 @@ defp fix_closed(data) do defp fix(data) do data - |> CommonFixes.fix_defaults() - |> CommonFixes.fix_attribution() + |> CommonFixes.fix_actor() + |> CommonFixes.fix_object_defaults() |> Transmogrifier.fix_emoji() |> fix_closed() end diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs index d3a2fd13f..ce8bef39f 100644 --- a/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs +++ b/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs @@ -9,7 +9,7 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.RecipientsTest do test "it asserts that all elements of the list are object ids" do list = ["https://lain.com/users/lain", "invalid"] - assert :error == Recipients.cast(list) + assert {:error, "invalid"} == Recipients.cast(list) end test "it works with a list" do diff --git a/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs index e733f167d..032ad24b5 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs @@ -24,6 +24,8 @@ test "it works for incoming listens" do "actor" => "http://mastodon.example.org/users/admin", "object" => %{ "type" => "Audio", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => [], "id" => "http://mastodon.example.org/users/admin/listens/1234", "attributedTo" => "http://mastodon.example.org/users/admin", "title" => "lain radio episode 1", @@ -61,7 +63,9 @@ test "Funkwhale Audio object" do assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] - assert object.data["cc"] == [] + assert object.data["cc"] == [ + "https://channels.tests.funkwhale.audio/federation/actors/compositions/followers" + ] assert object.data["url"] == "https://channels.tests.funkwhale.audio/library/tracks/74" diff --git a/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs index c4879fda1..14f5f704a 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs @@ -31,7 +31,7 @@ test "Mobilizon Event object" do ) assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] - assert object.data["cc"] == [] + assert object.data["cc"] == ["https://mobilizon.org/@tcit/followers"] assert object.data["url"] == "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" From c9449326747f8d33357f5179e69d3024b39089a0 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Thu, 10 Sep 2020 11:11:10 +0200 Subject: [PATCH 03/13] Pipeline Ingestion: Note --- .../object_validators/recipients.ex | 25 +-- lib/pleroma/web/activity_pub/activity_pub.ex | 2 +- .../web/activity_pub/object_validator.ex | 7 +- .../article_note_validator.ex | 29 +++- .../object_validators/common_fixes.ex | 18 +- .../object_validators/common_validations.ex | 1 + .../create_note_validator.ex | 29 ---- lib/pleroma/web/activity_pub/side_effects.ex | 15 +- .../web/activity_pub/transmogrifier.ex | 12 +- lib/pleroma/web/federator.ex | 5 + .../activitypub-client-post-activity.json | 1 + test/pleroma/activity_test.exs | 4 +- .../object_validators/recipients_test.exs | 4 +- test/pleroma/notification_test.exs | 6 + .../activity_pub_controller_test.exs | 45 ++--- .../transmogrifier/note_handling_test.exs | 155 ++++++++---------- .../web/activity_pub/transmogrifier_test.exs | 4 +- test/pleroma/web/federator_test.exs | 6 +- .../static_fe/static_fe_controller_test.exs | 13 +- 19 files changed, 202 insertions(+), 179 deletions(-) delete mode 100644 lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex index b76547e75..a03471462 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex @@ -13,20 +13,23 @@ def cast(object) when is_binary(object) do cast([object]) end + def cast(object) when is_map(object) do + case ObjectID.cast(object) do + {:ok, data} -> {:ok, data} + _ -> :error + end + end + def cast(data) when is_list(data) do data - |> Enum.reduce_while({:ok, []}, fn - nil, {:ok, list} -> - {:cont, {:ok, list}} + |> Enum.reduce_while({:ok, []}, fn element, {:ok, list} -> + case ObjectID.cast(element) do + {:ok, id} -> + {:cont, {:ok, [id | list]}} - element, {:ok, list} -> - case ObjectID.cast(element) do - {:ok, id} -> - {:cont, {:ok, [id | list]}} - - _ -> - {:halt, {:error, element}} - end + _ -> + {:cont, {:ok, list}} + end end) end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index efbf92c70..b74af3f3b 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -88,7 +88,7 @@ defp increase_replies_count_if_reply(%{ defp increase_replies_count_if_reply(_create_data), do: :noop - @object_types ~w[ChatMessage Question Answer Audio Video Event Article] + @object_types ~w[ChatMessage Question Answer Audio Video Event Article Note] @impl true def persist(%{"type" => type} = object, meta) when type in @object_types do with {:ok, object} <- Object.create(object) do diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index 70d9a35a9..e5b35cdd4 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -101,7 +101,7 @@ def validate( %{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity, meta ) - when objtype in ~w[Question Answer Audio Video Event Article] do + when objtype in ~w[Question Answer Audio Video Event Article Note] do with {:ok, object_data} <- cast_and_apply(object), meta = Keyword.put(meta, :object_data, object_data |> stringify_keys), {:ok, create_activity} <- @@ -114,7 +114,7 @@ def validate( end def validate(%{"type" => type} = object, meta) - when type in ~w[Event Question Audio Video Article] do + when type in ~w[Event Question Audio Video Article Note] do validator = case type do "Event" -> EventValidator @@ -122,6 +122,7 @@ def validate(%{"type" => type} = object, meta) "Audio" -> AudioVideoValidator "Video" -> AudioVideoValidator "Article" -> ArticleNoteValidator + "Note" -> ArticleNoteValidator end with {:ok, object} <- @@ -183,7 +184,7 @@ def cast_and_apply(%{"type" => "Event"} = object) do EventValidator.cast_and_apply(object) end - def cast_and_apply(%{"type" => "Article"} = object) do + def cast_and_apply(%{"type" => type} = object) when type in ~w[Article Note] do ArticleNoteValidator.cast_and_apply(object) end diff --git a/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex b/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex index d2026b5ea..193f85f49 100644 --- a/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex @@ -50,6 +50,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator do field(:likes, {:array, ObjectValidators.ObjectID}, default: []) field(:announcements, {:array, ObjectValidators.ObjectID}, default: []) + + field(:replies, {:array, ObjectValidators.ObjectID}, default: []) end def cast_and_apply(data) do @@ -65,24 +67,39 @@ def cast_and_validate(data) do end def cast_data(data) do - data = fix(data) - %__MODULE__{} |> changeset(data) end - defp fix_url(%{"url" => url} = data) when is_map(url) do - Map.put(data, "url", url["href"]) - end - + defp fix_url(%{"url" => url} = data) when is_bitstring(url), do: data + defp fix_url(%{"url" => url} = data) when is_map(url), do: Map.put(data, "url", url["href"]) defp fix_url(data), do: data + defp fix_tag(%{"tag" => tag} = data) when is_list(tag), do: data + defp fix_tag(%{"tag" => tag} = data) when is_map(tag), do: Map.put(data, "tag", [tag]) + defp fix_tag(data), do: Map.drop(data, ["tag"]) + + defp fix_replies(%{"replies" => %{"first" => %{"items" => replies}}} = data) + when is_list(replies), + do: Map.put(data, "replies", replies) + + defp fix_replies(%{"replies" => %{"items" => replies}} = data) when is_list(replies), + do: Map.put(data, "replies", replies) + + defp fix_replies(%{"replies" => replies} = data) when is_bitstring(replies), + do: Map.drop(data, ["replies"]) + + defp fix_replies(data), do: data + defp fix(data) do data |> CommonFixes.fix_actor() |> CommonFixes.fix_object_defaults() |> fix_url() + |> fix_tag() + |> fix_replies() |> Transmogrifier.fix_emoji() + |> Transmogrifier.fix_content_map() end def changeset(struct, data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex index 950eb1494..7309f6af2 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -26,14 +26,20 @@ def fix_object_defaults(data) do |> Transmogrifier.fix_implicit_addressing(follower_collection) end - def fix_activity_defaults(data, meta) do + defp fix_activity_recipients(activity, field, object) do + {:ok, data} = ObjectValidators.Recipients.cast(activity[field] || object[field]) + + Map.put(activity, field, data) + end + + def fix_activity_defaults(activity, meta) do object = meta[:object_data] || %{} - data - |> Map.put_new("to", object["to"] || []) - |> Map.put_new("cc", object["cc"] || []) - |> Map.put_new("bto", object["bto"] || []) - |> Map.put_new("bcc", object["bcc"] || []) + activity + |> fix_activity_recipients("to", object) + |> fix_activity_recipients("cc", object) + |> fix_activity_recipients("bto", object) + |> fix_activity_recipients("bcc", object) end def fix_actor(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex index 093549a45..85ac07044 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex @@ -14,6 +14,7 @@ def validate_any_presence(cng, fields) do fields |> Enum.map(fn field -> get_field(cng, field) end) |> Enum.any?(fn + nil -> false [] -> false _ -> true end) diff --git a/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex deleted file mode 100644 index a85a0298c..000000000 --- a/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex +++ /dev/null @@ -1,29 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateNoteValidator do - use Ecto.Schema - - alias Pleroma.EctoType.ActivityPub.ObjectValidators - alias Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator - - import Ecto.Changeset - - @primary_key false - - embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:actor, ObjectValidators.ObjectID) - field(:type, :string) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:bto, ObjectValidators.Recipients, default: []) - field(:bcc, ObjectValidators.Recipients, default: []) - embeds_one(:object, NoteValidator) - end - - def cast_data(data) do - cast(%__MODULE__{}, data, __schema__(:fields)) - end -end diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 0b9a9f0c5..3234b9e43 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -203,6 +203,19 @@ def handle(%{data: %{"type" => "Create"}} = activity, meta) do Object.increase_replies_count(in_reply_to) end + reply_depth = (meta[:depth] || 0) + 1 + + # FIXME: Force inReplyTo to replies + if Pleroma.Web.Federator.allowed_thread_distance?(reply_depth) and + object.data["replies"] != nil do + for reply_id <- object.data["replies"] do + Pleroma.Workers.RemoteFetcherWorker.enqueue("fetch_remote", %{ + "id" => reply_id, + "depth" => reply_depth + }) + end + end + ConcurrentLimiter.limit(Pleroma.Web.RichMedia.Helpers, fn -> Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end) end) @@ -366,7 +379,7 @@ def handle_object_creation(%{"type" => "Answer"} = object_map, meta) do end def handle_object_creation(%{"type" => objtype} = object, meta) - when objtype in ~w[Audio Video Question Event Article] do + when objtype in ~w[Audio Video Question Event Article Note] do with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do {:ok, object, meta} end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 047f23918..28bc25363 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -404,10 +404,9 @@ def handle_incoming(%{"id" => id}, _options) when is_binary(id) and byte_size(id # - tags # - emoji def handle_incoming( - %{"type" => "Create", "object" => %{"type" => objtype} = object} = data, + %{"type" => "Create", "object" => %{"type" => "Page"} = object} = data, options - ) - when objtype in ~w{Note Page} do + ) do actor = Containment.get_actor(data) with nil <- Activity.get_create_by_object_ap_id(object["id"]), @@ -499,14 +498,15 @@ def handle_incoming( def handle_incoming( %{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data, - _options + options ) - when objtype in ~w{Question Answer ChatMessage Audio Video Event Article} do + when objtype in ~w{Question Answer ChatMessage Audio Video Event Article Note} do data = Map.put(data, "object", strip_internal_fields(data["object"])) + options = Keyword.put(options, :local, false) with {:ok, %User{}} <- ObjectValidator.fetch_actor(data), nil <- Activity.get_create_by_object_ap_id(obj_id), - {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do + {:ok, activity, _} <- Pipeline.common_pipeline(data, options) do {:ok, activity} else %Activity{} = activity -> {:ok, activity} diff --git a/lib/pleroma/web/federator.ex b/lib/pleroma/web/federator.ex index f5ef76d32..69cfc2d52 100644 --- a/lib/pleroma/web/federator.ex +++ b/lib/pleroma/web/federator.ex @@ -96,6 +96,11 @@ def perform(:incoming_ap_doc, params) do Logger.debug("Unhandled actor #{actor}, #{inspect(e)}") {:error, e} + {:error, {:validate_object, _}} = e -> + Logger.error("Incoming AP doc validation error: #{inspect(e)}") + Logger.debug(Jason.encode!(params, pretty: true)) + e + e -> # Just drop those for now Logger.debug(fn -> "Unhandled activity\n" <> Jason.encode!(params, pretty: true) end) diff --git a/test/fixtures/activitypub-client-post-activity.json b/test/fixtures/activitypub-client-post-activity.json index c985e072b..e592081bc 100644 --- a/test/fixtures/activitypub-client-post-activity.json +++ b/test/fixtures/activitypub-client-post-activity.json @@ -3,6 +3,7 @@ "type": "Create", "object": { "type": "Note", + "to": ["https://www.w3.org/ns/activitystreams#Public"], "content": "It's a note" }, "to": ["https://www.w3.org/ns/activitystreams#Public"] diff --git a/test/pleroma/activity_test.exs b/test/pleroma/activity_test.exs index 390a06344..9911aa45c 100644 --- a/test/pleroma/activity_test.exs +++ b/test/pleroma/activity_test.exs @@ -123,7 +123,8 @@ test "when association is not loaded" do "type" => "Note", "content" => "find me!", "id" => "http://mastodon.example.org/users/admin/objects/1", - "attributedTo" => "http://mastodon.example.org/users/admin" + "attributedTo" => "http://mastodon.example.org/users/admin", + "to" => ["https://www.w3.org/ns/activitystreams#Public"] }, "to" => ["https://www.w3.org/ns/activitystreams#Public"] } @@ -132,6 +133,7 @@ test "when association is not loaded" do {:ok, japanese_activity} = Pleroma.Web.CommonAPI.post(user, %{status: "更新情報"}) {:ok, job} = Pleroma.Web.Federator.incoming_ap_doc(params) {:ok, remote_activity} = ObanHelpers.perform(job) + remote_activity = Activity.get_by_id_with_object(remote_activity.id) %{ japanese_activity: japanese_activity, diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs index ce8bef39f..4cdafa898 100644 --- a/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs +++ b/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs @@ -6,10 +6,10 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.RecipientsTest do alias Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients use Pleroma.DataCase, async: true - test "it asserts that all elements of the list are object ids" do + test "it only keeps elements that are valid object ids" do list = ["https://lain.com/users/lain", "invalid"] - assert {:error, "invalid"} == Recipients.cast(list) + assert {:ok, ["https://lain.com/users/lain"]} == Recipients.cast(list) end test "it works with a list" do diff --git a/test/pleroma/notification_test.exs b/test/pleroma/notification_test.exs index abf1b0410..85f895f0f 100644 --- a/test/pleroma/notification_test.exs +++ b/test/pleroma/notification_test.exs @@ -624,6 +624,8 @@ test "it sends notifications to mentioned users in new messages" do "actor" => user.ap_id, "object" => %{ "type" => "Note", + "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(), + "to" => ["https://www.w3.org/ns/activitystreams#Public"], "content" => "message with a Mention tag, but no explicit tagging", "tag" => [ %{ @@ -655,6 +657,9 @@ test "it does not send notifications to users who are only cc in new messages" d "actor" => user.ap_id, "object" => %{ "type" => "Note", + "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(), + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => [other_user.ap_id], "content" => "hi everyone", "attributedTo" => user.ap_id } @@ -951,6 +956,7 @@ test "notifications are deleted if a remote user is deleted" do "cc" => [], "object" => %{ "type" => "Note", + "id" => remote_user.ap_id <> "/objects/test", "content" => "Hello!", "tag" => [ %{ diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs index 19e04d472..2de52323e 100644 --- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -539,7 +539,7 @@ test "it inserts an incoming activity into the database" <> File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!() |> Map.put("actor", user.ap_id) - |> put_in(["object", "attridbutedTo"], user.ap_id) + |> put_in(["object", "attributedTo"], user.ap_id) conn = conn @@ -820,29 +820,34 @@ test "it clears `unreachable` federation status of the sender", %{conn: conn, da assert Instances.reachable?(sender_host) end + @tag capture_log: true 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") - |> Jason.decode!() + to = [ + recipient.ap_id, + recipient.follower_address, + "https://www.w3.org/ns/activitystreams#Public" + ] - object = Map.put(data["object"], "attributedTo", actor.ap_id) + cc = [recipient.follower_address, actor.follower_address] - 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" - ]) + data = %{ + "@context" => ["https://www.w3.org/ns/activitystreams"], + "type" => "Create", + "id" => Utils.generate_activity_id(), + "to" => to, + "cc" => cc, + "actor" => actor.ap_id, + "object" => %{ + "type" => "Note", + "to" => to, + "cc" => cc, + "content" => "It's a note", + "attributedTo" => actor.ap_id, + "id" => Utils.generate_object_id() + } + } conn |> assign(:valid_signature, true) @@ -852,7 +857,7 @@ test "it removes all follower collections but actor's", %{conn: conn} do ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - activity = Activity.get_by_ap_id(data["id"]) + assert activity = Activity.get_by_ap_id(data["id"]) assert activity.id assert actor.follower_address in activity.recipients diff --git a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs index deb956410..3eeae4004 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs @@ -14,7 +14,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do import Mock import Pleroma.Factory - import ExUnit.CaptureLog setup_all do Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) @@ -147,9 +146,7 @@ test "it does not crash if the object in inReplyTo can't be fetched" do data |> Map.put("object", object) - assert capture_log(fn -> - {:ok, _returned_activity} = Transmogrifier.handle_incoming(data) - end) =~ "[warn] Couldn't fetch \"https://404.site/whatever\", error: nil" + assert {:ok, _returned_activity} = Transmogrifier.handle_incoming(data) end test "it does not work for deactivated users" do @@ -221,8 +218,25 @@ test "it works for incoming notices with hashtags" do {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) object = Object.normalize(data["object"], fetch: false) - assert Enum.at(Object.tags(object), 2) == "moo" - assert Object.hashtags(object) == ["moo"] + assert match?( + %{ + "href" => "http://localtesting.pleroma.lol/users/lain", + "name" => "@lain@localtesting.pleroma.lol", + "type" => "Mention" + }, + Enum.at(object.data["tag"], 0) + ) + + assert match?( + %{ + "href" => "http://mastodon.example.org/tags/moo", + "name" => "#moo", + "type" => "Hashtag" + }, + Enum.at(object.data["tag"], 1) + ) + + assert "moo" == Enum.at(object.data["tag"], 2) end test "it works for incoming notices with contentMap" do @@ -276,13 +290,11 @@ test "it ensures that address fields become lists" do File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!() |> Map.put("actor", user.ap_id) - |> Map.put("to", nil) |> Map.put("cc", nil) object = data["object"] |> Map.put("attributedTo", user.ap_id) - |> Map.put("to", nil) |> Map.put("cc", nil) |> Map.put("id", user.ap_id <> "/activities/12345678") @@ -290,8 +302,7 @@ test "it ensures that address fields become lists" do {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - assert !is_nil(data["to"]) - assert !is_nil(data["cc"]) + refute is_nil(data["cc"]) end test "it strips internal likes" do @@ -330,70 +341,46 @@ test "it strips internal reactions" do end test "it correctly processes messages with non-array to field" do - user = insert(:user) + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + |> Map.put("to", "https://www.w3.org/ns/activitystreams#Public") + |> put_in(["object", "to"], "https://www.w3.org/ns/activitystreams#Public") - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => "https://www.w3.org/ns/activitystreams#Public", - "type" => "Create", - "object" => %{ - "content" => "blah blah blah", - "type" => "Note", - "attributedTo" => user.ap_id, - "inReplyTo" => nil - }, - "actor" => user.ap_id - } + assert {:ok, activity} = Transmogrifier.handle_incoming(data) - assert {:ok, activity} = Transmogrifier.handle_incoming(message) + assert [ + "http://mastodon.example.org/users/admin/followers", + "http://localtesting.pleroma.lol/users/lain" + ] == activity.data["cc"] assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"] end test "it correctly processes messages with non-array cc field" do - user = insert(:user) + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + |> Map.put("cc", "http://mastodon.example.org/users/admin/followers") + |> put_in(["object", "cc"], "http://mastodon.example.org/users/admin/followers") - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => user.follower_address, - "cc" => "https://www.w3.org/ns/activitystreams#Public", - "type" => "Create", - "object" => %{ - "content" => "blah blah blah", - "type" => "Note", - "attributedTo" => user.ap_id, - "inReplyTo" => nil - }, - "actor" => user.ap_id - } + assert {:ok, activity} = Transmogrifier.handle_incoming(data) - assert {:ok, activity} = Transmogrifier.handle_incoming(message) - - assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"] - assert [user.follower_address] == activity.data["to"] + assert ["http://mastodon.example.org/users/admin/followers"] == activity.data["cc"] + assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"] end test "it correctly processes messages with weirdness in address fields" do - user = insert(:user) + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + |> Map.put("cc", ["http://mastodon.example.org/users/admin/followers", ["¿"]]) + |> put_in(["object", "cc"], ["http://mastodon.example.org/users/admin/followers", ["¿"]]) - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => [nil, user.follower_address], - "cc" => ["https://www.w3.org/ns/activitystreams#Public", ["¿"]], - "type" => "Create", - "object" => %{ - "content" => "…", - "type" => "Note", - "attributedTo" => user.ap_id, - "inReplyTo" => nil - }, - "actor" => user.ap_id - } + assert {:ok, activity} = Transmogrifier.handle_incoming(data) - assert {:ok, activity} = Transmogrifier.handle_incoming(message) - - assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"] - assert [user.follower_address] == activity.data["to"] + assert ["http://mastodon.example.org/users/admin/followers"] == activity.data["cc"] + assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"] end end @@ -419,7 +406,11 @@ test "schedules background fetching of `replies` items if max thread depth limit } do clear_config([:instance, :federation_incoming_replies_max_depth], 10) - {:ok, _activity} = Transmogrifier.handle_incoming(data) + {:ok, activity} = Transmogrifier.handle_incoming(data) + + object = Object.normalize(activity.data["object"]) + + assert object.data["replies"] == items for id <- items do job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1} @@ -442,45 +433,41 @@ test "does NOT schedule background fetching of `replies` beyond max thread depth setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) setup do - user = insert(:user) + replies = %{ + "type" => "Collection", + "items" => [ + Pleroma.Web.ActivityPub.Utils.generate_object_id(), + Pleroma.Web.ActivityPub.Utils.generate_object_id() + ] + } - {:ok, activity} = CommonAPI.post(user, %{status: "post1"}) + activity = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + |> Kernel.put_in(["object", "replies"], replies) - {:ok, reply1} = - CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: activity.id}) - - {:ok, reply2} = - CommonAPI.post(user, %{status: "reply2", in_reply_to_status_id: activity.id}) - - replies_uris = Enum.map([reply1, reply2], fn a -> a.object.data["id"] end) - - {:ok, federation_output} = Transmogrifier.prepare_outgoing(activity.data) - - Repo.delete(activity.object) - Repo.delete(activity) - - %{federation_output: federation_output, replies_uris: replies_uris} + %{activity: activity} end test "schedules background fetching of `replies` items if max thread depth limit allows", %{ - federation_output: federation_output, - replies_uris: replies_uris + activity: activity } do clear_config([:instance, :federation_incoming_replies_max_depth], 1) - {:ok, _activity} = Transmogrifier.handle_incoming(federation_output) + assert {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(activity) + object = Object.normalize(data["object"]) - for id <- replies_uris do + for id <- object.data["replies"] do job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1} assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args) end end test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows", - %{federation_output: federation_output} do + %{activity: activity} do clear_config([:instance, :federation_incoming_replies_max_depth], 0) - {:ok, _activity} = Transmogrifier.handle_incoming(federation_output) + {:ok, _activity} = Transmogrifier.handle_incoming(activity) assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == [] end @@ -498,6 +485,7 @@ test "successfully reserializes a message with inReplyTo == nil" do "object" => %{ "to" => ["https://www.w3.org/ns/activitystreams#Public"], "cc" => [], + "id" => Utils.generate_object_id(), "type" => "Note", "content" => "Hi", "inReplyTo" => nil, @@ -522,6 +510,7 @@ test "successfully reserializes a message with AS2 objects in IR" do "object" => %{ "to" => ["https://www.w3.org/ns/activitystreams#Public"], "cc" => [], + "id" => Utils.generate_object_id(), "type" => "Note", "content" => "Hi", "inReplyTo" => nil, diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs index bb0b58e4d..5a3b57acb 100644 --- a/test/pleroma/web/activity_pub/transmogrifier_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do alias Pleroma.Tests.ObanHelpers alias Pleroma.User alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.CommonAPI @@ -159,8 +160,7 @@ test "it adds the json-ld context and the conversation property" do {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - assert modified["@context"] == - Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"] + assert modified["@context"] == Utils.make_json_ld_header()["@context"] assert modified["object"]["conversation"] == modified["context"] end diff --git a/test/pleroma/web/federator_test.exs b/test/pleroma/web/federator_test.exs index 532ee6d30..372b6a73a 100644 --- a/test/pleroma/web/federator_test.exs +++ b/test/pleroma/web/federator_test.exs @@ -123,7 +123,8 @@ test "successfully processes incoming AP docs with correct origin" do "type" => "Note", "content" => "hi world!", "id" => "http://mastodon.example.org/users/admin/objects/1", - "attributedTo" => "http://mastodon.example.org/users/admin" + "attributedTo" => "http://mastodon.example.org/users/admin", + "to" => ["https://www.w3.org/ns/activitystreams#Public"] }, "to" => ["https://www.w3.org/ns/activitystreams#Public"] } @@ -145,7 +146,8 @@ test "rejects incoming AP docs with incorrect origin" do "type" => "Note", "content" => "hi world!", "id" => "http://mastodon.example.org/users/admin/objects/1", - "attributedTo" => "http://mastodon.example.org/users/admin" + "attributedTo" => "http://mastodon.example.org/users/admin", + "to" => ["https://www.w3.org/ns/activitystreams#Public"] }, "to" => ["https://www.w3.org/ns/activitystreams#Public"] } diff --git a/test/pleroma/web/static_fe/static_fe_controller_test.exs b/test/pleroma/web/static_fe/static_fe_controller_test.exs index 2af14dfeb..5752cffda 100644 --- a/test/pleroma/web/static_fe/static_fe_controller_test.exs +++ b/test/pleroma/web/static_fe/static_fe_controller_test.exs @@ -7,6 +7,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do alias Pleroma.Activity alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.CommonAPI import Pleroma.Factory @@ -185,16 +186,16 @@ test "404 for private status", %{conn: conn, user: user} do test "302 for remote cached status", %{conn: conn, user: user} do message = %{ "@context" => "https://www.w3.org/ns/activitystreams", - "to" => user.follower_address, - "cc" => "https://www.w3.org/ns/activitystreams#Public", "type" => "Create", + "actor" => user.ap_id, "object" => %{ + "to" => user.follower_address, + "cc" => "https://www.w3.org/ns/activitystreams#Public", + "id" => Utils.generate_object_id(), "content" => "blah blah blah", "type" => "Note", - "attributedTo" => user.ap_id, - "inReplyTo" => nil - }, - "actor" => user.ap_id + "attributedTo" => user.ap_id + } } assert {:ok, activity} = Transmogrifier.handle_incoming(message) From 641184fc7aff694e4e7e802b9204a1d313c0877c Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Thu, 10 Sep 2020 19:45:42 +0200 Subject: [PATCH 04/13] recipients fixes/hardening for CreateGenericValidator --- .../object_validators/recipients.ex | 25 ++++---- .../object_validators/common_fixes.ex | 34 ++++++----- .../create_generic_validator.ex | 60 +++++++++++++------ .../transmogrifier/note_handling_test.exs | 12 ++-- 4 files changed, 82 insertions(+), 49 deletions(-) diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex index a03471462..06fed8fb3 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex @@ -15,22 +15,27 @@ def cast(object) when is_binary(object) do def cast(object) when is_map(object) do case ObjectID.cast(object) do - {:ok, data} -> {:ok, data} + {:ok, data} -> {:ok, [data]} _ -> :error end end def cast(data) when is_list(data) do - data - |> Enum.reduce_while({:ok, []}, fn element, {:ok, list} -> - case ObjectID.cast(element) do - {:ok, id} -> - {:cont, {:ok, [id | list]}} + data = + data + |> Enum.reduce_while([], fn element, list -> + case ObjectID.cast(element) do + {:ok, id} -> + {:cont, [id | list]} - _ -> - {:cont, {:ok, list}} - end - end) + _ -> + {:cont, list} + end + end) + |> Enum.sort() + |> Enum.uniq() + + {:ok, data} end def cast(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex index 7309f6af2..009cd51b0 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -9,37 +9,39 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils + def cast_recipients(message, field, field_fallback \\ []) do + {:ok, data} = ObjectValidators.Recipients.cast(message[field] || field_fallback) + + Map.put(message, field, data) + end + def fix_object_defaults(data) do %{data: %{"id" => context}, id: context_id} = Utils.create_context(data["context"] || data["conversation"]) %User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["attributedTo"]) - {:ok, to} = ObjectValidators.Recipients.cast(data["to"] || []) - {:ok, cc} = ObjectValidators.Recipients.cast(data["cc"] || []) data |> Map.put("context", context) |> Map.put("context_id", context_id) - |> Map.put("to", to) - |> Map.put("cc", cc) + |> cast_recipients("to") + |> cast_recipients("cc") + |> cast_recipients("bto") + |> cast_recipients("bcc") |> Transmogrifier.fix_explicit_addressing(follower_collection) |> Transmogrifier.fix_implicit_addressing(follower_collection) end - defp fix_activity_recipients(activity, field, object) do - {:ok, data} = ObjectValidators.Recipients.cast(activity[field] || object[field]) - - Map.put(activity, field, data) - end - - def fix_activity_defaults(activity, meta) do - object = meta[:object_data] || %{} + def fix_activity_addressing(activity, _meta) do + %User{follower_address: follower_collection} = User.get_cached_by_ap_id(activity["actor"]) activity - |> fix_activity_recipients("to", object) - |> fix_activity_recipients("cc", object) - |> fix_activity_recipients("bto", object) - |> fix_activity_recipients("bcc", object) + |> cast_recipients("to") + |> cast_recipients("cc") + |> cast_recipients("bto") + |> cast_recipients("bcc") + |> Transmogrifier.fix_explicit_addressing(follower_collection) + |> Transmogrifier.fix_implicit_addressing(follower_collection) end def fix_actor(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex index 99e8dc6c7..51d43e8d0 100644 --- a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex @@ -10,8 +10,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Object + alias Pleroma.User alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations + alias Pleroma.Web.ActivityPub.Transmogrifier import Ecto.Changeset @@ -23,6 +25,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do field(:type, :string) field(:to, ObjectValidators.Recipients, default: []) field(:cc, ObjectValidators.Recipients, default: []) + field(:bto, ObjectValidators.Recipients, default: []) + field(:bcc, ObjectValidators.Recipients, default: []) field(:object, ObjectValidators.ObjectID) field(:expires_at, ObjectValidators.DateTime) @@ -54,29 +58,38 @@ def changeset(struct, data) do |> cast(data, __schema__(:fields)) end - defp fix_context(data, meta) do - if object = meta[:object_data] do - Map.put_new(data, "context", object["context"]) - else - data - end + # CommonFixes.fix_activity_addressing adapted for Create specific behavior + defp fix_addressing(data, object) do + %User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["actor"]) + + data + |> CommonFixes.cast_recipients("to", object["to"]) + |> CommonFixes.cast_recipients("cc", object["cc"]) + |> CommonFixes.cast_recipients("bto", object["bto"]) + |> CommonFixes.cast_recipients("bcc", object["bcc"]) + |> Transmogrifier.fix_explicit_addressing(follower_collection) + |> Transmogrifier.fix_implicit_addressing(follower_collection) end - defp fix(data, meta) do + def fix(data, meta) do + object = meta[:object_data] + data - |> fix_context(meta) |> CommonFixes.fix_actor() - |> CommonFixes.fix_activity_defaults(meta) + |> Map.put_new("context", object["context"]) + |> fix_addressing(object) end defp validate_data(cng, meta) do + object = meta[:object_data] + cng - |> validate_required([:actor, :type, :object]) + |> validate_required([:actor, :type, :object, :to, :cc]) |> validate_inclusion(:type, ["Create"]) |> CommonValidations.validate_actor_presence() - |> CommonValidations.validate_any_presence([:to, :cc]) - |> validate_actors_match(meta) - |> validate_context_match(meta) + |> validate_actors_match(object) + |> validate_context_match(object) + |> validate_addressing_match(object) |> validate_object_nonexistence() |> validate_object_containment() end @@ -108,8 +121,8 @@ def validate_object_nonexistence(cng) do end) end - def validate_actors_match(cng, meta) do - attributed_to = meta[:object_data]["attributedTo"] || meta[:object_data]["actor"] + def validate_actors_match(cng, object) do + attributed_to = object["attributedTo"] || object["actor"] cng |> validate_change(:actor, fn :actor, actor -> @@ -121,7 +134,7 @@ def validate_actors_match(cng, meta) do end) end - def validate_context_match(cng, %{object_data: %{"context" => object_context}}) do + def validate_context_match(cng, %{"context" => object_context}) do cng |> validate_change(:context, fn :context, context -> if context == object_context do @@ -132,5 +145,18 @@ def validate_context_match(cng, %{object_data: %{"context" => object_context}}) end) end - def validate_context_match(cng, _), do: cng + def validate_addressing_match(cng, object) do + [:to, :cc, :bcc, :bto] + |> Enum.reduce(cng, fn field, cng -> + object_data = object[to_string(field)] + + validate_change(cng, field, fn field, data -> + if data == object_data do + [] + else + [{field, "field doesn't match with object (#{inspect(object_data)})"}] + end + end) + end) + end end diff --git a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs index 3eeae4004..b79f2c94c 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs @@ -171,8 +171,8 @@ test "it works for incoming notices" do assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] assert data["cc"] == [ - "http://mastodon.example.org/users/admin/followers", - "http://localtesting.pleroma.lol/users/lain" + "http://localtesting.pleroma.lol/users/lain", + "http://mastodon.example.org/users/admin/followers" ] assert data["actor"] == "http://mastodon.example.org/users/admin" @@ -185,8 +185,8 @@ test "it works for incoming notices" do assert object_data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] assert object_data["cc"] == [ - "http://mastodon.example.org/users/admin/followers", - "http://localtesting.pleroma.lol/users/lain" + "http://localtesting.pleroma.lol/users/lain", + "http://mastodon.example.org/users/admin/followers" ] assert object_data["actor"] == "http://mastodon.example.org/users/admin" @@ -350,8 +350,8 @@ test "it correctly processes messages with non-array to field" do assert {:ok, activity} = Transmogrifier.handle_incoming(data) assert [ - "http://mastodon.example.org/users/admin/followers", - "http://localtesting.pleroma.lol/users/lain" + "http://localtesting.pleroma.lol/users/lain", + "http://mastodon.example.org/users/admin/followers" ] == activity.data["cc"] assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"] From 96212b2e32e2542964c665f091158fb1ff1d987d Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Tue, 15 Sep 2020 17:22:08 +0200 Subject: [PATCH 05/13] Fix addressing --- lib/pleroma/object/fetcher.ex | 7 ++++-- .../object_validators/common_fixes.ex | 25 +++++++++++-------- .../create_generic_validator.ex | 9 +++---- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index bcccf1c4c..82d2c8bcb 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Object.Fetcher do alias Pleroma.HTTP + alias Pleroma.Maps alias Pleroma.Object alias Pleroma.Object.Containment alias Pleroma.Repo @@ -124,12 +125,14 @@ def fetch_object_from_id(id, options \\ []) do defp prepare_activity_params(data) do %{ "type" => "Create", - "to" => data["to"] || [], - "cc" => data["cc"] || [], # Should we seriously keep this attributedTo thing? "actor" => data["actor"] || data["attributedTo"], "object" => data } + |> Maps.put_if_present("to", data["to"]) + |> Maps.put_if_present("cc", data["cc"]) + |> Maps.put_if_present("bto", data["bto"]) + |> Maps.put_if_present("bcc", data["bcc"]) end def fetch_object_from_id!(id, options \\ []) do diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex index 009cd51b0..c958fcc5d 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -9,9 +9,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils - def cast_recipients(message, field, field_fallback \\ []) do + def cast_and_filter_recipients(message, field, follower_collection, field_fallback \\ []) do {:ok, data} = ObjectValidators.Recipients.cast(message[field] || field_fallback) + data = + Enum.reject(data, fn x -> + String.ends_with?(x, "/followers") and x != follower_collection + end) + Map.put(message, field, data) end @@ -24,11 +29,10 @@ def fix_object_defaults(data) do data |> Map.put("context", context) |> Map.put("context_id", context_id) - |> cast_recipients("to") - |> cast_recipients("cc") - |> cast_recipients("bto") - |> cast_recipients("bcc") - |> Transmogrifier.fix_explicit_addressing(follower_collection) + |> cast_and_filter_recipients("to", follower_collection) + |> cast_and_filter_recipients("cc", follower_collection) + |> cast_and_filter_recipients("bto", follower_collection) + |> cast_and_filter_recipients("bcc", follower_collection) |> Transmogrifier.fix_implicit_addressing(follower_collection) end @@ -36,11 +40,10 @@ def fix_activity_addressing(activity, _meta) do %User{follower_address: follower_collection} = User.get_cached_by_ap_id(activity["actor"]) activity - |> cast_recipients("to") - |> cast_recipients("cc") - |> cast_recipients("bto") - |> cast_recipients("bcc") - |> Transmogrifier.fix_explicit_addressing(follower_collection) + |> cast_and_filter_recipients("to", follower_collection) + |> cast_and_filter_recipients("cc", follower_collection) + |> cast_and_filter_recipients("bto", follower_collection) + |> cast_and_filter_recipients("bcc", follower_collection) |> Transmogrifier.fix_implicit_addressing(follower_collection) end diff --git a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex index 51d43e8d0..d2de53049 100644 --- a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex @@ -63,11 +63,10 @@ defp fix_addressing(data, object) do %User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["actor"]) data - |> CommonFixes.cast_recipients("to", object["to"]) - |> CommonFixes.cast_recipients("cc", object["cc"]) - |> CommonFixes.cast_recipients("bto", object["bto"]) - |> CommonFixes.cast_recipients("bcc", object["bcc"]) - |> Transmogrifier.fix_explicit_addressing(follower_collection) + |> CommonFixes.cast_and_filter_recipients("to", follower_collection, object["to"]) + |> CommonFixes.cast_and_filter_recipients("cc", follower_collection, object["cc"]) + |> CommonFixes.cast_and_filter_recipients("bto", follower_collection, object["bto"]) + |> CommonFixes.cast_and_filter_recipients("bcc", follower_collection, object["bcc"]) |> Transmogrifier.fix_implicit_addressing(follower_collection) end From d1205406d9237c72d10df937dd8d2d4da2786cc5 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Tue, 15 Sep 2020 18:18:57 +0200 Subject: [PATCH 06/13] ActivityPubControllerTest: Apply same addr changes to object --- lib/pleroma/web/activity_pub/utils.ex | 5 +++- .../activity_pub_controller_test.exs | 30 ++++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index a4dc469dc..e81623d83 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -97,7 +97,10 @@ def maybe_splice_recipient(ap_id, params) do if need_splice? do cc_list = extract_list(params["cc"]) - Map.put(params, "cc", [ap_id | cc_list]) + + params + |> Map.put("cc", [ap_id | cc_list]) + |> Kernel.put_in(["object", "cc"], [ap_id | cc_list]) else params end diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs index 2de52323e..f6ea9e2ca 100644 --- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -649,7 +649,11 @@ test "without valid signature, " <> 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]) + + data = + data + |> Map.put("bcc", [user.ap_id]) + |> Kernel.put_in(["object", "bcc"], [user.ap_id]) conn = conn @@ -666,8 +670,11 @@ test "it accepts messages with to as string instead of array", %{conn: conn, dat user = insert(:user) data = - Map.put(data, "to", user.ap_id) - |> Map.delete("cc") + data + |> Map.put("to", user.ap_id) + |> Map.put("cc", []) + |> Kernel.put_in(["object", "to"], user.ap_id) + |> Kernel.put_in(["object", "cc"], []) conn = conn @@ -684,8 +691,11 @@ test "it accepts messages with cc as string instead of array", %{conn: conn, dat user = insert(:user) data = - Map.put(data, "cc", user.ap_id) - |> Map.delete("to") + data + |> Map.put("to", []) + |> Map.put("cc", user.ap_id) + |> Kernel.put_in(["object", "to"], []) + |> Kernel.put_in(["object", "cc"], user.ap_id) conn = conn @@ -703,9 +713,13 @@ test "it accepts messages with bcc as string instead of array", %{conn: conn, da user = insert(:user) data = - Map.put(data, "bcc", user.ap_id) - |> Map.delete("to") - |> Map.delete("cc") + data + |> Map.put("to", []) + |> Map.put("cc", []) + |> Map.put("bcc", user.ap_id) + |> Kernel.put_in(["object", "to"], []) + |> Kernel.put_in(["object", "cc"], []) + |> Kernel.put_in(["object", "bcc"], user.ap_id) conn = conn From b0c778fde77f5ec2320b0bd0327e8a13b0f39a63 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Tue, 15 Sep 2020 18:19:38 +0200 Subject: [PATCH 07/13] NoteHandlingTest: remove fix_explicit_addressing-related test --- .../transmogrifier/note_handling_test.exs | 42 +++---------------- 1 file changed, 6 insertions(+), 36 deletions(-) diff --git a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs index b79f2c94c..1846b2291 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs @@ -10,6 +10,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do alias Pleroma.Object alias Pleroma.User alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.CommonAPI import Mock @@ -42,36 +43,6 @@ test "it works for incoming notices with tag not being an array (kroeg)" do assert Object.hashtags(object) == ["test"] end - test "it cleans up incoming notices which are not really DMs" do - user = insert(:user) - other_user = insert(:user) - - to = [user.ap_id, other_user.ap_id] - - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Jason.decode!() - |> Map.put("to", to) - |> Map.put("cc", []) - - object = - data["object"] - |> Map.put("to", to) - |> Map.put("cc", []) - - data = Map.put(data, "object", object) - - {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) - - assert data["to"] == [] - assert data["cc"] == to - - object_data = Object.normalize(activity, fetch: false).data - - assert object_data["to"] == [] - assert object_data["cc"] == to - end - test "it ignores an incoming notice if we already have it" do activity = insert(:note_activity) @@ -321,9 +292,11 @@ test "it strips internal likes" do object = Map.put(data["object"], "likes", likes) data = Map.put(data, "object", object) - {:ok, %Activity{object: object}} = Transmogrifier.handle_incoming(data) + {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(data) - refute Map.has_key?(object.data, "likes") + object = Object.normalize(activity) + + assert object.data["likes"] == [] end test "it strips internal reactions" do @@ -435,10 +408,7 @@ test "does NOT schedule background fetching of `replies` beyond max thread depth setup do replies = %{ "type" => "Collection", - "items" => [ - Pleroma.Web.ActivityPub.Utils.generate_object_id(), - Pleroma.Web.ActivityPub.Utils.generate_object_id() - ] + "items" => [Utils.generate_object_id(), Utils.generate_object_id()] } activity = From 461123110b7cf47f4d2c01d1dd6992a2b63337fe Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Thu, 17 Sep 2020 16:17:16 +0200 Subject: [PATCH 08/13] Object.Fetcher: Fix getting transmogrifier reject reason --- lib/pleroma/object/fetcher.ex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index 82d2c8bcb..4ca67f0fd 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -102,6 +102,9 @@ def fetch_object_from_id(id, options \\ []) do {:transmogrifier, {:error, {:reject, e}}} -> {:reject, e} + {:transmogrifier, {:reject, e}} -> + {:reject, e} + {:transmogrifier, _} = e -> {:error, e} From 6c9f6e62c8453f023c6ec9106d1a7c3e66ab95b7 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Mon, 28 Sep 2020 19:34:27 +0200 Subject: [PATCH 09/13] transmogrifier: Fixing votes from Note to Answer --- .../object_validators/answer_validator.ex | 7 ++++++ .../web/activity_pub/transmogrifier.ex | 22 ++++++++++++------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex b/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex index c9bd9e42d..3451e1ff8 100644 --- a/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator do use Ecto.Schema alias Pleroma.EctoType.ActivityPub.ObjectValidators + alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations import Ecto.Changeset @@ -23,6 +24,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator do field(:name, :string) field(:inReplyTo, ObjectValidators.ObjectID) field(:attributedTo, ObjectValidators.ObjectID) + field(:context, :string) # TODO: Remove actor on objects field(:actor, ObjectValidators.ObjectID) @@ -46,6 +48,11 @@ def cast_data(data) do end def changeset(struct, data) do + data = + data + |> CommonFixes.fix_actor() + |> CommonFixes.fix_object_defaults() + struct |> cast(data, __schema__(:fields)) end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 28bc25363..454bbce9d 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -43,7 +43,6 @@ def fix_object(object, options \\ []) do |> fix_content_map() |> fix_addressing() |> fix_summary() - |> fix_type(options) end def fix_summary(%{"summary" => nil} = object) do @@ -321,19 +320,18 @@ def fix_content_map(%{"contentMap" => content_map} = object) do def fix_content_map(object), do: object - def fix_type(object, options \\ []) + defp fix_type(%{"type" => "Note", "inReplyTo" => reply_id, "name" => _} = object, options) + when is_binary(reply_id) do + options = Keyword.put(options, :fetch, true) - def fix_type(%{"inReplyTo" => reply_id, "name" => _} = object, options) - when is_binary(reply_id) do - with true <- Federator.allowed_thread_distance?(options[:depth]), - {:ok, %{data: %{"type" => "Question"} = _} = _} <- get_obj_helper(reply_id, options) do + with %Object{data: %{"type" => "Question"}} <- Object.normalize(reply_id, options) do Map.put(object, "type", "Answer") else _ -> object end end - def fix_type(object, _), do: object + defp fix_type(object, _options), do: object # Reduce the object list to find the reported user. defp get_reported(objects) do @@ -501,7 +499,15 @@ def handle_incoming( options ) when objtype in ~w{Question Answer ChatMessage Audio Video Event Article Note} do - data = Map.put(data, "object", strip_internal_fields(data["object"])) + fetch_options = Keyword.put(options, :depth, (options[:depth] || 0) + 1) + + object = + data["object"] + |> strip_internal_fields() + |> fix_type(fetch_options) + |> fix_in_reply_to(fetch_options) + + data = Map.put(data, "object", object) options = Keyword.put(options, :local, false) with {:ok, %User{}} <- ObjectValidator.fetch_actor(data), From 0b88accae632e371becacb16be4e8798aa80c705 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 21 Oct 2020 01:20:06 +0200 Subject: [PATCH 10/13] fetcher_test: Fix missing mock function --- test/pleroma/object/fetcher_test.exs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/pleroma/object/fetcher_test.exs b/test/pleroma/object/fetcher_test.exs index a7ac90348..8d9c6c3cb 100644 --- a/test/pleroma/object/fetcher_test.exs +++ b/test/pleroma/object/fetcher_test.exs @@ -66,6 +66,14 @@ defmodule Pleroma.Object.FetcherTest do %Tesla.Env{ status: 500 } + + %{ + method: :get, + url: "https://stereophonic.space/objects/02997b83-3ea7-4b63-94af-ef3aa2d4ed17" + } -> + %Tesla.Env{ + status: 500 + } end) :ok From 53193b84b1d07c9fd3c6b80c04e3eada4fb4cd59 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 27 Nov 2020 00:25:24 +0100 Subject: [PATCH 11/13] =?UTF-8?q?utils:=20Fix=20maybe=5Fsplice=5Frecipient?= =?UTF-8?q?=20when=20"object"=20isn=E2=80=99t=20a=20map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pleroma/maps.ex | 6 ++++++ lib/pleroma/web/activity_pub/utils.ex | 6 +++--- .../web/activity_pub/activity_pub_controller_test.exs | 1 - 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/pleroma/maps.ex b/lib/pleroma/maps.ex index 0d2e94248..b08b83305 100644 --- a/lib/pleroma/maps.ex +++ b/lib/pleroma/maps.ex @@ -12,4 +12,10 @@ def put_if_present(map, key, value, value_function \\ &{:ok, &1}) when is_map(ma _ -> map end end + + def safe_put_in(data, keys, value) when is_map(data) and is_list(keys) do + Kernel.put_in(data, keys, value) + rescue + _ -> data + end end diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index e81623d83..0d1a6d0f1 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -96,11 +96,11 @@ def maybe_splice_recipient(ap_id, params) do !label_in_collection?(ap_id, params["cc"]) if need_splice? do - cc_list = extract_list(params["cc"]) + cc = [ap_id | extract_list(params["cc"])] params - |> Map.put("cc", [ap_id | cc_list]) - |> Kernel.put_in(["object", "cc"], [ap_id | cc_list]) + |> Map.put("cc", cc) + |> Maps.safe_put_in(["object", "cc"], cc) else params end diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs index f6ea9e2ca..f3ce703e2 100644 --- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -1003,7 +1003,6 @@ test "forwarded report from mastodon", %{conn: conn} do "actor" => remote_actor, "content" => "test report", "id" => "https://#{remote_domain}/e3b12fd1-948c-446e-b93b-a5e67edbe1d8", - "nickname" => reported_user.nickname, "object" => [ reported_user.ap_id, note.data["object"] From 6d6bef64bf3b37457b71cf7025e84aa9017a3b86 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Thu, 25 Mar 2021 10:17:26 +0100 Subject: [PATCH 12/13] fetcher_test: Remove assert on fake Create having an ap_id --- test/pleroma/object/fetcher_test.exs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/pleroma/object/fetcher_test.exs b/test/pleroma/object/fetcher_test.exs index 8d9c6c3cb..bd0a6e497 100644 --- a/test/pleroma/object/fetcher_test.exs +++ b/test/pleroma/object/fetcher_test.exs @@ -132,8 +132,7 @@ test "it fetches an object" do {:ok, object} = Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") - assert activity = Activity.get_create_by_object_ap_id(object.data["id"]) - assert activity.data["id"] + assert _activity = Activity.get_create_by_object_ap_id(object.data["id"]) {:ok, object_again} = Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") From 5ef4659b373ae1106090952ff3e963b419fa1d72 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Mon, 5 Apr 2021 18:57:14 +0200 Subject: [PATCH 13/13] test/pleroma/web/common_api_test.exs: Strip : around emoji key-name --- test/pleroma/web/common_api_test.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs index 6619f8fc8..86c12f0b2 100644 --- a/test/pleroma/web/common_api_test.exs +++ b/test/pleroma/web/common_api_test.exs @@ -539,8 +539,8 @@ test "it copies emoji from the subject of the parent post" do spoiler_text: ":joker_smile:" }) - assert Object.normalize(reply_activity).data["emoji"][":joker_smile:"] - refute Object.normalize(reply_activity).data["emoji"][":joker_disapprove:"] + assert Object.normalize(reply_activity).data["emoji"]["joker_smile"] + refute Object.normalize(reply_activity).data["emoji"]["joker_disapprove"] end test "deactivated users can't post" do