diff --git a/CHANGELOG.md b/CHANGELOG.md index bb5b8fa04..207c99d67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## 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 - Support for `streams` public key URIs +- Bookmarks are cleaned up on DB prune now ## 2023.04 diff --git a/config/config.exs b/config/config.exs index 95c576385..ca397a8fd 100644 --- a/config/config.exs +++ b/config/config.exs @@ -418,6 +418,8 @@ config :pleroma, :mrf_follow_bot, follower_nickname: nil +config :pleroma, :mrf_reject_newly_created_account_notes, age: 86_400 + config :pleroma, :rich_media, enabled: true, ignore_hosts: [], diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 371dea85b..273bdf337 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -160,7 +160,8 @@ defmodule Pleroma.User do field(:status_ttl_days, :integer, default: nil) field(:accepts_direct_messages_from, Ecto.Enum, - values: [:everybody, :people_i_follow, :nobody] + values: [:everybody, :people_i_follow, :nobody], + default: :everybody ) embeds_one( diff --git a/lib/pleroma/web/activity_pub/mrf/direct_message_disabled_policy.ex b/lib/pleroma/web/activity_pub/mrf/direct_message_disabled_policy.ex index 27a59c4f1..ec6fa70e2 100644 --- a/lib/pleroma/web/activity_pub/mrf/direct_message_disabled_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/direct_message_disabled_policy.ex @@ -12,7 +12,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.DirectMessageDisabledPolicy do @impl true def filter( %{ - "type" => "Note", + "type" => "Create", "actor" => actor } = activity ) do @@ -24,9 +24,13 @@ def filter( should_filter?(sender, recv) end) - {:ok, Map.put(activity, :to, new_to)} + {:ok, + activity + |> Map.put("to", new_to) + |> maybe_replace_object_to(new_to)} else - _ -> {:ok, activity} + _ -> + {:ok, activity} end end @@ -43,4 +47,10 @@ defp should_filter?(sender, receiver_ap_id) do _ -> false 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 diff --git a/lib/pleroma/web/activity_pub/mrf/reject_newly_created_account_note_policy.ex b/lib/pleroma/web/activity_pub/mrf/reject_newly_created_account_note_policy.ex index 5481f38de..01a846831 100644 --- a/lib/pleroma/web/activity_pub/mrf/reject_newly_created_account_note_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/reject_newly_created_account_note_policy.ex @@ -16,9 +16,9 @@ def filter( when type in ["Note", "Create"] do 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 - {:reject, "Account created too recently"} + {:reject, "[RejectNewlyCreatedAccountNotesPolicy] Account created too recently"} else _ -> {:ok, activity} end diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 8a46ec32a..8a6851d52 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -717,8 +717,7 @@ defp update_credentials_request do "people_i_follow" ], nullable: true, - description: - "Who to accept DMs from" + description: "Who to accept DMs from" } }, example: %{ diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 16b3e36a8..057af762a 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -191,7 +191,7 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p :show_role, :skip_thread_containment, :allow_following_move, - :also_known_as, + :also_known_as ] |> Enum.reduce(%{}, fn key, acc -> Maps.put_if_present(acc, key, params[key], &{:ok, Params.truthy_param?(&1)}) diff --git a/test/pleroma/web/activity_pub/mrf/direct_message_disabled_policy_test.exs b/test/pleroma/web/activity_pub/mrf/direct_message_disabled_policy_test.exs index 50ed36a0b..072fe0576 100644 --- a/test/pleroma/web/activity_pub/mrf/direct_message_disabled_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/direct_message_disabled_policy_test.exs @@ -8,7 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.DirectMessageDisabledPolicyTest do describe "strips recipients" do test "when the user denies the direct message" do 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) @@ -16,15 +16,15 @@ test "when the user denies the direct message" do "actor" => sender.ap_id, "to" => [recipient.ap_id], "cc" => [], - "type" => "Note" + "type" => "Create" } - assert {:ok, %{to: []}} = DirectMessageDisabledPolicy.filter(message) + assert {:ok, %{"to" => []}} = DirectMessageDisabledPolicy.filter(message) end test "when the user does not deny the direct message" do 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) @@ -32,11 +32,11 @@ test "when the user does not deny the direct message" do "actor" => sender.ap_id, "to" => [recipient.ap_id], "cc" => [], - "type" => "Note" + "type" => "Create" } assert {:ok, message} = DirectMessageDisabledPolicy.filter(message) - assert message.to == [recipient.ap_id] + assert message["to"] == [recipient.ap_id] end end end diff --git a/test/pleroma/web/activity_pub/mrf/reject_newly_created_account_note_policy_test.exs b/test/pleroma/web/activity_pub/mrf/reject_newly_created_account_note_policy_test.exs new file mode 100644 index 000000000..2fc65e6d6 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/reject_newly_created_account_note_policy_test.exs @@ -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 diff --git a/test/pleroma/web/activity_pub/mrf_test.exs b/test/pleroma/web/activity_pub/mrf_test.exs index 7359398fe..51af672cd 100644 --- a/test/pleroma/web/activity_pub/mrf_test.exs +++ b/test/pleroma/web/activity_pub/mrf_test.exs @@ -102,7 +102,13 @@ test "it works as expected with noop policy" do clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.NoOpPolicy]) expected = %{ - mrf_policies: ["NoOpPolicy", "HashtagPolicy", "InlineQuotePolicy", "NormalizeMarkup"], + mrf_policies: [ + "NoOpPolicy", + "HashtagPolicy", + "InlineQuotePolicy", + "NormalizeMarkup", + "DirectMessageDisabledPolicy" + ], mrf_hashtag: %{ federated_timeline_removal: [], reject: [], @@ -118,7 +124,13 @@ test "it works as expected with mock policy" do clear_config([:mrf, :policies], [MRFModuleMock]) expected = %{ - mrf_policies: ["MRFModuleMock", "HashtagPolicy", "InlineQuotePolicy", "NormalizeMarkup"], + mrf_policies: [ + "MRFModuleMock", + "HashtagPolicy", + "InlineQuotePolicy", + "NormalizeMarkup", + "DirectMessageDisabledPolicy" + ], mrf_module_mock: "some config data", mrf_hashtag: %{ federated_timeline_removal: [], diff --git a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs index 002042802..9faee7aa3 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs @@ -316,7 +316,7 @@ test "it strips internal reactions" do test "it correctly processes messages with non-array to field" do data = File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() + |> Jason.decode!() |> Map.put("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 data = File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() + |> Jason.decode!() |> Map.put("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 data = File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() + |> Jason.decode!() |> Map.put("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 = File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() + |> Jason.decode!() |> Kernel.put_in(["object", "replies"], replies) %{activity: activity} diff --git a/test/pleroma/web/mastodon_api/update_credentials_test.exs b/test/pleroma/web/mastodon_api/update_credentials_test.exs index 925ea25aa..70566f5d0 100644 --- a/test/pleroma/web/mastodon_api/update_credentials_test.exs +++ b/test/pleroma/web/mastodon_api/update_credentials_test.exs @@ -735,7 +735,9 @@ test "actor_type field has a higher priority than bot", %{conn: conn} do test "changing to :everybody", %{conn: conn} do account = 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) 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 account = 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) assert account["accepts_direct_messages_from"] 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 test "changing to an unsupported value", %{conn: conn} do 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) end end