diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9d4ec8b67..8586ea8a0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- OAuth: added job to clean expired access tokens
- MRF: Support for rejecting reports from specific instances (`mrf_simple`)
- MRF: Support for stripping avatars and banner images from specific instances (`mrf_simple`)
+- MRF: Support for running subchains.
### Changed
- **Breaking:** Configuration: move from Pleroma.Mailer to Pleroma.Emails.Mailer
diff --git a/config/config.exs b/config/config.exs
index 2c71f4a27..7d70c1a5e 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -326,6 +326,8 @@
federated_timeline_removal: [],
replace: []
+config :pleroma, :mrf_subchain, match_actor: %{}
+
config :pleroma, :rich_media, enabled: true
config :pleroma, :media_proxy,
diff --git a/docs/config.md b/docs/config.md
index e37ae6b95..718a7912a 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -86,6 +86,7 @@ config :pleroma, Pleroma.Emails.Mailer,
* `Pleroma.Web.ActivityPub.MRF.NoOpPolicy`: Doesn’t modify activities (default)
* `Pleroma.Web.ActivityPub.MRF.DropPolicy`: Drops all activities. It generally doesn’t makes sense to use in production
* `Pleroma.Web.ActivityPub.MRF.SimplePolicy`: Restrict the visibility of activities from certains instances (See ``:mrf_simple`` section)
+ * `Pleroma.Web.ActivityPub.MRF.SubchainPolicy`: Selectively runs other MRF policies when messages match (see ``:mrf_subchain`` section)
* `Pleroma.Web.ActivityPub.MRF.RejectNonPublic`: Drops posts with non-public visibility settings (See ``:mrf_rejectnonpublic`` section)
* `Pleroma.Web.ActivityPub.MRF.EnsureRePrepended`: Rewrites posts to ensure that replies to posts with subjects do not have an identical subject and instead begin with re:.
* `public`: Makes the client API in authentificated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network.
@@ -229,6 +230,21 @@ relates to mascots on the mastodon frontend
* `avatar_removal`: List of instances to strip avatars from
* `banner_removal`: List of instances to strip banners from
+## :mrf_subchain
+This policy processes messages through an alternate pipeline when a given message matches certain criteria.
+All criteria are configured as a map of regular expressions to lists of policy modules.
+
+* `match_actor`: Matches a series of regular expressions against the actor field.
+
+Example:
+
+```
+config :pleroma, :mrf_subchain,
+ match_actor: %{
+ ~r/https:\/\/example.com/s => [Pleroma.Web.ActivityPub.MRF.DropPolicy]
+ }
+```
+
## :mrf_rejectnonpublic
* `allow_followersonly`: whether to allow followers-only posts
* `allow_direct`: whether to allow direct messages
diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex
index 3bf7955f3..10ceef715 100644
--- a/lib/pleroma/web/activity_pub/mrf.ex
+++ b/lib/pleroma/web/activity_pub/mrf.ex
@@ -5,8 +5,8 @@
defmodule Pleroma.Web.ActivityPub.MRF do
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
- def filter(object) do
- get_policies()
+ def filter(policies, %{} = object) do
+ policies
|> Enum.reduce({:ok, object}, fn
policy, {:ok, object} ->
policy.filter(object)
@@ -16,6 +16,8 @@ def filter(object) do
end)
end
+ def filter(%{} = object), do: get_policies() |> filter(object)
+
def get_policies do
Pleroma.Config.get([:instance, :rewrite_policy], []) |> get_policies()
end
diff --git a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex
new file mode 100644
index 000000000..765704389
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex
@@ -0,0 +1,40 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
+ alias Pleroma.Config
+ alias Pleroma.Web.ActivityPub.MRF
+
+ require Logger
+
+ @behaviour MRF
+
+ defp lookup_subchain(actor) do
+ with matches <- Config.get([:mrf_subchain, :match_actor]),
+ {match, subchain} <- Enum.find(matches, fn {k, _v} -> String.match?(actor, k) end) do
+ {:ok, match, subchain}
+ else
+ _e -> {:error, :notfound}
+ end
+ end
+
+ @impl true
+ def filter(%{"actor" => actor} = message) do
+ with {:ok, match, subchain} <- lookup_subchain(actor) do
+ Logger.debug(
+ "[SubchainPolicy] Matched #{actor} against #{inspect(match)} with subchain #{
+ inspect(subchain)
+ }"
+ )
+
+ subchain
+ |> MRF.filter(message)
+ else
+ _e -> {:ok, message}
+ end
+ end
+
+ @impl true
+ def filter(message), do: {:ok, message}
+end
diff --git a/test/web/activity_pub/mrf/subchain_policy_test.exs b/test/web/activity_pub/mrf/subchain_policy_test.exs
new file mode 100644
index 000000000..f7cbcad48
--- /dev/null
+++ b/test/web/activity_pub/mrf/subchain_policy_test.exs
@@ -0,0 +1,32 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicyTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Web.ActivityPub.MRF.DropPolicy
+ alias Pleroma.Web.ActivityPub.MRF.SubchainPolicy
+
+ @message %{
+ "actor" => "https://banned.com",
+ "type" => "Create",
+ "object" => %{"content" => "hi"}
+ }
+
+ test "it matches and processes subchains when the actor matches a configured target" do
+ Pleroma.Config.put([:mrf_subchain, :match_actor], %{
+ ~r/^https:\/\/banned.com/s => [DropPolicy]
+ })
+
+ {:reject, _} = SubchainPolicy.filter(@message)
+ end
+
+ test "it doesn't match and process subchains when the actor doesn't match a configured target" do
+ Pleroma.Config.put([:mrf_subchain, :match_actor], %{
+ ~r/^https:\/\/borked.com/s => [DropPolicy]
+ })
+
+ {:ok, _message} = SubchainPolicy.filter(@message)
+ end
+end