From 9f99640cfc81f28c0b6cdb5f6ef065b3e46f5f23 Mon Sep 17 00:00:00 2001
From: rinpatch <rinpatch@sdf.org>
Date: Thu, 5 Dec 2019 02:50:38 +0300
Subject: [PATCH] ActivityPub: Fix deletes being exempt from MRF

Closes #1461
---
 CHANGELOG.md                                      |  1 +
 lib/pleroma/activity.ex                           |  5 +++--
 lib/pleroma/activity/queries.ex                   |  8 ++++++++
 lib/pleroma/object.ex                             |  2 +-
 lib/pleroma/web/activity_pub/activity_pub.ex      |  5 +++--
 .../activity_pub/activity_pub_controller_test.exs |  2 +-
 test/web/activity_pub/activity_pub_test.exs       | 15 +++++++++++++++
 7 files changed, 32 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a06ea211e..3ec34f452 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -78,6 +78,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 ### Fixed
 - Report emails now include functional links to profiles of remote user accounts
 - Not being able to log in to some third-party apps when logged in to MastoFE
+- MRF: `Delete` activities being exempt from MRF policies
 <details>
   <summary>API Changes</summary>
 
diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex
index f180c1e33..480b261cf 100644
--- a/lib/pleroma/activity.ex
+++ b/lib/pleroma/activity.ex
@@ -241,9 +241,10 @@ def normalize(obj) when is_map(obj), do: get_by_ap_id_with_object(obj["id"])
   def normalize(ap_id) when is_binary(ap_id), do: get_by_ap_id_with_object(ap_id)
   def normalize(_), do: nil
 
-  def delete_by_ap_id(id) when is_binary(id) do
+  def delete_all_by_object_ap_id(id) when is_binary(id) do
     id
     |> Queries.by_object_id()
+    |> Queries.exclude_type("Delete")
     |> select([u], u)
     |> Repo.delete_all()
     |> elem(1)
@@ -255,7 +256,7 @@ def delete_by_ap_id(id) when is_binary(id) do
     |> purge_web_resp_cache()
   end
 
-  def delete_by_ap_id(_), do: nil
+  def delete_all_by_object_ap_id(_), do: nil
 
   defp purge_web_resp_cache(%Activity{} = activity) do
     %{path: path} = URI.parse(activity.data["id"])
diff --git a/lib/pleroma/activity/queries.ex b/lib/pleroma/activity/queries.ex
index 949f010a8..26bc1099d 100644
--- a/lib/pleroma/activity/queries.ex
+++ b/lib/pleroma/activity/queries.ex
@@ -64,4 +64,12 @@ def by_type(query \\ Activity, activity_type) do
       where: fragment("(?)->>'type' = ?", activity.data, ^activity_type)
     )
   end
+
+  @spec exclude_type(query, String.t()) :: query
+  def exclude_type(query \\ Activity, activity_type) do
+    from(
+      activity in query,
+      where: fragment("(?)->>'type' != ?", activity.data, ^activity_type)
+    )
+  end
 end
diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex
index b4ed3a9b2..ff0e59241 100644
--- a/lib/pleroma/object.ex
+++ b/lib/pleroma/object.ex
@@ -147,7 +147,7 @@ def swap_object_with_tombstone(object) do
 
   def delete(%Object{data: %{"id" => id}} = object) do
     with {:ok, _obj} = swap_object_with_tombstone(object),
-         deleted_activity = Activity.delete_by_ap_id(id),
+         deleted_activity = Activity.delete_all_by_object_ap_id(id),
          {:ok, true} <- Cachex.del(:object_cache, "object:#{id}"),
          {:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path) do
       {:ok, object, deleted_activity}
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index d6a425d8b..c8160b0cf 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -456,17 +456,18 @@ def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ [
     user = User.get_cached_by_ap_id(actor)
     to = (object.data["to"] || []) ++ (object.data["cc"] || [])
 
-    with {:ok, object, activity} <- Object.delete(object),
+    with create_activity <- Activity.get_create_by_object_ap_id(id),
          data <-
            %{
              "type" => "Delete",
              "actor" => actor,
              "object" => id,
              "to" => to,
-             "deleted_activity_id" => activity && activity.id
+             "deleted_activity_id" => create_activity && create_activity.id
            }
            |> maybe_put("id", activity_id),
          {:ok, activity} <- insert(data, local, false),
+         {:ok, object, _create_activity} <- Object.delete(object),
          stream_out_participations(object, user),
          _ <- decrease_replies_count_if_reply(object),
          {:ok, _actor} <- decrease_note_count_if_public(user, object),
diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs
index 1aa73d75c..ba2ce1dd9 100644
--- a/test/web/activity_pub/activity_pub_controller_test.exs
+++ b/test/web/activity_pub/activity_pub_controller_test.exs
@@ -298,7 +298,7 @@ test "cached purged after activity deletion", %{conn: conn} do
       assert json_response(conn1, :ok)
       assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
 
-      Activity.delete_by_ap_id(activity.object.data["id"])
+      Activity.delete_all_by_object_ap_id(activity.object.data["id"])
 
       conn2 =
         conn
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index 2677b9e36..23dffb150 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -1256,6 +1256,21 @@ test "decreases reply count" do
       assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
       assert object.data["repliesCount"] == 0
     end
+
+    test "it passes delete activity through MRF before deleting the object" do
+      rewrite_policy = Pleroma.Config.get([:instance, :rewrite_policy])
+      Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.DropPolicy)
+
+      on_exit(fn -> Pleroma.Config.put([:instance, :rewrite_policy], rewrite_policy) end)
+
+      note = insert(:note_activity)
+      object = Object.normalize(note)
+
+      {:error, {:reject, _}} = ActivityPub.delete(object)
+
+      assert Activity.get_by_id(note.id)
+      assert Repo.get(Object, object.id).data["type"] == object.data["type"]
+    end
   end
 
   describe "timeline post-processing" do