Fix MRF policies to also work with Update
[akkoma] / lib / pleroma / web / activity_pub / mrf / tag_policy.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
6 alias Pleroma.User
7 @behaviour Pleroma.Web.ActivityPub.MRF.Policy
8 @moduledoc """
9 Apply policies based on user tags
10
11 This policy applies policies on a user activities depending on their tags
12 on your instance.
13
14 - `mrf_tag:media-force-nsfw`: Mark as sensitive on presence of attachments
15 - `mrf_tag:media-strip`: Remove attachments
16 - `mrf_tag:force-unlisted`: Mark as unlisted (removes from the federated timeline)
17 - `mrf_tag:sandbox`: Remove from public (local and federated) timelines
18 - `mrf_tag:disable-remote-subscription`: Reject non-local follow requests
19 - `mrf_tag:disable-any-subscription`: Reject any follow requests
20 """
21
22 require Pleroma.Constants
23
24 defp get_tags(%User{tags: tags}) when is_list(tags), do: tags
25 defp get_tags(_), do: []
26
27 defp process_tag(
28 "mrf_tag:media-force-nsfw",
29 %{
30 "type" => type,
31 "object" => %{"attachment" => child_attachment}
32 } = message
33 )
34 when length(child_attachment) > 0 and type in ["Create", "Update"] do
35 {:ok, Kernel.put_in(message, ["object", "sensitive"], true)}
36 end
37
38 defp process_tag(
39 "mrf_tag:media-strip",
40 %{
41 "type" => type,
42 "object" => %{"attachment" => child_attachment} = object
43 } = message
44 )
45 when length(child_attachment) > 0 and type in ["Create", "Update"] do
46 object = Map.delete(object, "attachment")
47 message = Map.put(message, "object", object)
48
49 {:ok, message}
50 end
51
52 defp process_tag(
53 "mrf_tag:force-unlisted",
54 %{
55 "type" => type,
56 "to" => to,
57 "cc" => cc,
58 "actor" => actor,
59 "object" => object
60 } = message
61 )
62 when type in ["Create", "Update"] do
63 user = User.get_cached_by_ap_id(actor)
64
65 if Enum.member?(to, Pleroma.Constants.as_public()) do
66 to = List.delete(to, Pleroma.Constants.as_public()) ++ [user.follower_address]
67 cc = List.delete(cc, user.follower_address) ++ [Pleroma.Constants.as_public()]
68
69 object =
70 object
71 |> Map.put("to", to)
72 |> Map.put("cc", cc)
73
74 message =
75 message
76 |> Map.put("to", to)
77 |> Map.put("cc", cc)
78 |> Map.put("object", object)
79
80 {:ok, message}
81 else
82 {:ok, message}
83 end
84 end
85
86 defp process_tag(
87 "mrf_tag:sandbox",
88 %{
89 "type" => type,
90 "to" => to,
91 "cc" => cc,
92 "actor" => actor,
93 "object" => object
94 } = message
95 )
96 when type in ["Create", "Update"] do
97 user = User.get_cached_by_ap_id(actor)
98
99 if Enum.member?(to, Pleroma.Constants.as_public()) or
100 Enum.member?(cc, Pleroma.Constants.as_public()) do
101 to = List.delete(to, Pleroma.Constants.as_public()) ++ [user.follower_address]
102 cc = List.delete(cc, Pleroma.Constants.as_public())
103
104 object =
105 object
106 |> Map.put("to", to)
107 |> Map.put("cc", cc)
108
109 message =
110 message
111 |> Map.put("to", to)
112 |> Map.put("cc", cc)
113 |> Map.put("object", object)
114
115 {:ok, message}
116 else
117 {:ok, message}
118 end
119 end
120
121 defp process_tag(
122 "mrf_tag:disable-remote-subscription",
123 %{"type" => "Follow", "actor" => actor} = message
124 ) do
125 user = User.get_cached_by_ap_id(actor)
126
127 if user.local == true do
128 {:ok, message}
129 else
130 {:reject,
131 "[TagPolicy] Follow from #{actor} tagged with mrf_tag:disable-remote-subscription"}
132 end
133 end
134
135 defp process_tag("mrf_tag:disable-any-subscription", %{"type" => "Follow", "actor" => actor}),
136 do: {:reject, "[TagPolicy] Follow from #{actor} tagged with mrf_tag:disable-any-subscription"}
137
138 defp process_tag(_, message), do: {:ok, message}
139
140 def filter_message(actor, message) do
141 User.get_cached_by_ap_id(actor)
142 |> get_tags()
143 |> Enum.reduce({:ok, message}, fn
144 tag, {:ok, message} ->
145 process_tag(tag, message)
146
147 _, error ->
148 error
149 end)
150 end
151
152 @impl true
153 def filter(%{"object" => target_actor, "type" => "Follow"} = message),
154 do: filter_message(target_actor, message)
155
156 @impl true
157 def filter(%{"actor" => actor, "type" => type} = message) when type in ["Create", "Update"],
158 do: filter_message(actor, message)
159
160 @impl true
161 def filter(message), do: {:ok, message}
162
163 @impl true
164 def describe, do: {:ok, %{}}
165 end