forked from YokaiRick/akkoma
Fix create processing in direct message disabled
This commit is contained in:
parent
ab34680554
commit
037f881187
12 changed files with 111 additions and 25 deletions
|
@ -6,8 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## Added
|
||||||
|
- Custom options for users to accept/reject private messages
|
||||||
|
- options: everybody, nobody, people\_i\_follow
|
||||||
|
- MRF to reject notes from accounts newer than a given age
|
||||||
|
- this will have the side-effect of rejecting legitimate messages if your
|
||||||
|
post gets boosted outside of your local bubble and people your instance
|
||||||
|
does not know about reply to it.
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
- Support for `streams` public key URIs
|
- Support for `streams` public key URIs
|
||||||
|
- Bookmarks are cleaned up on DB prune now
|
||||||
|
|
||||||
## 2023.04
|
## 2023.04
|
||||||
|
|
||||||
|
|
|
@ -418,6 +418,8 @@
|
||||||
|
|
||||||
config :pleroma, :mrf_follow_bot, follower_nickname: nil
|
config :pleroma, :mrf_follow_bot, follower_nickname: nil
|
||||||
|
|
||||||
|
config :pleroma, :mrf_reject_newly_created_account_notes, age: 86_400
|
||||||
|
|
||||||
config :pleroma, :rich_media,
|
config :pleroma, :rich_media,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
ignore_hosts: [],
|
ignore_hosts: [],
|
||||||
|
|
|
@ -160,7 +160,8 @@ defmodule Pleroma.User do
|
||||||
field(:status_ttl_days, :integer, default: nil)
|
field(:status_ttl_days, :integer, default: nil)
|
||||||
|
|
||||||
field(:accepts_direct_messages_from, Ecto.Enum,
|
field(:accepts_direct_messages_from, Ecto.Enum,
|
||||||
values: [:everybody, :people_i_follow, :nobody]
|
values: [:everybody, :people_i_follow, :nobody],
|
||||||
|
default: :everybody
|
||||||
)
|
)
|
||||||
|
|
||||||
embeds_one(
|
embeds_one(
|
||||||
|
|
|
@ -12,7 +12,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.DirectMessageDisabledPolicy do
|
||||||
@impl true
|
@impl true
|
||||||
def filter(
|
def filter(
|
||||||
%{
|
%{
|
||||||
"type" => "Note",
|
"type" => "Create",
|
||||||
"actor" => actor
|
"actor" => actor
|
||||||
} = activity
|
} = activity
|
||||||
) do
|
) do
|
||||||
|
@ -24,9 +24,13 @@ def filter(
|
||||||
should_filter?(sender, recv)
|
should_filter?(sender, recv)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
{:ok, Map.put(activity, :to, new_to)}
|
{:ok,
|
||||||
|
activity
|
||||||
|
|> Map.put("to", new_to)
|
||||||
|
|> maybe_replace_object_to(new_to)}
|
||||||
else
|
else
|
||||||
_ -> {:ok, activity}
|
_ ->
|
||||||
|
{:ok, activity}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -43,4 +47,10 @@ defp should_filter?(sender, receiver_ap_id) do
|
||||||
_ -> false
|
_ -> false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp maybe_replace_object_to(%{"object" => %{"to" => _}} = activity, to) do
|
||||||
|
Kernel.put_in(activity, ["object", "to"], to)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_replace_object_to(other, _), do: other
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,9 +16,9 @@ def filter(
|
||||||
when type in ["Note", "Create"] do
|
when type in ["Note", "Create"] do
|
||||||
min_age = Pleroma.Config.get([:mrf_reject_newly_created_account_notes, :age])
|
min_age = Pleroma.Config.get([:mrf_reject_newly_created_account_notes, :age])
|
||||||
|
|
||||||
with %User{} = user <- Pleroma.User.get_cached_by_ap_id(actor),
|
with %User{local: false} = user <- Pleroma.User.get_cached_by_ap_id(actor),
|
||||||
true <- Timex.diff(Timex.now(), user.inserted_at, :seconds) < min_age do
|
true <- Timex.diff(Timex.now(), user.inserted_at, :seconds) < min_age do
|
||||||
{:reject, "Account created too recently"}
|
{:reject, "[RejectNewlyCreatedAccountNotesPolicy] Account created too recently"}
|
||||||
else
|
else
|
||||||
_ -> {:ok, activity}
|
_ -> {:ok, activity}
|
||||||
end
|
end
|
||||||
|
|
|
@ -717,8 +717,7 @@ defp update_credentials_request do
|
||||||
"people_i_follow"
|
"people_i_follow"
|
||||||
],
|
],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description:
|
description: "Who to accept DMs from"
|
||||||
"Who to accept DMs from"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
example: %{
|
example: %{
|
||||||
|
|
|
@ -191,7 +191,7 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p
|
||||||
:show_role,
|
:show_role,
|
||||||
:skip_thread_containment,
|
:skip_thread_containment,
|
||||||
:allow_following_move,
|
:allow_following_move,
|
||||||
:also_known_as,
|
:also_known_as
|
||||||
]
|
]
|
||||||
|> Enum.reduce(%{}, fn key, acc ->
|
|> Enum.reduce(%{}, fn key, acc ->
|
||||||
Maps.put_if_present(acc, key, params[key], &{:ok, Params.truthy_param?(&1)})
|
Maps.put_if_present(acc, key, params[key], &{:ok, Params.truthy_param?(&1)})
|
||||||
|
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.DirectMessageDisabledPolicyTest do
|
||||||
describe "strips recipients" do
|
describe "strips recipients" do
|
||||||
test "when the user denies the direct message" do
|
test "when the user denies the direct message" do
|
||||||
sender = insert(:user)
|
sender = insert(:user)
|
||||||
recipient = insert(:user, %{accepts_direct_messages_from_not_followed: false})
|
recipient = insert(:user, %{accepts_direct_messages_from: :nobody})
|
||||||
|
|
||||||
refute User.accepts_direct_messages?(recipient, sender)
|
refute User.accepts_direct_messages?(recipient, sender)
|
||||||
|
|
||||||
|
@ -16,15 +16,15 @@ test "when the user denies the direct message" do
|
||||||
"actor" => sender.ap_id,
|
"actor" => sender.ap_id,
|
||||||
"to" => [recipient.ap_id],
|
"to" => [recipient.ap_id],
|
||||||
"cc" => [],
|
"cc" => [],
|
||||||
"type" => "Note"
|
"type" => "Create"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert {:ok, %{to: []}} = DirectMessageDisabledPolicy.filter(message)
|
assert {:ok, %{"to" => []}} = DirectMessageDisabledPolicy.filter(message)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "when the user does not deny the direct message" do
|
test "when the user does not deny the direct message" do
|
||||||
sender = insert(:user)
|
sender = insert(:user)
|
||||||
recipient = insert(:user, %{accepts_direct_messages_from_not_followed: true})
|
recipient = insert(:user, %{accepts_direct_messages_from: :everybody})
|
||||||
|
|
||||||
assert User.accepts_direct_messages?(recipient, sender)
|
assert User.accepts_direct_messages?(recipient, sender)
|
||||||
|
|
||||||
|
@ -32,11 +32,11 @@ test "when the user does not deny the direct message" do
|
||||||
"actor" => sender.ap_id,
|
"actor" => sender.ap_id,
|
||||||
"to" => [recipient.ap_id],
|
"to" => [recipient.ap_id],
|
||||||
"cc" => [],
|
"cc" => [],
|
||||||
"type" => "Note"
|
"type" => "Create"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert {:ok, message} = DirectMessageDisabledPolicy.filter(message)
|
assert {:ok, message} = DirectMessageDisabledPolicy.filter(message)
|
||||||
assert message.to == [recipient.ap_id]
|
assert message["to"] == [recipient.ap_id]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
defmodule Pleroma.Web.ActivityPub.MRF.RejectNewlyCreatedAccountNotesPolicyTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Web.ActivityPub.MRF.RejectNewlyCreatedAccountNotesPolicy
|
||||||
|
|
||||||
|
describe "reject notes from new accounts" do
|
||||||
|
test "rejects notes from accounts created more recently than `age`" do
|
||||||
|
clear_config([:mrf_reject_newly_created_account_notes, :age], 86_400)
|
||||||
|
sender = insert(:user, %{inserted_at: Timex.now(), local: false})
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"actor" => sender.ap_id,
|
||||||
|
"type" => "Create"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:reject, _} = RejectNewlyCreatedAccountNotesPolicy.filter(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not reject notes from accounts created longer ago" do
|
||||||
|
clear_config([:mrf_reject_newly_created_account_notes, :age], 86_400)
|
||||||
|
a_day_ago = Timex.shift(Timex.now(), days: -1)
|
||||||
|
sender = insert(:user, %{inserted_at: a_day_ago, local: false})
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"actor" => sender.ap_id,
|
||||||
|
"type" => "Create"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:ok, _} = RejectNewlyCreatedAccountNotesPolicy.filter(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not affect local users" do
|
||||||
|
clear_config([:mrf_reject_newly_created_account_notes, :age], 86_400)
|
||||||
|
sender = insert(:user, %{inserted_at: Timex.now(), local: true})
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"actor" => sender.ap_id,
|
||||||
|
"type" => "Create"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:ok, _} = RejectNewlyCreatedAccountNotesPolicy.filter(message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -102,7 +102,13 @@ test "it works as expected with noop policy" do
|
||||||
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.NoOpPolicy])
|
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.NoOpPolicy])
|
||||||
|
|
||||||
expected = %{
|
expected = %{
|
||||||
mrf_policies: ["NoOpPolicy", "HashtagPolicy", "InlineQuotePolicy", "NormalizeMarkup"],
|
mrf_policies: [
|
||||||
|
"NoOpPolicy",
|
||||||
|
"HashtagPolicy",
|
||||||
|
"InlineQuotePolicy",
|
||||||
|
"NormalizeMarkup",
|
||||||
|
"DirectMessageDisabledPolicy"
|
||||||
|
],
|
||||||
mrf_hashtag: %{
|
mrf_hashtag: %{
|
||||||
federated_timeline_removal: [],
|
federated_timeline_removal: [],
|
||||||
reject: [],
|
reject: [],
|
||||||
|
@ -118,7 +124,13 @@ test "it works as expected with mock policy" do
|
||||||
clear_config([:mrf, :policies], [MRFModuleMock])
|
clear_config([:mrf, :policies], [MRFModuleMock])
|
||||||
|
|
||||||
expected = %{
|
expected = %{
|
||||||
mrf_policies: ["MRFModuleMock", "HashtagPolicy", "InlineQuotePolicy", "NormalizeMarkup"],
|
mrf_policies: [
|
||||||
|
"MRFModuleMock",
|
||||||
|
"HashtagPolicy",
|
||||||
|
"InlineQuotePolicy",
|
||||||
|
"NormalizeMarkup",
|
||||||
|
"DirectMessageDisabledPolicy"
|
||||||
|
],
|
||||||
mrf_module_mock: "some config data",
|
mrf_module_mock: "some config data",
|
||||||
mrf_hashtag: %{
|
mrf_hashtag: %{
|
||||||
federated_timeline_removal: [],
|
federated_timeline_removal: [],
|
||||||
|
|
|
@ -316,7 +316,7 @@ test "it strips internal reactions" do
|
||||||
test "it correctly processes messages with non-array to field" do
|
test "it correctly processes messages with non-array to field" do
|
||||||
data =
|
data =
|
||||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||||
|> Poison.decode!()
|
|> Jason.decode!()
|
||||||
|> Map.put("to", "https://www.w3.org/ns/activitystreams#Public")
|
|> Map.put("to", "https://www.w3.org/ns/activitystreams#Public")
|
||||||
|> put_in(["object", "to"], "https://www.w3.org/ns/activitystreams#Public")
|
|> put_in(["object", "to"], "https://www.w3.org/ns/activitystreams#Public")
|
||||||
|
|
||||||
|
@ -333,7 +333,7 @@ test "it correctly processes messages with non-array to field" do
|
||||||
test "it correctly processes messages with non-array cc field" do
|
test "it correctly processes messages with non-array cc field" do
|
||||||
data =
|
data =
|
||||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||||
|> Poison.decode!()
|
|> Jason.decode!()
|
||||||
|> Map.put("cc", "http://mastodon.example.org/users/admin/followers")
|
|> Map.put("cc", "http://mastodon.example.org/users/admin/followers")
|
||||||
|> put_in(["object", "cc"], "http://mastodon.example.org/users/admin/followers")
|
|> put_in(["object", "cc"], "http://mastodon.example.org/users/admin/followers")
|
||||||
|
|
||||||
|
@ -346,7 +346,7 @@ test "it correctly processes messages with non-array cc field" do
|
||||||
test "it correctly processes messages with weirdness in address fields" do
|
test "it correctly processes messages with weirdness in address fields" do
|
||||||
data =
|
data =
|
||||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||||
|> Poison.decode!()
|
|> Jason.decode!()
|
||||||
|> Map.put("cc", ["http://mastodon.example.org/users/admin/followers", ["¿"]])
|
|> Map.put("cc", ["http://mastodon.example.org/users/admin/followers", ["¿"]])
|
||||||
|> put_in(["object", "cc"], ["http://mastodon.example.org/users/admin/followers", ["¿"]])
|
|> put_in(["object", "cc"], ["http://mastodon.example.org/users/admin/followers", ["¿"]])
|
||||||
|
|
||||||
|
@ -412,7 +412,7 @@ test "does NOT schedule background fetching of `replies` beyond max thread depth
|
||||||
|
|
||||||
activity =
|
activity =
|
||||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||||
|> Poison.decode!()
|
|> Jason.decode!()
|
||||||
|> Kernel.put_in(["object", "replies"], replies)
|
|> Kernel.put_in(["object", "replies"], replies)
|
||||||
|
|
||||||
%{activity: activity}
|
%{activity: activity}
|
||||||
|
|
|
@ -735,7 +735,9 @@ test "actor_type field has a higher priority than bot", %{conn: conn} do
|
||||||
test "changing to :everybody", %{conn: conn} do
|
test "changing to :everybody", %{conn: conn} do
|
||||||
account =
|
account =
|
||||||
conn
|
conn
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{accepts_direct_messages_from: "everybody"})
|
|> patch("/api/v1/accounts/update_credentials", %{
|
||||||
|
accepts_direct_messages_from: "everybody"
|
||||||
|
})
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert account["accepts_direct_messages_from"]
|
assert account["accepts_direct_messages_from"]
|
||||||
|
@ -757,17 +759,23 @@ test "changing to :nobody", %{conn: conn} do
|
||||||
test "changing to :people_i_follow", %{conn: conn} do
|
test "changing to :people_i_follow", %{conn: conn} do
|
||||||
account =
|
account =
|
||||||
conn
|
conn
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{accepts_direct_messages_from: "people_i_follow"})
|
|> patch("/api/v1/accounts/update_credentials", %{
|
||||||
|
accepts_direct_messages_from: "people_i_follow"
|
||||||
|
})
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert account["accepts_direct_messages_from"]
|
assert account["accepts_direct_messages_from"]
|
||||||
assert account["accepts_direct_messages_from"] == "people_i_follow"
|
assert account["accepts_direct_messages_from"] == "people_i_follow"
|
||||||
assert Pleroma.User.get_by_ap_id(account["url"]).accepts_direct_messages_from == :people_i_follow
|
|
||||||
|
assert Pleroma.User.get_by_ap_id(account["url"]).accepts_direct_messages_from ==
|
||||||
|
:people_i_follow
|
||||||
end
|
end
|
||||||
|
|
||||||
test "changing to an unsupported value", %{conn: conn} do
|
test "changing to an unsupported value", %{conn: conn} do
|
||||||
conn
|
conn
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{accepts_direct_messages_from: "unsupported"})
|
|> patch("/api/v1/accounts/update_credentials", %{
|
||||||
|
accepts_direct_messages_from: "unsupported"
|
||||||
|
})
|
||||||
|> json_response(400)
|
|> json_response(400)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue