From 91f3cf9bc6e8e8567d20bb859ee0bb9854a20a07 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Fri, 24 Jul 2020 14:06:41 +0200 Subject: [PATCH 1/4] Pipeline: Add embedded object federation. --- lib/pleroma/web/activity_pub/pipeline.ex | 7 ++++ test/web/activity_pub/pipeline_test.exs | 45 ++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/lib/pleroma/web/activity_pub/pipeline.ex b/lib/pleroma/web/activity_pub/pipeline.ex index 6875c47f6..50d9016e6 100644 --- a/lib/pleroma/web/activity_pub/pipeline.ex +++ b/lib/pleroma/web/activity_pub/pipeline.ex @@ -52,6 +52,13 @@ defp maybe_federate(%Activity{} = activity, meta) do do_not_federate = meta[:do_not_federate] || !Config.get([:instance, :federating]) if !do_not_federate && local do + activity = + if object = Keyword.get(meta, :embedded_object) do + %{activity | data: Map.put(activity.data, "object", object)} + else + activity + end + Federator.publish(activity) {:ok, :federated} else diff --git a/test/web/activity_pub/pipeline_test.exs b/test/web/activity_pub/pipeline_test.exs index 8deb64501..202b5fe61 100644 --- a/test/web/activity_pub/pipeline_test.exs +++ b/test/web/activity_pub/pipeline_test.exs @@ -14,6 +14,51 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do :ok end + test "when given an `embedded_object` in meta, Federation will receive a the original activity with the `object` field set to this embedded object" do + activity = insert(:note_activity) + object = %{"id" => "1", "type" => "Love"} + meta = [local: true, embedded_object: object] + + activity_with_object = %{activity | data: Map.put(activity.data, "object", object)} + + with_mocks([ + {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, + { + Pleroma.Web.ActivityPub.MRF, + [], + [filter: fn o -> {:ok, o} end] + }, + { + Pleroma.Web.ActivityPub.ActivityPub, + [], + [persist: fn o, m -> {:ok, o, m} end] + }, + { + Pleroma.Web.ActivityPub.SideEffects, + [], + [ + handle: fn o, m -> {:ok, o, m} end, + handle_after_transaction: fn m -> m end + ] + }, + { + Pleroma.Web.Federator, + [], + [publish: fn _o -> :ok end] + } + ]) do + assert {:ok, ^activity, ^meta} = + Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) + + assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity)) + assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) + refute called(Pleroma.Web.Federator.publish(activity)) + assert_called(Pleroma.Web.Federator.publish(activity_with_object)) + end + end + test "it goes through validation, filtering, persisting, side effects and federation for local activities" do activity = insert(:note_activity) meta = [local: true] From 3d13fb05f851d127d852ee9c26afa4dab61410ad Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Fri, 24 Jul 2020 14:40:22 +0200 Subject: [PATCH 2/4] Side Effects: On undoing, put information about the undone object. --- lib/pleroma/web/activity_pub/side_effects.ex | 4 ++++ test/web/activity_pub/side_effects_test.exs | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 1d2c296a5..33bee1576 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -174,6 +174,10 @@ def handle(%{data: %{"type" => "Announce"}} = object, meta) do def handle(%{data: %{"type" => "Undo", "object" => undone_object}} = object, meta) do with undone_object <- Activity.get_by_ap_id(undone_object), :ok <- handle_undoing(undone_object) do + meta = + meta + |> Keyword.put(:embedded_object, undone_object.data) + {:ok, object, meta} end end diff --git a/test/web/activity_pub/side_effects_test.exs b/test/web/activity_pub/side_effects_test.exs index 2649b060a..d48c235c0 100644 --- a/test/web/activity_pub/side_effects_test.exs +++ b/test/web/activity_pub/side_effects_test.exs @@ -312,8 +312,13 @@ test "when activation is required", %{delete: delete, user: user} do } end - test "deletes the original block", %{block_undo: block_undo, block: block} do - {:ok, _block_undo, _} = SideEffects.handle(block_undo) + test "deletes the original block, but sets `embedded_object`", %{ + block_undo: block_undo, + block: block + } do + {:ok, _block_undo, meta} = SideEffects.handle(block_undo) + + assert meta[:embedded_object] == block.data refute Activity.get_by_id(block.id) end From 1dd6de03ee655f5247ac62fee488307c934d7378 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Fri, 24 Jul 2020 14:54:13 +0200 Subject: [PATCH 3/4] CommonAPI Tests: Check that undoing objects federates them as embeds. --- test/web/common_api/common_api_test.exs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 7e11fede3..313dda21b 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -624,14 +624,27 @@ test "unreacting to a status with an emoji" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"}) - {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍") + clear_config([:instance, :federating], true) - {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍") + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"}) + {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍") - assert unreaction.data["type"] == "Undo" - assert unreaction.data["object"] == reaction.data["id"] - assert unreaction.local + {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍") + + assert unreaction.data["type"] == "Undo" + assert unreaction.data["object"] == reaction.data["id"] + assert unreaction.local + + # On federation, it contains the undone (and deleted) object + unreaction_with_object = %{ + unreaction + | data: Map.put(unreaction.data, "object", reaction.data) + } + + assert called(Pleroma.Web.Federator.publish(unreaction_with_object)) + end end test "repeating a status" do From 9e6f4694dd21f92bb2292e819d0f7f1cad149887 Mon Sep 17 00:00:00 2001 From: lain Date: Mon, 27 Jul 2020 16:39:50 +0200 Subject: [PATCH 4/4] Pipeline: Unify embedded_object / object_data, move to validator. --- lib/pleroma/web/activity_pub/object_validator.ex | 7 +++++++ lib/pleroma/web/activity_pub/pipeline.ex | 2 +- lib/pleroma/web/activity_pub/side_effects.ex | 4 ---- test/web/activity_pub/pipeline_test.exs | 4 ++-- test/web/activity_pub/side_effects_test.exs | 5 ++--- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index df926829c..0dcc7be4d 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do the system. """ + alias Pleroma.Activity alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Object alias Pleroma.User @@ -71,6 +72,12 @@ def validate(%{"type" => "Undo"} = object, meta) do |> UndoValidator.cast_and_validate() |> Ecto.Changeset.apply_action(:insert) do object = stringify_keys(object) + undone_object = Activity.get_by_ap_id(object["object"]) + + meta = + meta + |> Keyword.put(:object_data, undone_object.data) + {:ok, object, meta} end end diff --git a/lib/pleroma/web/activity_pub/pipeline.ex b/lib/pleroma/web/activity_pub/pipeline.ex index 50d9016e6..36e325c37 100644 --- a/lib/pleroma/web/activity_pub/pipeline.ex +++ b/lib/pleroma/web/activity_pub/pipeline.ex @@ -53,7 +53,7 @@ defp maybe_federate(%Activity{} = activity, meta) do if !do_not_federate && local do activity = - if object = Keyword.get(meta, :embedded_object) do + if object = Keyword.get(meta, :object_data) do %{activity | data: Map.put(activity.data, "object", object)} else activity diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 33bee1576..1d2c296a5 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -174,10 +174,6 @@ def handle(%{data: %{"type" => "Announce"}} = object, meta) do def handle(%{data: %{"type" => "Undo", "object" => undone_object}} = object, meta) do with undone_object <- Activity.get_by_ap_id(undone_object), :ok <- handle_undoing(undone_object) do - meta = - meta - |> Keyword.put(:embedded_object, undone_object.data) - {:ok, object, meta} end end diff --git a/test/web/activity_pub/pipeline_test.exs b/test/web/activity_pub/pipeline_test.exs index 202b5fe61..f2a231eaf 100644 --- a/test/web/activity_pub/pipeline_test.exs +++ b/test/web/activity_pub/pipeline_test.exs @@ -14,10 +14,10 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do :ok end - test "when given an `embedded_object` in meta, Federation will receive a the original activity with the `object` field set to this embedded object" do + test "when given an `object_data` in meta, Federation will receive a the original activity with the `object` field set to this embedded object" do activity = insert(:note_activity) object = %{"id" => "1", "type" => "Love"} - meta = [local: true, embedded_object: object] + meta = [local: true, object_data: object] activity_with_object = %{activity | data: Map.put(activity.data, "object", object)} diff --git a/test/web/activity_pub/side_effects_test.exs b/test/web/activity_pub/side_effects_test.exs index d48c235c0..4a08eb7ee 100644 --- a/test/web/activity_pub/side_effects_test.exs +++ b/test/web/activity_pub/side_effects_test.exs @@ -312,13 +312,12 @@ test "when activation is required", %{delete: delete, user: user} do } end - test "deletes the original block, but sets `embedded_object`", %{ + test "deletes the original block", %{ block_undo: block_undo, block: block } do - {:ok, _block_undo, meta} = SideEffects.handle(block_undo) + {:ok, _block_undo, _meta} = SideEffects.handle(block_undo) - assert meta[:embedded_object] == block.data refute Activity.get_by_id(block.id) end