Make MRF Keyword history-aware
This commit is contained in:
parent
04ded94a50
commit
eba9b0760f
|
@ -197,4 +197,44 @@ def make_new_object_data_from_update_object(original_data, new_data) do
|
||||||
used_history_in_new_object?: used_history_in_new_object?
|
used_history_in_new_object?: used_history_in_new_object?
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp for_each_history_item(%{"orderedItems" => items} = history, _object, fun) do
|
||||||
|
new_items =
|
||||||
|
Enum.map(items, fun)
|
||||||
|
|> Enum.reduce_while(
|
||||||
|
{:ok, []},
|
||||||
|
fn
|
||||||
|
{:ok, item}, {:ok, acc} -> {:cont, {:ok, acc ++ [item]}}
|
||||||
|
e, _acc -> {:halt, e}
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
case new_items do
|
||||||
|
{:ok, items} -> {:ok, Map.put(history, "orderedItems", items)}
|
||||||
|
e -> e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp for_each_history_item(history, _, _) do
|
||||||
|
{:ok, history}
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_with_history(object, fun) do
|
||||||
|
with history <- object["formerRepresentations"],
|
||||||
|
object <- Map.drop(object, ["formerRepresentations"]),
|
||||||
|
{_, {:ok, object}} <- {:main_body, fun.(object)},
|
||||||
|
{_, {:ok, history}} <- {:history_items, for_each_history_item(history, object, fun)} do
|
||||||
|
object =
|
||||||
|
if history do
|
||||||
|
Map.put(object, "formerRepresentations", history)
|
||||||
|
else
|
||||||
|
object
|
||||||
|
end
|
||||||
|
|
||||||
|
{:ok, object}
|
||||||
|
else
|
||||||
|
{:main_body, e} -> e
|
||||||
|
{:history_items, e} -> e
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,6 +27,8 @@ defp object_payload(%{} = object) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_reject(%{"object" => %{} = object} = message) do
|
defp check_reject(%{"object" => %{} = object} = message) do
|
||||||
|
with {:ok, _new_object} <-
|
||||||
|
Pleroma.Object.Updater.do_with_history(object, fn object ->
|
||||||
payload = object_payload(object)
|
payload = object_payload(object)
|
||||||
|
|
||||||
if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern ->
|
if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern ->
|
||||||
|
@ -36,15 +38,35 @@ defp check_reject(%{"object" => %{} = object} = message) do
|
||||||
else
|
else
|
||||||
{:ok, message}
|
{:ok, message}
|
||||||
end
|
end
|
||||||
|
end) do
|
||||||
|
{:ok, message}
|
||||||
|
else
|
||||||
|
e -> e
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_ftl_removal(%{"to" => to, "object" => %{} = object} = message) do
|
defp check_ftl_removal(%{"type" => "Create", "to" => to, "object" => %{} = object} = message) do
|
||||||
|
check_keyword = fn object ->
|
||||||
payload = object_payload(object)
|
payload = object_payload(object)
|
||||||
|
|
||||||
if Pleroma.Constants.as_public() in to and
|
if Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern ->
|
||||||
Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern ->
|
|
||||||
string_matches?(payload, pattern)
|
string_matches?(payload, pattern)
|
||||||
end) do
|
end) do
|
||||||
|
{:should_delist, nil}
|
||||||
|
else
|
||||||
|
{:ok, %{}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
should_delist? = fn object ->
|
||||||
|
with {:ok, _} <- Pleroma.Object.Updater.do_with_history(object, check_keyword) do
|
||||||
|
false
|
||||||
|
else
|
||||||
|
_ -> true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if Pleroma.Constants.as_public() in to and should_delist?.(object) do
|
||||||
to = List.delete(to, Pleroma.Constants.as_public())
|
to = List.delete(to, Pleroma.Constants.as_public())
|
||||||
cc = [Pleroma.Constants.as_public() | message["cc"] || []]
|
cc = [Pleroma.Constants.as_public() | message["cc"] || []]
|
||||||
|
|
||||||
|
@ -59,8 +81,12 @@ defp check_ftl_removal(%{"to" => to, "object" => %{} = object} = message) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp check_ftl_removal(message) do
|
||||||
|
{:ok, message}
|
||||||
|
end
|
||||||
|
|
||||||
defp check_replace(%{"object" => %{} = object} = message) do
|
defp check_replace(%{"object" => %{} = object} = message) do
|
||||||
object =
|
replace_kw = fn object ->
|
||||||
["content", "name", "summary"]
|
["content", "name", "summary"]
|
||||||
|> Enum.filter(fn field -> Map.has_key?(object, field) && object[field] end)
|
|> Enum.filter(fn field -> Map.has_key?(object, field) && object[field] end)
|
||||||
|> Enum.reduce(object, fn field, object ->
|
|> Enum.reduce(object, fn field, object ->
|
||||||
|
@ -73,6 +99,10 @@ defp check_replace(%{"object" => %{} = object} = message) do
|
||||||
|
|
||||||
Map.put(object, field, data)
|
Map.put(object, field, data)
|
||||||
end)
|
end)
|
||||||
|
|> (fn object -> {:ok, object} end).()
|
||||||
|
end
|
||||||
|
|
||||||
|
{:ok, object} = Pleroma.Object.Updater.do_with_history(object, replace_kw)
|
||||||
|
|
||||||
message = Map.put(message, "object", object)
|
message = Map.put(message, "object", object)
|
||||||
|
|
||||||
|
@ -80,7 +110,8 @@ defp check_replace(%{"object" => %{} = object} = message) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def filter(%{"type" => "Create", "object" => %{"content" => _content}} = message) do
|
def filter(%{"type" => type, "object" => %{"content" => _content}} = message)
|
||||||
|
when type in ["Create", "Update"] do
|
||||||
with {:ok, message} <- check_reject(message),
|
with {:ok, message} <- check_reject(message),
|
||||||
{:ok, message} <- check_ftl_removal(message),
|
{:ok, message} <- check_ftl_removal(message),
|
||||||
{:ok, message} <- check_replace(message) do
|
{:ok, message} <- check_replace(message) do
|
||||||
|
|
|
@ -79,6 +79,54 @@ test "rejects if regex matches in summary" do
|
||||||
KeywordPolicy.filter(message)
|
KeywordPolicy.filter(message)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "rejects if string matches in history" do
|
||||||
|
clear_config([:mrf_keyword, :reject], ["pun"])
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"type" => "Create",
|
||||||
|
"object" => %{
|
||||||
|
"content" => "just a daily reminder that compLAINer is a good",
|
||||||
|
"summary" => "",
|
||||||
|
"formerRepresentations" => %{
|
||||||
|
"type" => "OrderedCollection",
|
||||||
|
"orderedItems" => [
|
||||||
|
%{
|
||||||
|
"content" => "just a daily reminder that compLAINer is a good pun",
|
||||||
|
"summary" => ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} =
|
||||||
|
KeywordPolicy.filter(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "rejects Updates" do
|
||||||
|
clear_config([:mrf_keyword, :reject], ["pun"])
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"type" => "Update",
|
||||||
|
"object" => %{
|
||||||
|
"content" => "just a daily reminder that compLAINer is a good",
|
||||||
|
"summary" => "",
|
||||||
|
"formerRepresentations" => %{
|
||||||
|
"type" => "OrderedCollection",
|
||||||
|
"orderedItems" => [
|
||||||
|
%{
|
||||||
|
"content" => "just a daily reminder that compLAINer is a good pun",
|
||||||
|
"summary" => ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} =
|
||||||
|
KeywordPolicy.filter(message)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "delisting from ftl based on keywords" do
|
describe "delisting from ftl based on keywords" do
|
||||||
|
@ -157,6 +205,31 @@ test "delists if regex matches in summary" do
|
||||||
not (["https://www.w3.org/ns/activitystreams#Public"] == result["to"])
|
not (["https://www.w3.org/ns/activitystreams#Public"] == result["to"])
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "delists if string matches in history" do
|
||||||
|
clear_config([:mrf_keyword, :federated_timeline_removal], ["pun"])
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"type" => "Create",
|
||||||
|
"object" => %{
|
||||||
|
"content" => "just a daily reminder that compLAINer is a good",
|
||||||
|
"summary" => "",
|
||||||
|
"formerRepresentations" => %{
|
||||||
|
"orderedItems" => [
|
||||||
|
%{
|
||||||
|
"content" => "just a daily reminder that compLAINer is a good pun",
|
||||||
|
"summary" => ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, result} = KeywordPolicy.filter(message)
|
||||||
|
assert ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"]
|
||||||
|
refute ["https://www.w3.org/ns/activitystreams#Public"] == result["to"]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "replacing keywords" do
|
describe "replacing keywords" do
|
||||||
|
@ -221,5 +294,63 @@ test "replaces keyword if regex matches in summary" do
|
||||||
result == "ZFS is free software"
|
result == "ZFS is free software"
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "replaces keyword if string matches in history" do
|
||||||
|
clear_config([:mrf_keyword, :replace], [{"opensource", "free software"}])
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"type" => "Create",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"object" => %{
|
||||||
|
"content" => "ZFS is opensource",
|
||||||
|
"summary" => "",
|
||||||
|
"formerRepresentations" => %{
|
||||||
|
"type" => "OrderedCollection",
|
||||||
|
"orderedItems" => [
|
||||||
|
%{"content" => "ZFS is opensource mew mew", "summary" => ""}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
"object" => %{
|
||||||
|
"content" => "ZFS is free software",
|
||||||
|
"formerRepresentations" => %{
|
||||||
|
"orderedItems" => [%{"content" => "ZFS is free software mew mew"}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}} = KeywordPolicy.filter(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "replaces keyword in Updates" do
|
||||||
|
clear_config([:mrf_keyword, :replace], [{"opensource", "free software"}])
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"type" => "Update",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"object" => %{
|
||||||
|
"content" => "ZFS is opensource",
|
||||||
|
"summary" => "",
|
||||||
|
"formerRepresentations" => %{
|
||||||
|
"type" => "OrderedCollection",
|
||||||
|
"orderedItems" => [
|
||||||
|
%{"content" => "ZFS is opensource mew mew", "summary" => ""}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
"object" => %{
|
||||||
|
"content" => "ZFS is free software",
|
||||||
|
"formerRepresentations" => %{
|
||||||
|
"orderedItems" => [%{"content" => "ZFS is free software mew mew"}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}} = KeywordPolicy.filter(message)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1647,5 +1647,22 @@ test "editing a post that copied a remote title with remote emoji should keep th
|
||||||
|
|
||||||
assert edited_note.data["emoji"]["remoteemoji"] == remote_emoji_uri
|
assert edited_note.data["emoji"]["remoteemoji"] == remote_emoji_uri
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "respects MRF" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
|
||||||
|
clear_config([:mrf_keyword, :replace], [{"updated", "mewmew"}])
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{status: "foo1", spoiler_text: "updated 1"})
|
||||||
|
assert Object.normalize(activity).data["summary"] == "mewmew 1"
|
||||||
|
|
||||||
|
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
|
||||||
|
|
||||||
|
updated_object = Object.normalize(updated)
|
||||||
|
assert updated_object.data["content"] == "mewmew 2"
|
||||||
|
assert Map.get(updated_object.data, "summary", "") == ""
|
||||||
|
assert Map.has_key?(updated_object.data, "updated")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue