From afa8b469ed0a71247f27efec08d6eeac24b6674f Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 1 Jul 2020 21:12:59 -0500 Subject: [PATCH 01/34] Allow restricting public timeline by instance --- lib/pleroma/web/activity_pub/activity_pub.ex | 12 ++---------- .../web/api_spec/operations/timeline_operation.ex | 10 ++++++++++ .../mastodon_api/controllers/timeline_controller.ex | 1 + .../controllers/timeline_controller_test.exs | 12 ++++++++++++ 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index bc7b5d95a..9ce2b04dd 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -927,16 +927,8 @@ defp restrict_muted_reblogs(query, %{muting_user: %User{} = user} = opts) do defp restrict_muted_reblogs(query, _), do: query - defp restrict_instance(query, %{instance: instance}) do - users = - from( - u in User, - select: u.ap_id, - where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}") - ) - |> Repo.all() - - from(activity in query, where: activity.actor in ^users) + defp restrict_instance(query, %{instance: instance}) when is_binary(instance) do + from(activity in query, where: ilike(activity.actor, ^"%://#{instance}/%")) end defp restrict_instance(query, _), do: query diff --git a/lib/pleroma/web/api_spec/operations/timeline_operation.ex b/lib/pleroma/web/api_spec/operations/timeline_operation.ex index 8e19bace7..83cdbad69 100644 --- a/lib/pleroma/web/api_spec/operations/timeline_operation.ex +++ b/lib/pleroma/web/api_spec/operations/timeline_operation.ex @@ -59,6 +59,7 @@ def public_operation do security: [%{"oAuth" => ["read:statuses"]}], parameters: [ local_param(), + instance_param(), only_media_param(), with_muted_param(), exclude_visibilities_param(), @@ -158,6 +159,15 @@ defp local_param do ) end + defp instance_param do + Operation.parameter( + :instance, + :query, + %Schema{type: :string}, + "Show only statuses from the given domain" + ) + end + defp with_muted_param do Operation.parameter(:with_muted, :query, BooleanLike, "Includeactivities by muted users") end diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index ab7b1d6aa..7dccc0005 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -110,6 +110,7 @@ def public(%{assigns: %{user: user}} = conn, params) do |> Map.put(:blocking_user, user) |> Map.put(:muting_user, user) |> Map.put(:reply_filtering_user, user) + |> Map.put(:instance, params[:instance]) |> ActivityPub.fetch_public_activities() conn diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs index 50e0d783d..6acd512c7 100644 --- a/test/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs @@ -140,6 +140,18 @@ test "doesn't return replies if follow is posting with users from blocked domain activities = json_response_and_validate_schema(res_conn, 200) [%{"id" => ^activity_id}] = activities end + + test "can be filtered by instance", %{conn: conn} do + user = insert(:user, ap_id: "https://lain.com/users/lain") + insert(:note_activity, local: false) + insert(:note_activity, local: false) + + {:ok, _} = CommonAPI.post(user, %{status: "test"}) + + conn = get(conn, "/api/v1/timelines/public?instance=lain.com") + + assert length(json_response_and_validate_schema(conn, :ok)) == 1 + end end defp local_and_remote_activities do From e9cff69bceb5cfb14a13ef09d1d7ab1079028a58 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 2 Aug 2020 12:24:40 -0500 Subject: [PATCH 02/34] Add TagPolicy as default MRF, #2010 --- config/config.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.exs b/config/config.exs index 134dccf49..68a050315 100644 --- a/config/config.exs +++ b/config/config.exs @@ -719,7 +719,7 @@ config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: false config :pleroma, :mrf, - policies: Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy, + policies: [Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy, Pleroma.Web.ActivityPub.MRF.TagPolicy], transparency: true, transparency_exclusions: [] From ad9c925efb77287316f5dbac26f6a1b16662910a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 5 Aug 2020 13:08:13 -0500 Subject: [PATCH 03/34] Speed up instance timeline query --- lib/pleroma/web/activity_pub/activity_pub.ex | 5 ++++- test/web/admin_api/controllers/admin_api_controller_test.exs | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 9ce2b04dd..76fc9c3ee 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -928,7 +928,10 @@ defp restrict_muted_reblogs(query, %{muting_user: %User{} = user} = opts) do defp restrict_muted_reblogs(query, _), do: query defp restrict_instance(query, %{instance: instance}) when is_binary(instance) do - from(activity in query, where: ilike(activity.actor, ^"%://#{instance}/%")) + from( + activity in query, + where: fragment("split_part(actor::text, '/'::text, 3) = ?", ^instance) + ) end defp restrict_instance(query, _), do: query diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs index da91cd552..26194eb81 100644 --- a/test/web/admin_api/controllers/admin_api_controller_test.exs +++ b/test/web/admin_api/controllers/admin_api_controller_test.exs @@ -1647,8 +1647,8 @@ test "sets password_reset_pending to true", %{conn: conn} do describe "instances" do test "GET /instances/:instance/statuses", %{conn: conn} do - user = insert(:user, local: false, nickname: "archaeme@archae.me") - user2 = insert(:user, local: false, nickname: "test@test.com") + user = insert(:user, local: false, ap_id: "https://archae.me/users/archaeme") + user2 = insert(:user, local: false, ap_id: "https://test.com/users/test") insert_pair(:note_activity, user: user) activity = insert(:note_activity, user: user2) From 24ce9c011caf7401fb261c7df4196b2ef9ba3d90 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 5 Aug 2020 19:33:51 +0000 Subject: [PATCH 04/34] Apply 1 suggestion(s) to 1 file(s) --- lib/pleroma/web/api_spec/operations/timeline_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/api_spec/operations/timeline_operation.ex b/lib/pleroma/web/api_spec/operations/timeline_operation.ex index 83cdbad69..95720df9f 100644 --- a/lib/pleroma/web/api_spec/operations/timeline_operation.ex +++ b/lib/pleroma/web/api_spec/operations/timeline_operation.ex @@ -169,7 +169,7 @@ defp instance_param do end defp with_muted_param do - Operation.parameter(:with_muted, :query, BooleanLike, "Includeactivities by muted users") + Operation.parameter(:with_muted, :query, BooleanLike, "Include activities by muted users") end defp exclude_visibilities_param do From c56e3d4f3bfb090d19bdbe93dac6cede7616cc4d Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Tue, 8 Sep 2020 13:26:44 +0300 Subject: [PATCH 05/34] Add expires_in param for account mutes --- config/config.exs | 5 ++- lib/pleroma/user.ex | 45 ++++++++++--------- .../api_spec/operations/account_operation.ex | 15 ++++++- .../controllers/account_controller.ex | 2 +- lib/pleroma/workers/mute_expire_worker.ex | 22 +++++++++ test/notification_test.exs | 4 +- test/user_test.exs | 2 +- .../notification_controller_test.exs | 2 +- .../mastodon_api/views/account_view_test.exs | 2 +- 9 files changed, 69 insertions(+), 30 deletions(-) create mode 100644 lib/pleroma/workers/mute_expire_worker.ex diff --git a/config/config.exs b/config/config.exs index ed37b93c0..0649f3078 100644 --- a/config/config.exs +++ b/config/config.exs @@ -541,7 +541,8 @@ background: 5, remote_fetcher: 2, attachments_cleanup: 5, - new_users_digest: 1 + new_users_digest: 1, + mute_expire: 5 ], plugins: [Oban.Plugins.Pruner], crontab: [ @@ -672,7 +673,7 @@ # With no frontend configuration, the bundled files from the `static` directory will # be used. # -# config :pleroma, :frontends, +# config :pleroma, :frontends, # primary: %{"name" => "pleroma-fe", "ref" => "develop"}, # admin: %{"name" => "admin-fe", "ref" => "stable"}, # available: %{...} diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 94c96de8d..040db8d80 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1356,14 +1356,34 @@ def get_recipients_from_activity(%Activity{recipients: to, actor: actor}) do |> Repo.all() end - @spec mute(User.t(), User.t(), boolean()) :: + @spec mute(User.t(), User.t(), map()) :: {:ok, list(UserRelationship.t())} | {:error, String.t()} - def mute(%User{} = muter, %User{} = mutee, notifications? \\ true) do - add_to_mutes(muter, mutee, notifications?) + def mute(%User{} = muter, %User{} = mutee, params \\ %{}) do + notifications? = Map.get(params, :notifications, true) + expires_in = Map.get(params, :expires_in, 0) + + with {:ok, user_mute} <- UserRelationship.create_mute(muter, mutee), + {:ok, user_notification_mute} <- + (notifications? && UserRelationship.create_notification_mute(muter, mutee)) || + {:ok, nil} do + with seconds when seconds > 0 <- expires_in do + Pleroma.Workers.MuteExpireWorker.enqueue( + "unmute", + %{"muter" => muter.id, "mutee" => mutee.id}, + schedule_in: expires_in + ) + end + + {:ok, Enum.filter([user_mute, user_notification_mute], & &1)} + end end def unmute(%User{} = muter, %User{} = mutee) do - remove_from_mutes(muter, mutee) + with {:ok, user_mute} <- UserRelationship.delete_mute(muter, mutee), + {:ok, user_notification_mute} <- + UserRelationship.delete_notification_mute(muter, mutee) do + {:ok, [user_mute, user_notification_mute]} + end end def subscribe(%User{} = subscriber, %User{} = target) do @@ -2379,23 +2399,6 @@ defp remove_from_block(%User{} = user, %User{} = blocked) do UserRelationship.delete_block(user, blocked) end - defp add_to_mutes(%User{} = user, %User{} = muted_user, notifications?) do - with {:ok, user_mute} <- UserRelationship.create_mute(user, muted_user), - {:ok, user_notification_mute} <- - (notifications? && UserRelationship.create_notification_mute(user, muted_user)) || - {:ok, nil} do - {:ok, Enum.filter([user_mute, user_notification_mute], & &1)} - end - end - - defp remove_from_mutes(user, %User{} = muted_user) do - with {:ok, user_mute} <- UserRelationship.delete_mute(user, muted_user), - {:ok, user_notification_mute} <- - UserRelationship.delete_notification_mute(user, muted_user) do - {:ok, [user_mute, user_notification_mute]} - end - end - def set_invisible(user, invisible) do params = %{invisible: invisible} diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index aaebc9b5c..de715a077 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -262,6 +262,12 @@ def mute_operation do :query, %Schema{allOf: [BooleanLike], default: true}, "Mute notifications in addition to statuses? Defaults to `true`." + ), + Operation.parameter( + :expires_in, + :query, + %Schema{type: :integer, default: 0}, + "Expire the mute in `expires_in` seconds. Default 0 for infinity" ) ], responses: %{ @@ -718,10 +724,17 @@ defp mute_request do nullable: true, description: "Mute notifications in addition to statuses? Defaults to true.", default: true + }, + expires_in: %Schema{ + type: :integer, + nullable: true, + description: "Expire the mute in `expires_in` seconds. Default 0 for infinity", + default: 0 } }, example: %{ - "notifications" => true + "notifications" => true, + "expires_in" => 86_400 } } end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 95d8452df..ca1a79f5e 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -394,7 +394,7 @@ def unfollow(%{assigns: %{user: follower, account: followed}} = conn, _params) d @doc "POST /api/v1/accounts/:id/mute" def mute(%{assigns: %{user: muter, account: muted}, body_params: params} = conn, _params) do - with {:ok, _user_relationships} <- User.mute(muter, muted, params.notifications) do + with {:ok, _user_relationships} <- User.mute(muter, muted, params) do render(conn, "relationship.json", user: muter, target: muted) else {:error, message} -> json_response(conn, :forbidden, %{error: message}) diff --git a/lib/pleroma/workers/mute_expire_worker.ex b/lib/pleroma/workers/mute_expire_worker.ex new file mode 100644 index 000000000..b8ec939a9 --- /dev/null +++ b/lib/pleroma/workers/mute_expire_worker.ex @@ -0,0 +1,22 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.MuteExpireWorker do + use Pleroma.Workers.WorkerHelper, queue: "mute_expire" + + require Logger + + @impl Oban.Worker + def perform(%Job{args: %{"op" => "unmute", "muter" => muter_id, "mutee" => mutee_id}}) do + muter = Pleroma.User.get_by_id(muter_id) + mutee = Pleroma.User.get_by_id(mutee_id) + Pleroma.User.unmute(muter, mutee) + :ok + end + + def perform(any) do + Logger.error("Got call to perform(#{inspect(any)})") + :ok + end +end diff --git a/test/notification_test.exs b/test/notification_test.exs index a09b08675..ffd737969 100644 --- a/test/notification_test.exs +++ b/test/notification_test.exs @@ -227,7 +227,7 @@ test "notification created if user is muted without notifications" do muter = insert(:user) muted = insert(:user) - {:ok, _user_relationships} = User.mute(muter, muted, false) + {:ok, _user_relationships} = User.mute(muter, muted, %{notifications: false}) {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"}) @@ -1013,7 +1013,7 @@ test "move activity generates a notification" do test "it returns notifications for muted user without notifications", %{user: user} do muted = insert(:user) - {:ok, _user_relationships} = User.mute(user, muted, false) + {:ok, _user_relationships} = User.mute(user, muted, %{notifications: false}) {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"}) diff --git a/test/user_test.exs b/test/user_test.exs index 50f72549e..b23e36be3 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -981,7 +981,7 @@ test "it mutes user without notifications" do refute User.mutes?(user, muted_user) refute User.muted_notifications?(user, muted_user) - {:ok, _user_relationships} = User.mute(user, muted_user, false) + {:ok, _user_relationships} = User.mute(user, muted_user, %{notifications: false}) assert User.mutes?(user, muted_user) refute User.muted_notifications?(user, muted_user) diff --git a/test/web/mastodon_api/controllers/notification_controller_test.exs b/test/web/mastodon_api/controllers/notification_controller_test.exs index 70ef0e8b5..5fd518c60 100644 --- a/test/web/mastodon_api/controllers/notification_controller_test.exs +++ b/test/web/mastodon_api/controllers/notification_controller_test.exs @@ -502,7 +502,7 @@ test "see notifications after muting user without notifications" do assert length(json_response_and_validate_schema(ret_conn, 200)) == 1 - {:ok, _user_relationships} = User.mute(user, user2, false) + {:ok, _user_relationships} = User.mute(user, user2, %{notifications: false}) conn = get(conn, "/api/v1/notifications") diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs index 8f37efa3c..c34cbcfc1 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/web/mastodon_api/views/account_view_test.exs @@ -277,7 +277,7 @@ test "represent a relationship for the following and followed user" do {:ok, user} = User.follow(user, other_user) {:ok, other_user} = User.follow(other_user, user) {:ok, _subscription} = User.subscribe(user, other_user) - {:ok, _user_relationships} = User.mute(user, other_user, true) + {:ok, _user_relationships} = User.mute(user, other_user, %{notifications: true}) {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, other_user) expected = From f6b250fb8d13f6788c1ecc6c1287e76febbfd888 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Tue, 8 Sep 2020 14:11:00 +0300 Subject: [PATCH 06/34] Add test for expiring mutes --- test/user_test.exs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/user_test.exs b/test/user_test.exs index b23e36be3..83c017ec5 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -963,6 +963,19 @@ test "it mutes people" do assert User.muted_notifications?(user, muted_user) end + test "expiring" do + user = insert(:user) + muted_user = insert(:user) + + {:ok, _user_relationships} = User.mute(user, muted_user, %{expires_in: 60}) + assert User.mutes?(user, muted_user) + + assert_enqueued( + worker: Pleroma.Workers.MuteExpireWorker, + args: %{"op" => "unmute", "muter" => user.id, "mutee" => muted_user.id} + ) + end + test "it unmutes users" do user = insert(:user) muted_user = insert(:user) From e3f845b24363cd867ab85b7297f2d34bfa16b13f Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Tue, 8 Sep 2020 15:13:50 +0300 Subject: [PATCH 07/34] Add expiring mutes for activities --- lib/pleroma/user.ex | 6 ++--- .../api_spec/operations/status_operation.ex | 22 ++++++++++++++++++- lib/pleroma/web/common_api/common_api.ex | 12 +++++++++- lib/pleroma/workers/mute_expire_worker.ex | 10 ++++++--- test/user_test.exs | 12 ++++++++-- test/web/common_api/common_api_test.exs | 18 +++++++++++++++ 6 files changed, 70 insertions(+), 10 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 040db8d80..46e03553c 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1366,10 +1366,10 @@ def mute(%User{} = muter, %User{} = mutee, params \\ %{}) do {:ok, user_notification_mute} <- (notifications? && UserRelationship.create_notification_mute(muter, mutee)) || {:ok, nil} do - with seconds when seconds > 0 <- expires_in do + if expires_in > 0 do Pleroma.Workers.MuteExpireWorker.enqueue( - "unmute", - %{"muter" => muter.id, "mutee" => mutee.id}, + "unmute_user", + %{"muter_id" => muter.id, "mutee_id" => mutee.id}, schedule_in: expires_in ) end diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index 5bd4619d5..6589a16f3 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -223,7 +223,27 @@ def mute_conversation_operation do security: [%{"oAuth" => ["write:mutes"]}], description: "Do not receive notifications for the thread that this status is part of.", operationId: "StatusController.mute_conversation", - parameters: [id_param()], + requestBody: + request_body("Parameters", %Schema{ + type: :object, + properties: %{ + expires_in: %Schema{ + type: :integer, + nullable: true, + description: "Expire the mute in `expires_in` seconds. Default 0 for infinity", + default: 0 + } + } + }), + parameters: [ + id_param(), + Operation.parameter( + :expires_in, + :query, + %Schema{type: :integer, default: 0}, + "Expire the mute in `expires_in` seconds. Default 0 for infinity" + ) + ], responses: %{ 200 => status_response(), 400 => Operation.response("Error", "application/json", ApiError) diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 4ab533658..b217c4d10 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -451,9 +451,19 @@ def unpin(id, user) do end end - def add_mute(user, activity) do + def add_mute(user, activity, params \\ %{}) do + expires_in = Map.get(params, :expires_in, 0) + with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]), _ <- Pleroma.Notification.mark_context_as_read(user, activity.data["context"]) do + if expires_in > 0 do + Pleroma.Workers.MuteExpireWorker.enqueue( + "unmute_conversation", + %{"user_id" => user.id, "activity_id" => activity.id}, + schedule_in: expires_in + ) + end + {:ok, activity} else {:error, _} -> {:error, dgettext("errors", "conversation is already muted")} diff --git a/lib/pleroma/workers/mute_expire_worker.ex b/lib/pleroma/workers/mute_expire_worker.ex index b8ec939a9..622fdbadd 100644 --- a/lib/pleroma/workers/mute_expire_worker.ex +++ b/lib/pleroma/workers/mute_expire_worker.ex @@ -8,15 +8,19 @@ defmodule Pleroma.Workers.MuteExpireWorker do require Logger @impl Oban.Worker - def perform(%Job{args: %{"op" => "unmute", "muter" => muter_id, "mutee" => mutee_id}}) do + def perform(%Job{args: %{"op" => "unmute_user", "muter_id" => muter_id, "mutee_id" => mutee_id}}) do muter = Pleroma.User.get_by_id(muter_id) mutee = Pleroma.User.get_by_id(mutee_id) Pleroma.User.unmute(muter, mutee) :ok end - def perform(any) do - Logger.error("Got call to perform(#{inspect(any)})") + def perform(%Job{ + args: %{"op" => "unmute_conversation", "user_id" => user_id, "activity_id" => activity_id} + }) do + user = Pleroma.User.get_by_id(user_id) + activity = Pleroma.Activity.get_by_id(activity_id) + Pleroma.Web.CommonAPI.remove_mute(user, activity) :ok end end diff --git a/test/user_test.exs b/test/user_test.exs index 83c017ec5..d49afb35a 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -970,10 +970,18 @@ test "expiring" do {:ok, _user_relationships} = User.mute(user, muted_user, %{expires_in: 60}) assert User.mutes?(user, muted_user) + worker = Pleroma.Workers.MuteExpireWorker + args = %{"op" => "unmute_user", "muter_id" => user.id, "mutee_id" => muted_user.id} + assert_enqueued( - worker: Pleroma.Workers.MuteExpireWorker, - args: %{"op" => "unmute", "muter" => user.id, "mutee" => muted_user.id} + worker: worker, + args: args ) + + assert :ok = perform_job(worker, args) + + refute User.mutes?(user, muted_user) + refute User.muted_notifications?(user, muted_user) end test "it unmutes users" do diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 800db9a20..7ceb7ec7f 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -3,7 +3,9 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.CommonAPITest do + use Oban.Testing, repo: Pleroma.Repo use Pleroma.DataCase + alias Pleroma.Activity alias Pleroma.Chat alias Pleroma.Conversation.Participation @@ -878,6 +880,22 @@ test "add mute", %{user: user, activity: activity} do assert CommonAPI.thread_muted?(user, activity) end + test "add expiring mute", %{user: user, activity: activity} do + {:ok, _} = CommonAPI.add_mute(user, activity, %{expires_in: 60}) + assert CommonAPI.thread_muted?(user, activity) + + worker = Pleroma.Workers.MuteExpireWorker + args = %{"op" => "unmute_conversation", "user_id" => user.id, "activity_id" => activity.id} + + assert_enqueued( + worker: worker, + args: args + ) + + assert :ok = perform_job(worker, args) + refute CommonAPI.thread_muted?(user, activity) + end + test "remove mute", %{user: user, activity: activity} do CommonAPI.add_mute(user, activity) {:ok, _} = CommonAPI.remove_mute(user, activity) From 91b9985e1c0ef1766eb1705364e1aebe69d9b9bd Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Tue, 8 Sep 2020 15:26:06 +0300 Subject: [PATCH 08/34] Pass expires_in param from status controller --- lib/pleroma/web/mastodon_api/controllers/status_controller.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index ecfa38489..da14c0b6c 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -285,9 +285,9 @@ def unbookmark(%{assigns: %{user: user}} = conn, %{id: id}) do end @doc "POST /api/v1/statuses/:id/mute" - def mute_conversation(%{assigns: %{user: user}} = conn, %{id: id}) do + def mute_conversation(%{assigns: %{user: user}, body_params: params} = conn, %{id: id}) do with %Activity{} = activity <- Activity.get_by_id(id), - {:ok, activity} <- CommonAPI.add_mute(user, activity) do + {:ok, activity} <- CommonAPI.add_mute(user, activity, params) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) end end From de2499e54b33a1746e5f6a5b79f1422d31c11570 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Wed, 9 Sep 2020 09:52:07 +0300 Subject: [PATCH 09/34] don't run update in tests --- lib/pleroma/stats.ex | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/lib/pleroma/stats.ex b/lib/pleroma/stats.ex index e5c9c668b..48afe901e 100644 --- a/lib/pleroma/stats.ex +++ b/lib/pleroma/stats.ex @@ -23,7 +23,6 @@ def start_link(_) do @impl true def init(_args) do - if Pleroma.Config.get(:env) == :test, do: :ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo) {:ok, nil, {:continue, :calculate_stats}} end @@ -32,11 +31,6 @@ def force_update do GenServer.call(__MODULE__, :force_update) end - @doc "Performs collect stats" - def do_collect do - GenServer.cast(__MODULE__, :run_update) - end - @doc "Returns stats data" @spec get_stats() :: %{ domain_count: non_neg_integer(), @@ -111,7 +105,11 @@ def get_status_visibility_count(instance \\ nil) do @impl true def handle_continue(:calculate_stats, _) do stats = calculate_stat_data() - Process.send_after(self(), :run_update, @interval) + + unless Pleroma.Config.get(:env) == :test do + Process.send_after(self(), :run_update, @interval) + end + {:noreply, stats} end @@ -126,13 +124,6 @@ def handle_call(:get_state, _from, state) do {:reply, state, state} end - @impl true - def handle_cast(:run_update, _state) do - new_stats = calculate_stat_data() - - {:noreply, new_stats} - end - @impl true def handle_info(:run_update, _) do new_stats = calculate_stat_data() From 527afb813af6c64337d02ddf1a2f159fe557acbc Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sun, 13 Sep 2020 12:23:45 +0300 Subject: [PATCH 10/34] Remove unused require --- lib/pleroma/workers/mute_expire_worker.ex | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/pleroma/workers/mute_expire_worker.ex b/lib/pleroma/workers/mute_expire_worker.ex index 622fdbadd..c8b44894e 100644 --- a/lib/pleroma/workers/mute_expire_worker.ex +++ b/lib/pleroma/workers/mute_expire_worker.ex @@ -5,8 +5,6 @@ defmodule Pleroma.Workers.MuteExpireWorker do use Pleroma.Workers.WorkerHelper, queue: "mute_expire" - require Logger - @impl Oban.Worker def perform(%Job{args: %{"op" => "unmute_user", "muter_id" => muter_id, "mutee_id" => mutee_id}}) do muter = Pleroma.User.get_by_id(muter_id) From 28d0986f839651df7d305da8932f7b5c48a4fbfb Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sun, 20 Sep 2020 20:51:20 +0300 Subject: [PATCH 11/34] Refactor mutes removing in CommonAPI and User --- lib/pleroma/user.ex | 14 ++++++++++++++ lib/pleroma/web/common_api/common_api.ex | 18 +++++++++++++++++- lib/pleroma/workers/mute_expire_worker.ex | 8 ++------ test/user_test.exs | 11 +++++++++++ test/web/common_api/common_api_test.exs | 6 ++++++ 5 files changed, 50 insertions(+), 7 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 83eb4d5ff..83e89a12c 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1385,6 +1385,20 @@ def unmute(%User{} = muter, %User{} = mutee) do end end + def unmute(muter_id, mutee_id) do + with {:muter, %User{} = muter} <- {:muter, User.get_by_id(muter_id)}, + {:mutee, %User{} = mutee} <- {:mutee, User.get_by_id(mutee_id)} do + unmute(muter, mutee) + else + {who, result} = error -> + Logger.warn( + "User.unmute/2 failed. #{who}: #{result}, muter_id: #{muter_id}, mutee_id: #{mutee_id}" + ) + + {:error, error} + end + end + def subscribe(%User{} = subscriber, %User{} = target) do deny_follow_blocked = Config.get([:user, :deny_follow_blocked]) diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index fca9246a5..aa4c6ddab 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -472,11 +472,27 @@ def add_mute(user, activity, params \\ %{}) do end end - def remove_mute(user, activity) do + def remove_mute(%User{} = user, %Activity{} = activity) do ThreadMute.remove_mute(user.id, activity.data["context"]) {:ok, activity} end + def remove_mute(user_id, activity_id) do + with {:user, %User{} = user} <- {:user, User.get_by_id(user_id)}, + {:activity, %Activity{} = activity} <- {:activity, Activity.get_by_id(activity_id)} do + remove_mute(user, activity) + else + {what, result} = error -> + Logger.warn( + "CommonAPI.remove_mute/2 failed. #{what}: #{result}, user_id: #{user_id}, activity_id: #{ + activity_id + }" + ) + + {:error, error} + end + end + def thread_muted?(%User{id: user_id}, %{data: %{"context" => context}}) when is_binary(context) do ThreadMute.exists?(user_id, context) diff --git a/lib/pleroma/workers/mute_expire_worker.ex b/lib/pleroma/workers/mute_expire_worker.ex index c8b44894e..32a12ba85 100644 --- a/lib/pleroma/workers/mute_expire_worker.ex +++ b/lib/pleroma/workers/mute_expire_worker.ex @@ -7,18 +7,14 @@ defmodule Pleroma.Workers.MuteExpireWorker do @impl Oban.Worker def perform(%Job{args: %{"op" => "unmute_user", "muter_id" => muter_id, "mutee_id" => mutee_id}}) do - muter = Pleroma.User.get_by_id(muter_id) - mutee = Pleroma.User.get_by_id(mutee_id) - Pleroma.User.unmute(muter, mutee) + Pleroma.User.unmute(muter_id, mutee_id) :ok end def perform(%Job{ args: %{"op" => "unmute_conversation", "user_id" => user_id, "activity_id" => activity_id} }) do - user = Pleroma.User.get_by_id(user_id) - activity = Pleroma.Activity.get_by_id(activity_id) - Pleroma.Web.CommonAPI.remove_mute(user, activity) + Pleroma.Web.CommonAPI.remove_mute(user_id, activity_id) :ok end end diff --git a/test/user_test.exs b/test/user_test.exs index ce0d4d38b..79c8b76b8 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -1034,6 +1034,17 @@ test "it unmutes users" do refute User.muted_notifications?(user, muted_user) end + test "it unmutes users by id" do + user = insert(:user) + muted_user = insert(:user) + + {:ok, _user_relationships} = User.mute(user, muted_user) + {:ok, _user_mute} = User.unmute(user.id, muted_user.id) + + refute User.mutes?(user, muted_user) + refute User.muted_notifications?(user, muted_user) + end + test "it mutes user without notifications" do user = insert(:user) muted_user = insert(:user) diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index bf4353a57..45ab1e0bd 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -913,6 +913,12 @@ test "remove mute", %{user: user, activity: activity} do refute CommonAPI.thread_muted?(user, activity) end + test "remove mute by ids", %{user: user, activity: activity} do + CommonAPI.add_mute(user, activity) + {:ok, _} = CommonAPI.remove_mute(user.id, activity.id) + refute CommonAPI.thread_muted?(user, activity) + end + test "check that mutes can't be duplicate", %{user: user, activity: activity} do CommonAPI.add_mute(user, activity) {:error, _} = CommonAPI.add_mute(user, activity) From b0bd81ef7187ddf5b4e6cfbc1780fc60b65798c6 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sun, 20 Sep 2020 20:58:32 +0300 Subject: [PATCH 12/34] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a7e27fd3..9fd5f9efe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - Experimental websocket-based federation between Pleroma instances. +- User and conversation mutes can now auto-expire if `expires_in` parameter was given while adding the mute. ### Changed From 9c672ecbb5d4477cd16d2139a2cb66d3923ac5c8 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 8 Oct 2020 20:01:48 -0500 Subject: [PATCH 13/34] Remote Timeline: add Streaming support --- CHANGELOG.md | 1 + docs/API/differences_in_mastoapi_responses.md | 6 ++++++ lib/pleroma/activity/ir/topics.ex | 13 ++++++++++++- lib/pleroma/web/streamer/streamer.ex | 9 +++++++++ test/activity/ir/topics_test.exs | 14 ++++++++++++++ test/integration/mastodon_websocket_test.exs | 1 + test/web/streamer/streamer_test.exs | 8 ++++++++ 7 files changed, 51 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fc1750d1..0eeffb72f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Mix tasks for controlling user account confirmation status in bulk (`mix pleroma.user confirm_all` and `mix pleroma.user unconfirm_all`) - Mix task for sending confirmation emails to all unconfirmed users (`mix pleroma.email send_confirmation_mails`) - Mix task option for force-unfollowing relays +- Ability to view remote timelines, with ex. `/api/v1/timelines/public?instance=lain.com` and streams `public:remote` and `public:remote:media` ### Changed diff --git a/docs/API/differences_in_mastoapi_responses.md b/docs/API/differences_in_mastoapi_responses.md index 38865dc68..bb1000b0b 100644 --- a/docs/API/differences_in_mastoapi_responses.md +++ b/docs/API/differences_in_mastoapi_responses.md @@ -9,9 +9,13 @@ Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mas ## Timelines Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users. + Adding the parameter `exclude_visibilities` to the timeline queries will exclude the statuses with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`), e.g., `exclude_visibilities[]=direct&exclude_visibilities[]=private`. + Adding the parameter `reply_visibility` to the public and home timelines queries will filter replies. Possible values: without parameter (default) shows all replies, `following` - replies directed to you or users you follow, `self` - replies directed to you. +Adding the parameter `instance=lain.com` to the public timeline will show only statuses originating from `lain.com` (or any remote instance). + ## Statuses - `visibility`: has an additional possible value `list` @@ -249,6 +253,8 @@ Has these additional fields under the `pleroma` object: There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field. +For viewing remote server timelines, there are `public:remote` and `public:remote:media` streams. Each of these accept a parameter like `?instance=lain.com`. + ## Not implemented Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer features and non-essential features are omitted. These features usually return an HTTP 200 status code, but with an empty response. While they may be added in the future, they are considered low priority. diff --git a/lib/pleroma/activity/ir/topics.ex b/lib/pleroma/activity/ir/topics.ex index 9e65bedad..fe2e8cb5c 100644 --- a/lib/pleroma/activity/ir/topics.ex +++ b/lib/pleroma/activity/ir/topics.ex @@ -40,7 +40,8 @@ defp visibility_tags(object, activity) do end defp item_creation_tags(tags, object, %{data: %{"type" => "Create"}} = activity) do - tags ++ hashtags_to_topics(object) ++ attachment_topics(object, activity) + tags ++ + remote_topics(activity) ++ hashtags_to_topics(object) ++ attachment_topics(object, activity) end defp item_creation_tags(tags, _, _) do @@ -55,9 +56,19 @@ defp hashtags_to_topics(%{data: %{"tag" => tags}}) do defp hashtags_to_topics(_), do: [] + defp remote_topics(%{local: true}), do: [] + + defp remote_topics(%{actor: actor}) when is_binary(actor), + do: ["public:remote:" <> URI.parse(actor).host] + + defp remote_topics(_), do: [] + defp attachment_topics(%{data: %{"attachment" => []}}, _act), do: [] defp attachment_topics(_object, %{local: true}), do: ["public:media", "public:local:media"] + defp attachment_topics(_object, %{actor: actor}) when is_binary(actor), + do: ["public:media", "public:remote:media:" <> URI.parse(actor).host] + defp attachment_topics(_object, _act), do: ["public:media"] end diff --git a/lib/pleroma/web/streamer/streamer.ex b/lib/pleroma/web/streamer/streamer.ex index 5475f18a6..d774f0dd9 100644 --- a/lib/pleroma/web/streamer/streamer.ex +++ b/lib/pleroma/web/streamer/streamer.ex @@ -57,6 +57,15 @@ def get_topic("hashtag", _user, _oauth_token, %{"tag" => tag} = _params) do {:ok, "hashtag:" <> tag} end + # Allow remote instance streams. + def get_topic("public:remote", _user, _oauth_token, %{"instance" => instance} = _params) do + {:ok, "public:remote:" <> instance} + end + + def get_topic("public:remote:media", _user, _oauth_token, %{"instance" => instance} = _params) do + {:ok, "public:remote:media:" <> instance} + end + # Expand user streams. def get_topic( stream, diff --git a/test/activity/ir/topics_test.exs b/test/activity/ir/topics_test.exs index 14a6e6b71..c8dcb28cc 100644 --- a/test/activity/ir/topics_test.exs +++ b/test/activity/ir/topics_test.exs @@ -93,6 +93,13 @@ test "only converts strings to hash tags", %{ refute Enum.member?(topics, "hashtag:2") end + + test "non-local action produces public:remote topic", %{activity: activity} do + activity = %{activity | local: false, actor: "https://lain.com/users/lain"} + topics = Topics.get_activity_topics(activity) + + assert Enum.member?(topics, "public:remote:lain.com") + end end describe "public visibility create events with attachments" do @@ -124,6 +131,13 @@ test "non-local doesn't produce public:local:media topics", %{activity: activity refute Enum.member?(topics, "public:local:media") end + + test "non-local action produces public:remote:media topic", %{activity: activity} do + activity = %{activity | local: false, actor: "https://lain.com/users/lain"} + topics = Topics.get_activity_topics(activity) + + assert Enum.member?(topics, "public:remote:media:lain.com") + end end describe "non-public visibility" do diff --git a/test/integration/mastodon_websocket_test.exs b/test/integration/mastodon_websocket_test.exs index 0f2e6cc2b..bb8e795b7 100644 --- a/test/integration/mastodon_websocket_test.exs +++ b/test/integration/mastodon_websocket_test.exs @@ -49,6 +49,7 @@ test "requires authentication and a valid token for protected streams" do test "allows public streams without authentication" do assert {:ok, _} = start_socket("?stream=public") assert {:ok, _} = start_socket("?stream=public:local") + assert {:ok, _} = start_socket("?stream=public:remote&instance=lain.com") assert {:ok, _} = start_socket("?stream=hashtag&tag=lain") end diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs index 185724a9f..1495ed124 100644 --- a/test/web/streamer/streamer_test.exs +++ b/test/web/streamer/streamer_test.exs @@ -29,6 +29,14 @@ test "allows public" do assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil, nil) end + test "allows instance streams" do + assert {:ok, "public:remote:lain.com"} = + Streamer.get_topic("public:remote", nil, nil, %{"instance" => "lain.com"}) + + assert {:ok, "public:remote:media:lain.com"} = + Streamer.get_topic("public:remote:media", nil, nil, %{"instance" => "lain.com"}) + end + test "allows hashtag streams" do assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", nil, nil, %{"tag" => "cofe"}) end From 06934b820e739f8a5a598bb807235b621ca6e2ba Mon Sep 17 00:00:00 2001 From: Ali Riza Keles Date: Mon, 12 Oct 2020 23:20:10 +0100 Subject: [PATCH 14/34] Add ejabberd auth document --- docs/configuration/howto_ejabberd.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 docs/configuration/howto_ejabberd.md diff --git a/docs/configuration/howto_ejabberd.md b/docs/configuration/howto_ejabberd.md new file mode 100644 index 000000000..6940acbda --- /dev/null +++ b/docs/configuration/howto_ejabberd.md @@ -0,0 +1,9 @@ +# Configuring Ejabberd (XMPP Server) to use Pleroma for authentication + +If you want to give your Pleroma users an XMPP (chat) account, you can configure [Ejabberd](https://github.com/processone/ejabberd) to use your Pleroma server for user authentication, automatically giving every local user an XMPP account. + +In general, you just have to follow the configuration described at [https://docs.ejabberd.im/admin/configuration/authentication/#external-script](https://docs.ejabberd.im/admin/configuration/authentication/#external-script). Please read this section carefully. + +To get the external script please go to [https://github.com/alirizakeles/ejabberd-pleroma-auth](https://github.com/alirizakeles/ejabberd-pleroma-auth) and follow the steps described in README. + +After restarting your Ejabberd server, your users should now be able to connect with their Pleroma credentials. From 943f65c7399a03075cb6392ce89ae5403ab0c1a0 Mon Sep 17 00:00:00 2001 From: Ali Riza Keles Date: Tue, 13 Oct 2020 19:58:38 +0100 Subject: [PATCH 15/34] Include python script and description --- docs/configuration/howto_ejabberd.md | 129 ++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-) diff --git a/docs/configuration/howto_ejabberd.md b/docs/configuration/howto_ejabberd.md index 6940acbda..520a0acbc 100644 --- a/docs/configuration/howto_ejabberd.md +++ b/docs/configuration/howto_ejabberd.md @@ -4,6 +4,133 @@ If you want to give your Pleroma users an XMPP (chat) account, you can configure In general, you just have to follow the configuration described at [https://docs.ejabberd.im/admin/configuration/authentication/#external-script](https://docs.ejabberd.im/admin/configuration/authentication/#external-script). Please read this section carefully. -To get the external script please go to [https://github.com/alirizakeles/ejabberd-pleroma-auth](https://github.com/alirizakeles/ejabberd-pleroma-auth) and follow the steps described in README. +Copy the script below to suitable path on your system and set owner and permissions. Also do not forget adjusting `PLEROMA_HOST` and `PLEROMA_PORT`, if necessary. + +```bash +cp pleroma_ejabberd_auth.py /etc/ejabberd/pleroma_ejabberd_auth.py +chown ejabberd /etc/ejabberd/pleroma_ejabberd_auth.py +chmod 700 /etc/ejabberd/pleroma_ejabberd_auth.py +``` + +Set external auth params in ejabberd.yaml file: + +```bash +auth_method: [external] +extauth_program: "python3 /etc/ejabberd/pleroma_ejabberd_auth.py" +extauth_instances: 3 +auth_use_cache: false +``` + +Restart / reload your ejabberd service. After restarting your Ejabberd server, your users should now be able to connect with their Pleroma credentials. + + +```python +import sys +import struct +import http.client +from base64 import b64encode +import logging + + +PLEROMA_HOST = "127.0.0.1" +PLEROMA_PORT = "4000" +AUTH_ENDPOINT = "/api/v1/accounts/verify_credentials" +USER_ENDPOINT = "/api/v1/accounts" +LOGFILE = "/var/log/ejabberd/pleroma_auth.log" + +logging.basicConfig(filename=LOGFILE, level=logging.INFO) + + +# Pleroma functions +def create_connection(): + return http.client.HTTPConnection(PLEROMA_HOST, PLEROMA_PORT) + + +def verify_credentials(user: str, password: str) -> bool: + user_pass_b64 = b64encode("{}:{}".format( + user, password).encode('utf-8')).decode("ascii") + params = {} + headers = { + "Authorization": "Basic {}".format(user_pass_b64) + } + + try: + conn = create_connection() + conn.request("GET", AUTH_ENDPOINT, params, headers) + + response = conn.getresponse() + if response.status == 200: + return True + + return False + except Exception as e: + logging.info("Can not connect: %s", str(e)) + return False + + +def does_user_exist(user: str) -> bool: + conn = create_connection() + conn.request("GET", "{}/{}".format(USER_ENDPOINT, user)) + + response = conn.getresponse() + if response.status == 200: + return True + + return False + + +def auth(username: str, server: str, password: str) -> bool: + return verify_credentials(username, password) + + +def isuser(username, server): + return does_user_exist(username) + + +def read(): + (pkt_size,) = struct.unpack('>H', bytes(sys.stdin.read(2), encoding='utf8')) + pkt = sys.stdin.read(pkt_size) + cmd = pkt.split(':')[0] + if cmd == 'auth': + username, server, password = pkt.split(':', 3)[1:] + write(auth(username, server, password)) + elif cmd == 'isuser': + username, server = pkt.split(':', 2)[1:] + write(isuser(username, server)) + elif cmd == 'setpass': + # u, s, p = pkt.split(':', 3)[1:] + write(False) + elif cmd == 'tryregister': + # u, s, p = pkt.split(':', 3)[1:] + write(False) + elif cmd == 'removeuser': + # u, s = pkt.split(':', 2)[1:] + write(False) + elif cmd == 'removeuser3': + # u, s, p = pkt.split(':', 3)[1:] + write(False) + else: + write(False) + + +def write(result): + if result: + sys.stdout.write('\x00\x02\x00\x01') + else: + sys.stdout.write('\x00\x02\x00\x00') + sys.stdout.flush() + + +if __name__ == "__main__": + logging.info("Starting pleroma ejabberd auth daemon...") + while True: + try: + read() + except Exception as e: + logging.info( + "Error while processing data from ejabberd %s", str(e)) + pass + +``` \ No newline at end of file From 3b5a7a6b14f4c09d1d371d6fcb49bece84d6c3e1 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 16 Oct 2020 00:32:20 +0200 Subject: [PATCH 16/34] federation_status: New endpoint showing unreachable instances --- lib/pleroma/instances.ex | 1 + lib/pleroma/instances/instance.ex | 11 +++++ .../controllers/instances_controller.ex | 19 +++++++++ lib/pleroma/web/router.ex | 1 + .../controllers/instances_controller_test.exs | 40 +++++++++++++++++++ 5 files changed, 72 insertions(+) create mode 100644 lib/pleroma/web/pleroma_api/controllers/instances_controller.ex create mode 100644 test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs diff --git a/lib/pleroma/instances.ex b/lib/pleroma/instances.ex index 557e8decf..7315bd7cb 100644 --- a/lib/pleroma/instances.ex +++ b/lib/pleroma/instances.ex @@ -11,6 +11,7 @@ defmodule Pleroma.Instances do defdelegate reachable?(url_or_host), to: @adapter defdelegate set_reachable(url_or_host), to: @adapter defdelegate set_unreachable(url_or_host, unreachable_since \\ nil), to: @adapter + defdelegate get_consistently_unreachable(), to: @adapter def set_consistently_unreachable(url_or_host), do: set_unreachable(url_or_host, reachability_datetime_threshold()) diff --git a/lib/pleroma/instances/instance.ex b/lib/pleroma/instances/instance.ex index f0f601469..df471a39d 100644 --- a/lib/pleroma/instances/instance.ex +++ b/lib/pleroma/instances/instance.ex @@ -119,6 +119,17 @@ def set_unreachable(url_or_host, unreachable_since) when is_binary(url_or_host) def set_unreachable(_, _), do: {:error, nil} + def get_consistently_unreachable do + reachability_datetime_threshold = Instances.reachability_datetime_threshold() + + from(i in Instance, + where: ^reachability_datetime_threshold > i.unreachable_since, + order_by: i.unreachable_since, + select: {i.host, i.unreachable_since} + ) + |> Repo.all() + end + defp parse_datetime(datetime) when is_binary(datetime) do NaiveDateTime.from_iso8601(datetime) end diff --git a/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex b/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex new file mode 100644 index 000000000..bd95cb523 --- /dev/null +++ b/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex @@ -0,0 +1,19 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.InstancesController do + use Pleroma.Web, :controller + + alias Pleroma.Instances + + # defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaInstancesController + + def show(conn, _params) do + unreachable = + Instances.get_consistently_unreachable() + |> Enum.reduce(%{}, fn {host, date}, acc -> Map.put(acc, host, to_string(date)) end) + + json(conn, %{"unreachable" => unreachable}) + end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index d2d939989..5f9a749e4 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -373,6 +373,7 @@ defmodule Pleroma.Web.Router do scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do pipe_through(:api) get("/accounts/:id/scrobbles", ScrobbleController, :index) + get("/federation_status", InstancesController, :show) end scope "/api/v1", Pleroma.Web.MastodonAPI do diff --git a/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs new file mode 100644 index 000000000..9ce901ce3 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs @@ -0,0 +1,40 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaApi.InstancesControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Instances + + setup_all do: clear_config([:instance, :federation_reachability_timeout_days], 1) + + setup do + constant = "http://consistently-unreachable.name/" + eventual = "http://eventually-unreachable.com/path" + + {:ok, %Pleroma.Instances.Instance{unreachable_since: constant_unreachable}} = + Instances.set_consistently_unreachable(constant) + + _eventual_unrechable = Instances.set_unreachable(eventual) + + %{constant_unreachable: constant_unreachable, constant: constant} + end + + test "GET /api/v1/pleroma/federation_status", %{ + conn: conn, + constant_unreachable: constant_unreachable, + constant: constant + } do + constant_host = URI.parse(constant).host + + assert conn + |> put_req_header("content-type", "application/json") + |> get("/api/v1/pleroma/federation_status") + |> json_response(200) == %{ + "unreachable" => %{constant_host => to_string(constant_unreachable)} + } + + # |> json_response_and_validate_schema(200) + end +end From aafdc975bdd38f74cdf5d3f8517d41c5dd76c56b Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 16 Oct 2020 01:13:52 +0200 Subject: [PATCH 17/34] federation_status: Add ApiSpec --- .../operations/pleroma_instances_operation.ex | 40 +++++++++++++++++++ .../controllers/instances_controller.ex | 4 +- .../controllers/instances_controller_test.exs | 4 +- 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex diff --git a/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex new file mode 100644 index 000000000..2c455b0df --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex @@ -0,0 +1,40 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.PleromaInstancesOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def show_operation do + %Operation{ + tags: ["PleromaInstances"], + summary: "Instances federation status", + description: "Information about instances deemed unreachable by the server", + operationId: "PleromaInstances.show", + responses: %{ + 200 => Operation.response("PleromaInstances", "application/json", pleroma_instances()) + } + } + end + + def pleroma_instances do + %Schema{ + type: :object, + properties: %{ + unreachable: %Schema{ + type: :object, + properties: %{hostname: %Schema{type: :string, format: :"date-time"}} + } + }, + example: %{ + "unreachable" => %{"consistently-unreachable.name" => "2020-10-14 22:07:58.216473"} + } + } + end +end diff --git a/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex b/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex index bd95cb523..c577f1d1e 100644 --- a/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex @@ -7,7 +7,9 @@ defmodule Pleroma.Web.PleromaAPI.InstancesController do alias Pleroma.Instances - # defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaInstancesController + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaInstancesOperation def show(conn, _params) do unreachable = diff --git a/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs index 9ce901ce3..13491ed9c 100644 --- a/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs @@ -31,10 +31,8 @@ test "GET /api/v1/pleroma/federation_status", %{ assert conn |> put_req_header("content-type", "application/json") |> get("/api/v1/pleroma/federation_status") - |> json_response(200) == %{ + |> json_response_and_validate_schema(200) == %{ "unreachable" => %{constant_host => to_string(constant_unreachable)} } - - # |> json_response_and_validate_schema(200) end end From 2ca98f2d94e2976ae35998aecff27809d4b066cf Mon Sep 17 00:00:00 2001 From: Haelwenn Date: Wed, 21 Oct 2020 19:40:37 +0000 Subject: [PATCH 18/34] Apply 1 suggestion(s) to 1 file(s) --- lib/pleroma/web/pleroma_api/controllers/instances_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex b/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex index c577f1d1e..9e97480df 100644 --- a/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex @@ -14,7 +14,7 @@ defmodule Pleroma.Web.PleromaAPI.InstancesController do def show(conn, _params) do unreachable = Instances.get_consistently_unreachable() - |> Enum.reduce(%{}, fn {host, date}, acc -> Map.put(acc, host, to_string(date)) end) + |> Map.new(fn {host, date} -> {host, to_string(date)} end) json(conn, %{"unreachable" => unreachable}) end From be52819a112abb66032a56d613eed0233995eef4 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 2 Nov 2020 17:51:54 +0400 Subject: [PATCH 19/34] Hide chats from muted users --- .../controllers/chat_controller.ex | 27 ++++++++----------- .../controllers/chat_controller_test.exs | 22 +++++++++++++++ 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex index 2c4d3f135..8fc70c15a 100644 --- a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex @@ -15,7 +15,6 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do alias Pleroma.User alias Pleroma.Web.CommonAPI alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView - alias Pleroma.Web.PleromaAPI.ChatView alias Pleroma.Web.Plugs.OAuthScopesPlug import Ecto.Query @@ -121,9 +120,7 @@ def mark_as_read( ) do with {:ok, chat} <- Chat.get_by_user_and_id(user, id), {_n, _} <- MessageReference.set_all_seen_for_chat(chat, last_read_id) do - conn - |> put_view(ChatView) - |> render("show.json", chat: chat) + render(conn, "show.json", chat: chat) end end @@ -142,32 +139,30 @@ def messages(%{assigns: %{user: user}} = conn, %{id: id} = params) do end def index(%{assigns: %{user: %{id: user_id} = user}} = conn, _params) do - blocked_ap_ids = User.blocked_users_ap_ids(user) + exclude_users = + user + |> User.blocked_users_ap_ids() + |> Enum.concat(User.muted_users_ap_ids(user)) chats = - Chat.for_user_query(user_id) - |> where([c], c.recipient not in ^blocked_ap_ids) + user_id + |> Chat.for_user_query() + |> where([c], c.recipient not in ^exclude_users) |> Repo.all() - conn - |> put_view(ChatView) - |> render("index.json", chats: chats) + render(conn, "index.json", chats: chats) end def create(%{assigns: %{user: user}} = conn, %{id: id}) do with %User{ap_id: recipient} <- User.get_cached_by_id(id), {:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do - conn - |> put_view(ChatView) - |> render("show.json", chat: chat) + render(conn, "show.json", chat: chat) end end def show(%{assigns: %{user: user}} = conn, %{id: id}) do with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do - conn - |> put_view(ChatView) - |> render("show.json", chat: chat) + render(conn, "show.json", chat: chat) end end diff --git a/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs index fa6b9db65..b0498df2b 100644 --- a/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs @@ -343,6 +343,28 @@ test "it does not return chats with users you blocked", %{conn: conn, user: user assert length(result) == 0 end + test "it does not return chats with users you muted", %{conn: conn, user: user} do + recipient = insert(:user) + + {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id) + + result = + conn + |> get("/api/v1/pleroma/chats") + |> json_response_and_validate_schema(200) + + assert length(result) == 1 + + User.mute(user, recipient) + + result = + conn + |> get("/api/v1/pleroma/chats") + |> json_response_and_validate_schema(200) + + assert length(result) == 0 + end + test "it returns all chats", %{conn: conn, user: user} do Enum.each(1..30, fn _ -> recipient = insert(:user) From 1cfc3278c086c9eaa7b2d1bd170e82c8b2aebd78 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 4 Nov 2020 10:14:00 +0100 Subject: [PATCH 20/34] Poll View: Always return `voters_count`. --- lib/pleroma/web/mastodon_api/views/poll_view.ex | 2 +- test/pleroma/web/mastodon_api/views/poll_view_test.exs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/views/poll_view.ex b/lib/pleroma/web/mastodon_api/views/poll_view.ex index 1208dc9a0..4101f21d0 100644 --- a/lib/pleroma/web/mastodon_api/views/poll_view.ex +++ b/lib/pleroma/web/mastodon_api/views/poll_view.ex @@ -19,7 +19,7 @@ def render("show.json", %{object: object, multiple: multiple, options: options} expired: expired, multiple: multiple, votes_count: votes_count, - voters_count: (multiple || nil) && voters_count(object), + voters_count: voters_count(object), options: options, voted: voted?(params), emojis: Pleroma.Web.MastodonAPI.StatusView.build_emojis(object.data["emoji"]) diff --git a/test/pleroma/web/mastodon_api/views/poll_view_test.exs b/test/pleroma/web/mastodon_api/views/poll_view_test.exs index b7e2f17ef..c655ca438 100644 --- a/test/pleroma/web/mastodon_api/views/poll_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/poll_view_test.exs @@ -44,7 +44,7 @@ test "renders a poll" do ], voted: false, votes_count: 0, - voters_count: nil + voters_count: 0 } result = PollView.render("show.json", %{object: object}) From f09bb814a96c71f24fcf6e403a25e90be9cc684e Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 4 Nov 2020 10:14:48 +0100 Subject: [PATCH 21/34] Changelog: Add info about poll view changes --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ee17d239..8c5a9f9dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Users with the `discoverable` field set to false will not show up in searches. - Minimum lifetime for ephmeral activities changed to 10 minutes and made configurable (`:min_lifetime` option). - Introduced optional dependencies on `ffmpeg`, `ImageMagick`, `exiftool` software packages. Please refer to `docs/installation/optional/media_graphics_packages.md`. +- Polls now always return a `voters_count`, even if they are single-choice
API Changes From 92d252f364ed421f2afcdd135507ced3554eb3f0 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 4 Nov 2020 10:20:09 +0100 Subject: [PATCH 22/34] Poll Schema: Update and fix. --- lib/pleroma/web/api_spec/schemas/poll.ex | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/api_spec/schemas/poll.ex b/lib/pleroma/web/api_spec/schemas/poll.ex index c62096db0..0dfa60b97 100644 --- a/lib/pleroma/web/api_spec/schemas/poll.ex +++ b/lib/pleroma/web/api_spec/schemas/poll.ex @@ -28,8 +28,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Poll do }, votes_count: %Schema{ type: :integer, - nullable: true, - description: "How many votes have been received. Number, or null if `multiple` is false." + description: "How many votes have been received. Number." + }, + voters_count: %Schema{ + type: :integer, + description: "How many unique accounts have voted. Number." }, voted: %Schema{ type: :boolean, @@ -61,7 +64,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Poll do expired: true, multiple: false, votes_count: 10, - voters_count: nil, + voters_count: 10, voted: true, own_votes: [ 1 From ca95cbe0b48b6c64e6e33addf79e4d212d5f9872 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 4 Nov 2020 16:40:12 +0400 Subject: [PATCH 23/34] Add `with_muted` param to ChatController.index/2 --- docs/API/chats.md | 4 ++++ lib/pleroma/web/api_spec/operations/chat_operation.ex | 6 +++++- lib/pleroma/web/api_spec/operations/timeline_operation.ex | 2 +- lib/pleroma/web/pleroma_api/controllers/chat_controller.ex | 7 +++---- .../web/pleroma_api/controllers/chat_controller_test.exs | 7 +++++++ 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/API/chats.md b/docs/API/chats.md index 9857aac67..f50144c86 100644 --- a/docs/API/chats.md +++ b/docs/API/chats.md @@ -116,6 +116,10 @@ The modified chat message This will return a list of chats that you have been involved in, sorted by their last update (so new chats will be at the top). +Parameters: + +- with_muted: Include chats from muted users (boolean). + Returned data: ```json diff --git a/lib/pleroma/web/api_spec/operations/chat_operation.ex b/lib/pleroma/web/api_spec/operations/chat_operation.ex index 0dcfdb354..560b81f17 100644 --- a/lib/pleroma/web/api_spec/operations/chat_operation.ex +++ b/lib/pleroma/web/api_spec/operations/chat_operation.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do alias OpenApiSpex.Operation alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Schemas.ApiError + alias Pleroma.Web.ApiSpec.Schemas.BooleanLike alias Pleroma.Web.ApiSpec.Schemas.Chat alias Pleroma.Web.ApiSpec.Schemas.ChatMessage @@ -132,7 +133,10 @@ def index_operation do tags: ["chat"], summary: "Get a list of chats that you participated in", operationId: "ChatController.index", - parameters: pagination_params(), + parameters: [ + Operation.parameter(:with_muted, :query, BooleanLike, "Include chats from muted users") + | pagination_params() + ], responses: %{ 200 => Operation.response("The chats of the user", "application/json", chats_response()) }, diff --git a/lib/pleroma/web/api_spec/operations/timeline_operation.ex b/lib/pleroma/web/api_spec/operations/timeline_operation.ex index 8e19bace7..1b5ad796f 100644 --- a/lib/pleroma/web/api_spec/operations/timeline_operation.ex +++ b/lib/pleroma/web/api_spec/operations/timeline_operation.ex @@ -159,7 +159,7 @@ defp local_param do end defp with_muted_param do - Operation.parameter(:with_muted, :query, BooleanLike, "Includeactivities by muted users") + Operation.parameter(:with_muted, :query, BooleanLike, "Include activities by muted users") end defp exclude_visibilities_param do diff --git a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex index 8fc70c15a..77564b342 100644 --- a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex @@ -138,11 +138,10 @@ def messages(%{assigns: %{user: user}} = conn, %{id: id} = params) do end end - def index(%{assigns: %{user: %{id: user_id} = user}} = conn, _params) do + def index(%{assigns: %{user: %{id: user_id} = user}} = conn, params) do exclude_users = - user - |> User.blocked_users_ap_ids() - |> Enum.concat(User.muted_users_ap_ids(user)) + User.blocked_users_ap_ids(user) ++ + if params[:with_muted], do: [], else: User.muted_users_ap_ids(user) chats = user_id diff --git a/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs index b0498df2b..c1e6a8cc5 100644 --- a/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs @@ -363,6 +363,13 @@ test "it does not return chats with users you muted", %{conn: conn, user: user} |> json_response_and_validate_schema(200) assert length(result) == 0 + + result = + conn + |> get("/api/v1/pleroma/chats?with_muted=true") + |> json_response_and_validate_schema(200) + + assert length(result) == 1 end test "it returns all chats", %{conn: conn, user: user} do From cb3cd3a761d96a08b1b55f5b277795822aa7e1d7 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 4 Nov 2020 15:24:10 +0100 Subject: [PATCH 24/34] TopicsTest: Small addition. --- test/pleroma/activity/ir/topics_test.exs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/pleroma/activity/ir/topics_test.exs b/test/pleroma/activity/ir/topics_test.exs index 2e5655334..5e5c2f8da 100644 --- a/test/pleroma/activity/ir/topics_test.exs +++ b/test/pleroma/activity/ir/topics_test.exs @@ -104,6 +104,13 @@ test "non-local action produces public:remote topic", %{activity: activity} do assert Enum.member?(topics, "public:remote:lain.com") end + + test "local action doesn't produce public:remote topic", %{activity: activity} do + activity = %{activity | local: true, actor: "https://lain.com/users/lain"} + topics = Topics.get_activity_topics(activity) + + refute Enum.member?(topics, "public:remote:lain.com") + end end describe "public visibility create events with attachments" do From eb1e1e74945a55c37e4b0dc7cea1a7c926ff981a Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 4 Nov 2020 15:39:32 +0100 Subject: [PATCH 25/34] Changelog: Add info about federation status endpoint --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ee17d239..8951b4523 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Admin API: (`GET /api/pleroma/admin/users`) added filters user by `unconfirmed` status - Admin API: (`GET /api/pleroma/admin/users`) added filters user by `actor_type` - Pleroma API: Add `idempotency_key` to the chat message entity that can be used for optimistic message sending. +- Pleroma API: (`GET /api/v1/pleroma/federation_status`) Add a way to get a list of unreachable instances.
From 6d850c46dc439e018323fb3580e708131247457b Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 4 Nov 2020 17:12:47 +0100 Subject: [PATCH 26/34] AdminEmail: Use AP id as user url. --- lib/pleroma/emails/admin_email.ex | 10 +++------- test/pleroma/emails/admin_email_test.exs | 6 +++--- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/pleroma/emails/admin_email.ex b/lib/pleroma/emails/admin_email.ex index 8979db2f8..02274554f 100644 --- a/lib/pleroma/emails/admin_email.ex +++ b/lib/pleroma/emails/admin_email.ex @@ -18,10 +18,6 @@ defp instance_notify_email do Keyword.get(instance_config(), :notify_email, instance_config()[:email]) end - defp user_url(user) do - Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, user.id) - end - def test_email(mail_to \\ nil) do html_body = """

Instance Test Email

@@ -69,8 +65,8 @@ def report(to, reporter, account, statuses, comment) do end html_body = """ -

Reported by: #{reporter.nickname}

-

Reported Account: #{account.nickname}

+

Reported by: #{reporter.nickname}

+

Reported Account: #{account.nickname}

#{comment_html} #{statuses_html}

@@ -86,7 +82,7 @@ def report(to, reporter, account, statuses, comment) do def new_unapproved_registration(to, account) do html_body = """ -

New account for review: @#{account.nickname}

+

New account for review: @#{account.nickname}

#{HTML.strip_tags(account.registration_reason)}
Visit AdminFE """ diff --git a/test/pleroma/emails/admin_email_test.exs b/test/pleroma/emails/admin_email_test.exs index 155057f3e..0da0699cc 100644 --- a/test/pleroma/emails/admin_email_test.exs +++ b/test/pleroma/emails/admin_email_test.exs @@ -19,8 +19,8 @@ test "build report email" do AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment") status_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, "12") - reporter_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.id) - account_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id) + reporter_url = reporter.ap_id + account_url = account.ap_id assert res.to == [{to_user.name, to_user.email}] assert res.from == {config[:name], config[:notify_email]} @@ -54,7 +54,7 @@ test "new unapproved registration email" do res = AdminEmail.new_unapproved_registration(to_user, account) - account_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id) + account_url = account.ap_id assert res.to == [{to_user.name, to_user.email}] assert res.from == {config[:name], config[:notify_email]} From 5ddf0be208b1a2703f24c907ac5a07cb8ea12f8c Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 4 Nov 2020 17:13:34 +0100 Subject: [PATCH 27/34] Changelog: Add info about admin email user link changes. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a82f7d6c..756939dde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Minimum lifetime for ephmeral activities changed to 10 minutes and made configurable (`:min_lifetime` option). - Introduced optional dependencies on `ffmpeg`, `ImageMagick`, `exiftool` software packages. Please refer to `docs/installation/optional/media_graphics_packages.md`. - Polls now always return a `voters_count`, even if they are single-choice +- Admin Emails: The ap id is used as the user link in emails now.
API Changes From 9b2ed1427725cd8c5e8d81e39cc5bbc73a2140bc Mon Sep 17 00:00:00 2001 From: lain Date: Thu, 5 Nov 2020 13:23:58 +0100 Subject: [PATCH 28/34] Docs: Add info about expiring mutes. --- docs/API/differences_in_mastoapi_responses.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/API/differences_in_mastoapi_responses.md b/docs/API/differences_in_mastoapi_responses.md index bb1000b0b..3075b6b86 100644 --- a/docs/API/differences_in_mastoapi_responses.md +++ b/docs/API/differences_in_mastoapi_responses.md @@ -255,6 +255,10 @@ There is an additional `user:pleroma_chat` stream. Incoming chat messages will m For viewing remote server timelines, there are `public:remote` and `public:remote:media` streams. Each of these accept a parameter like `?instance=lain.com`. +## User muting and thread muting + +Both user muting and thread muting can be done for only a certain time by adding an `expires_in` parameter to the API calls and giving the expiration time in seconds. + ## Not implemented Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer features and non-essential features are omitted. These features usually return an HTTP 200 status code, but with an empty response. While they may be added in the future, they are considered low priority. From cc45c69fff515cb82c4902b67c8edce6b109c819 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 7 Nov 2020 22:09:28 +0300 Subject: [PATCH 29/34] Remove release_env While taking a final look at instance.gen before releasing I noticed that the release_env task outputs messages in broken english. Upon further inspection it seems to have even more severe issues which, in my opinion, warrant it's at least temporary removal: - We do not explain what it actually does, anywhere. Neither the task docs nor instance.gen, nor installation instructions. - It does not respect FHS on OTP releases (uses /opt/pleroma/config even though we store the config in /etc/pleroma/config.exs). - It doesn't work on OTP releases, which is the main reason it exists. Neither systemd nor openrc service files for OTP include it. - It is not mentioned in install guides other than the ones for Debian and OTP releases. --- .../CLI_tasks/release_environments.md | 9 --- docs/installation/debian_based_en.md | 1 - docs/installation/otp_en.md | 3 - installation/init.d/pleroma | 1 - installation/pleroma.service | 2 - lib/mix/tasks/pleroma/instance.ex | 22 +----- lib/mix/tasks/pleroma/release_env.ex | 76 ------------------- test/mix/tasks/pleroma/instance_test.exs | 11 +-- test/mix/tasks/pleroma/release_env_test.exs | 30 -------- 9 files changed, 2 insertions(+), 153 deletions(-) delete mode 100644 docs/administration/CLI_tasks/release_environments.md delete mode 100644 lib/mix/tasks/pleroma/release_env.ex delete mode 100644 test/mix/tasks/pleroma/release_env_test.exs diff --git a/docs/administration/CLI_tasks/release_environments.md b/docs/administration/CLI_tasks/release_environments.md deleted file mode 100644 index 36ab43864..000000000 --- a/docs/administration/CLI_tasks/release_environments.md +++ /dev/null @@ -1,9 +0,0 @@ -# Generate release environment file - -```sh tab="OTP" - ./bin/pleroma_ctl release_env gen -``` - -```sh tab="From Source" -mix pleroma.release_env gen -``` diff --git a/docs/installation/debian_based_en.md b/docs/installation/debian_based_en.md index b9fc4e112..75ceb6595 100644 --- a/docs/installation/debian_based_en.md +++ b/docs/installation/debian_based_en.md @@ -182,7 +182,6 @@ sudo cp /opt/pleroma/installation/pleroma.service /etc/systemd/system/pleroma.se ``` * Edit the service file and make sure that all paths fit your installation -* Check that `EnvironmentFile` contains the correct path to the env file. Or generate the env file: `sudo -Hu pleroma mix pleroma.release_env gen` * Enable and start `pleroma.service`: ```shell diff --git a/docs/installation/otp_en.md b/docs/installation/otp_en.md index 98360bcf7..63eda63ca 100644 --- a/docs/installation/otp_en.md +++ b/docs/installation/otp_en.md @@ -149,9 +149,6 @@ chown -R pleroma /etc/pleroma # Run the config generator su pleroma -s $SHELL -lc "./bin/pleroma_ctl instance gen --output /etc/pleroma/config.exs --output-psql /tmp/setup_db.psql" -# Run the environment file generator. -su pleroma -s $SHELL -lc "./bin/pleroma_ctl release_env gen" - # Create the postgres database su postgres -s $SHELL -lc "psql -f /tmp/setup_db.psql" diff --git a/installation/init.d/pleroma b/installation/init.d/pleroma index e908cda1b..384536f7e 100755 --- a/installation/init.d/pleroma +++ b/installation/init.d/pleroma @@ -8,7 +8,6 @@ pidfile="/var/run/pleroma.pid" directory=/opt/pleroma healthcheck_delay=60 healthcheck_timer=30 -export $(cat /opt/pleroma/config/pleroma.env) : ${pleroma_port:-4000} diff --git a/installation/pleroma.service b/installation/pleroma.service index 63e83ed6e..8338228d8 100644 --- a/installation/pleroma.service +++ b/installation/pleroma.service @@ -17,8 +17,6 @@ Environment="MIX_ENV=prod" Environment="HOME=/var/lib/pleroma" ; Path to the folder containing the Pleroma installation. WorkingDirectory=/opt/pleroma -; Path to the environment file. the file contains RELEASE_COOKIE and etc -EnvironmentFile=/opt/pleroma/config/pleroma.env ; Path to the Mix binary. ExecStart=/usr/bin/mix phx.server diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index 1915aacd9..fc21ae062 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -36,9 +36,7 @@ def run(["gen" | rest]) do listen_port: :string, strip_uploads: :string, anonymize_uploads: :string, - dedupe_uploads: :string, - skip_release_env: :boolean, - release_env_file: :string + dedupe_uploads: :string ], aliases: [ o: :output, @@ -243,24 +241,6 @@ def run(["gen" | rest]) do write_robots_txt(static_dir, indexable, template_dir) - if Keyword.get(options, :skip_release_env, false) do - shell_info(""" - Release environment file is skip. Please generate the release env file before start. - `MIX_ENV=#{Mix.env()} mix pleroma.release_env gen` - """) - else - shell_info("Generation the environment file:") - - release_env_args = - with path when not is_nil(path) <- Keyword.get(options, :release_env_file) do - ["gen", "--path", path] - else - _ -> ["gen"] - end - - Mix.Tasks.Pleroma.ReleaseEnv.run(release_env_args) - end - shell_info( "\n All files successfully written! Refer to the installation instructions for your platform for next steps." ) diff --git a/lib/mix/tasks/pleroma/release_env.ex b/lib/mix/tasks/pleroma/release_env.ex deleted file mode 100644 index 9da74ffcf..000000000 --- a/lib/mix/tasks/pleroma/release_env.ex +++ /dev/null @@ -1,76 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.ReleaseEnv do - use Mix.Task - import Mix.Pleroma - - @shortdoc "Generate Pleroma environment file." - @moduledoc File.read!("docs/administration/CLI_tasks/release_environments.md") - - def run(["gen" | rest]) do - {options, [], []} = - OptionParser.parse( - rest, - strict: [ - force: :boolean, - path: :string - ], - aliases: [ - p: :path, - f: :force - ] - ) - - file_path = - get_option( - options, - :path, - "Environment file path", - "./config/pleroma.env" - ) - - env_path = Path.expand(file_path) - - proceed? = - if File.exists?(env_path) do - get_option( - options, - :force, - "Environment file already exists. Do you want to overwrite the #{env_path} file? (y/n)", - "n" - ) === "y" - else - true - end - - if proceed? do - case do_generate(env_path) do - {:error, reason} -> - shell_error( - File.Error.message(%{action: "write to file", reason: reason, path: env_path}) - ) - - _ -> - shell_info("\nThe file generated: #{env_path}.\n") - - shell_info(""" - WARNING: before start pleroma app please make sure to make the file read-only and non-modifiable. - Example: - chmod 0444 #{file_path} - chattr +i #{file_path} - """) - end - else - shell_info("\nThe file is exist. #{env_path}.\n") - end - end - - def do_generate(path) do - content = "RELEASE_COOKIE=#{Base.encode32(:crypto.strong_rand_bytes(32))}" - - File.mkdir_p!(Path.dirname(path)) - File.write(path, content) - end -end diff --git a/test/mix/tasks/pleroma/instance_test.exs b/test/mix/tasks/pleroma/instance_test.exs index fe69a2def..8a02710ee 100644 --- a/test/mix/tasks/pleroma/instance_test.exs +++ b/test/mix/tasks/pleroma/instance_test.exs @@ -5,8 +5,6 @@ defmodule Mix.Tasks.Pleroma.InstanceTest do use ExUnit.Case - @release_env_file "./test/pleroma.test.env" - setup do File.mkdir_p!(tmp_path()) @@ -18,8 +16,6 @@ defmodule Mix.Tasks.Pleroma.InstanceTest do File.rm_rf(Path.join(static_dir, "robots.txt")) end - if File.exists?(@release_env_file), do: File.rm_rf(@release_env_file) - Pleroma.Config.put([:instance, :static_dir], static_dir) end) @@ -73,9 +69,7 @@ test "running gen" do "--dedupe-uploads", "n", "--anonymize-uploads", - "n", - "--release-env-file", - @release_env_file + "n" ]) end @@ -97,9 +91,6 @@ test "running gen" do assert generated_config =~ "filters: [Pleroma.Upload.Filter.ExifTool]" assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql() assert File.exists?(Path.expand("./test/instance/static/robots.txt")) - assert File.exists?(@release_env_file) - - assert File.read!(@release_env_file) =~ ~r/^RELEASE_COOKIE=.*/ end defp generated_setup_psql do diff --git a/test/mix/tasks/pleroma/release_env_test.exs b/test/mix/tasks/pleroma/release_env_test.exs deleted file mode 100644 index 519f1eba9..000000000 --- a/test/mix/tasks/pleroma/release_env_test.exs +++ /dev/null @@ -1,30 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.ReleaseEnvTest do - use ExUnit.Case - import ExUnit.CaptureIO, only: [capture_io: 1] - - @path "config/pleroma.test.env" - - def do_clean do - if File.exists?(@path) do - File.rm_rf(@path) - end - end - - setup do - do_clean() - on_exit(fn -> do_clean() end) - :ok - end - - test "generate pleroma.env" do - assert capture_io(fn -> - Mix.Tasks.Pleroma.ReleaseEnv.run(["gen", "--path", @path, "--force"]) - end) =~ "The file generated" - - assert File.read!(@path) =~ "RELEASE_COOKIE=" - end -end From e4a21084f0017c873a307ec85d0ef5ea341ce026 Mon Sep 17 00:00:00 2001 From: Sean King Date: Sun, 8 Nov 2020 16:16:20 -0700 Subject: [PATCH 30/34] Fix title on load of Pleroma HTML --- lib/pleroma/web/fallback/redirect_controller.ex | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pleroma/web/fallback/redirect_controller.ex b/lib/pleroma/web/fallback/redirect_controller.ex index 6f759d559..712991c18 100644 --- a/lib/pleroma/web/fallback/redirect_controller.ex +++ b/lib/pleroma/web/fallback/redirect_controller.ex @@ -37,9 +37,11 @@ def redirector_with_meta(conn, params) do tags = build_tags(conn, params) preloads = preload_data(conn, params) + title = "#{Pleroma.Config.get([:instance, :name])}" response = index_content + |> String.replace(~r/.+?<\/title>/, title) |> String.replace("<!--server-generated-meta-->", tags <> preloads) conn @@ -54,9 +56,11 @@ def redirector_with_preload(conn, %{"path" => ["pleroma", "admin"]}) do def redirector_with_preload(conn, params) do {:ok, index_content} = File.read(index_file_path()) preloads = preload_data(conn, params) + title = "<title>#{Pleroma.Config.get([:instance, :name])}" response = index_content + |> String.replace(~r/.+?<\/title>/, title) |> String.replace("<!--server-generated-meta-->", preloads) conn From 0c68b9ac137e98867cf8aacfef1f264412cc7b3e Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Tue, 10 Nov 2020 10:44:22 +0300 Subject: [PATCH 31/34] escaping summary and other fields in xml templates --- lib/pleroma/web/feed/feed_view.ex | 2 +- .../templates/feed/feed/_activity.atom.eex | 2 +- .../web/templates/feed/feed/_activity.rss.eex | 2 +- .../pleroma/web/feed/user_controller_test.exs | 77 +++++++------------ 4 files changed, 29 insertions(+), 54 deletions(-) diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex index 1ae03e7e2..56c024617 100644 --- a/lib/pleroma/web/feed/feed_view.ex +++ b/lib/pleroma/web/feed/feed_view.ex @@ -83,7 +83,7 @@ def activity_content(%{"content" => content}) do def activity_content(_), do: "" - def activity_context(activity), do: activity.data["context"] + def activity_context(activity), do: escape(activity.data["context"]) def attachment_href(attachment) do attachment["url"] diff --git a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex index 78350f2aa..3fd150c4e 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex @@ -12,7 +12,7 @@ <link href="<%= activity_context(@activity) %>" rel="ostatus:conversation"/> <%= if @data["summary"] do %> - <summary><%= @data["summary"] %></summary> + <summary><%= escape(@data["summary"]) %></summary> <% end %> <%= if @activity.local do %> diff --git a/lib/pleroma/web/templates/feed/feed/_activity.rss.eex b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex index a304a16af..42960de7d 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex @@ -12,7 +12,7 @@ <link rel="ostatus:conversation"><%= activity_context(@activity) %></link> <%= if @data["summary"] do %> - <description><%= @data["summary"] %></description> + <description><%= escape(@data["summary"]) %></description> <% end %> <%= if @activity.local do %> diff --git a/test/pleroma/web/feed/user_controller_test.exs b/test/pleroma/web/feed/user_controller_test.exs index eabfe3a63..16f002717 100644 --- a/test/pleroma/web/feed/user_controller_test.exs +++ b/test/pleroma/web/feed/user_controller_test.exs @@ -12,16 +12,17 @@ defmodule Pleroma.Web.Feed.UserControllerTest do alias Pleroma.Object alias Pleroma.User alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Feed.FeedView setup do: clear_config([:static_fe, :enabled], false) describe "feed" do setup do: clear_config([:feed]) - test "gets an atom feed", %{conn: conn} do + setup do Config.put( [:feed, :post_title], - %{max_length: 10, omission: "..."} + %{max_length: 15, omission: "..."} ) activity = insert(:note_activity) @@ -29,7 +30,8 @@ test "gets an atom feed", %{conn: conn} do note = insert(:note, data: %{ - "content" => "This is :moominmamma: note ", + "content" => "This & this is :moominmamma: note ", + "source" => "This & this is :moominmamma: note ", "attachment" => [ %{ "url" => [ @@ -37,7 +39,9 @@ test "gets an atom feed", %{conn: conn} do ] } ], - "inReplyTo" => activity.data["id"] + "inReplyTo" => activity.data["id"], + "context" => "2hu & as", + "summary" => "2hu & as" } ) @@ -48,7 +52,7 @@ test "gets an atom feed", %{conn: conn} do insert(:note, user: user, data: %{ - "content" => "42 This is :moominmamma: note ", + "content" => "42 & This is :moominmamma: note ", "inReplyTo" => activity.data["id"] } ) @@ -56,6 +60,10 @@ test "gets an atom feed", %{conn: conn} do note_activity2 = insert(:note_activity, note: note2) object = Object.normalize(note_activity) + [user: user, object: object, max_id: note_activity2.id] + end + + test "gets an atom feed", %{conn: conn, user: user, object: object, max_id: max_id} do resp = conn |> put_req_header("accept", "application/atom+xml") @@ -67,13 +75,15 @@ test "gets an atom feed", %{conn: conn} do |> SweetXml.parse() |> SweetXml.xpath(~x"//entry/title/text()"l) - assert activity_titles == ['42 This...', 'This is...'] - assert resp =~ object.data["content"] + assert activity_titles == ['42 & Thi...', 'This & t...'] + assert resp =~ FeedView.escape(object.data["content"]) + assert resp =~ FeedView.escape(object.data["summary"]) + assert resp =~ FeedView.escape(object.data["context"]) resp = conn |> put_req_header("accept", "application/atom+xml") - |> get("/users/#{user.nickname}/feed", %{"max_id" => note_activity2.id}) + |> get("/users/#{user.nickname}/feed", %{"max_id" => max_id}) |> response(200) activity_titles = @@ -81,47 +91,10 @@ test "gets an atom feed", %{conn: conn} do |> SweetXml.parse() |> SweetXml.xpath(~x"//entry/title/text()"l) - assert activity_titles == ['This is...'] + assert activity_titles == ['This & t...'] end - test "gets a rss feed", %{conn: conn} do - Pleroma.Config.put( - [:feed, :post_title], - %{max_length: 10, omission: "..."} - ) - - activity = insert(:note_activity) - - note = - insert(:note, - data: %{ - "content" => "This is :moominmamma: note ", - "attachment" => [ - %{ - "url" => [ - %{"mediaType" => "image/png", "href" => "https://pleroma.gov/image.png"} - ] - } - ], - "inReplyTo" => activity.data["id"] - } - ) - - note_activity = insert(:note_activity, note: note) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - note2 = - insert(:note, - user: user, - data: %{ - "content" => "42 This is :moominmamma: note ", - "inReplyTo" => activity.data["id"] - } - ) - - note_activity2 = insert(:note_activity, note: note2) - object = Object.normalize(note_activity) - + test "gets a rss feed", %{conn: conn, user: user, object: object, max_id: max_id} do resp = conn |> put_req_header("accept", "application/rss+xml") @@ -133,13 +106,15 @@ test "gets a rss feed", %{conn: conn} do |> SweetXml.parse() |> SweetXml.xpath(~x"//item/title/text()"l) - assert activity_titles == ['42 This...', 'This is...'] - assert resp =~ object.data["content"] + assert activity_titles == ['42 & Thi...', 'This & t...'] + assert resp =~ FeedView.escape(object.data["content"]) + assert resp =~ FeedView.escape(object.data["summary"]) + assert resp =~ FeedView.escape(object.data["context"]) resp = conn |> put_req_header("accept", "application/rss+xml") - |> get("/users/#{user.nickname}/feed.rss", %{"max_id" => note_activity2.id}) + |> get("/users/#{user.nickname}/feed.rss", %{"max_id" => max_id}) |> response(200) activity_titles = @@ -147,7 +122,7 @@ test "gets a rss feed", %{conn: conn} do |> SweetXml.parse() |> SweetXml.xpath(~x"//item/title/text()"l) - assert activity_titles == ['This is...'] + assert activity_titles == ['This & t...'] end test "returns 404 for a missing feed", %{conn: conn} do From 485697d96c6a45127af22b9a5f357c3802dba73c Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Tue, 10 Nov 2020 19:18:53 +0300 Subject: [PATCH 32/34] config descriptions for custom MRF policies --- config/description.exs | 302 ------------------ lib/pleroma/docs/json.ex | 6 +- lib/pleroma/web/activity_pub/mrf.ex | 58 +++- .../mrf/activity_expiration_policy.ex | 18 ++ .../web/activity_pub/mrf/hellthread_policy.ex | 27 ++ .../web/activity_pub/mrf/keyword_policy.ex | 42 +++ .../web/activity_pub/mrf/mention_policy.ex | 18 ++ .../web/activity_pub/mrf/normalize_markup.ex | 19 ++ .../web/activity_pub/mrf/object_age_policy.ex | 28 ++ .../web/activity_pub/mrf/reject_non_public.ex | 23 ++ .../web/activity_pub/mrf/simple_policy.ex | 74 +++++ .../web/activity_pub/mrf/subchain_policy.ex | 24 ++ .../mrf/user_allow_list_policy.ex | 14 + .../web/activity_pub/mrf/vocabulary_policy.ex | 28 ++ test/fixtures/modules/good_mrf.ex | 19 ++ test/pleroma/web/activity_pub/mrf_test.exs | 17 + 16 files changed, 412 insertions(+), 305 deletions(-) create mode 100644 test/fixtures/modules/good_mrf.ex diff --git a/config/description.exs b/config/description.exs index 0b651696b..69072312c 100644 --- a/config/description.exs +++ b/config/description.exs @@ -1,5 +1,4 @@ use Mix.Config -alias Pleroma.Docs.Generator websocket_config = [ path: "/websocket", @@ -1589,264 +1588,6 @@ } ] }, - %{ - group: :pleroma, - key: :mrf_simple, - tab: :mrf, - related_policy: "Pleroma.Web.ActivityPub.MRF.SimplePolicy", - label: "MRF Simple", - type: :group, - description: "Simple ingress policies", - children: [ - %{ - key: :media_removal, - type: {:list, :string}, - description: "List of instances to strip media attachments from", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :media_nsfw, - label: "Media NSFW", - type: {:list, :string}, - description: "List of instances to tag all media as NSFW (sensitive) from", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :federated_timeline_removal, - type: {:list, :string}, - description: - "List of instances to remove from the Federated (aka The Whole Known Network) Timeline", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :reject, - type: {:list, :string}, - description: "List of instances to reject activities from (except deletes)", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :accept, - type: {:list, :string}, - description: "List of instances to only accept activities from (except deletes)", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :followers_only, - type: {:list, :string}, - description: "Force posts from the given instances to be visible by followers only", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :report_removal, - type: {:list, :string}, - description: "List of instances to reject reports from", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :avatar_removal, - type: {:list, :string}, - description: "List of instances to strip avatars from", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :banner_removal, - type: {:list, :string}, - description: "List of instances to strip banners from", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :reject_deletes, - type: {:list, :string}, - description: "List of instances to reject deletions from", - suggestions: ["example.com", "*.example.com"] - } - ] - }, - %{ - group: :pleroma, - key: :mrf_activity_expiration, - tab: :mrf, - related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy", - label: "MRF Activity Expiration Policy", - type: :group, - description: "Adds automatic expiration to all local activities", - children: [ - %{ - key: :days, - type: :integer, - description: "Default global expiration time for all local activities (in days)", - suggestions: [90, 365] - } - ] - }, - %{ - group: :pleroma, - key: :mrf_subchain, - tab: :mrf, - related_policy: "Pleroma.Web.ActivityPub.MRF.SubchainPolicy", - label: "MRF Subchain", - type: :group, - description: - "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.", - children: [ - %{ - key: :match_actor, - type: {:map, {:list, :string}}, - description: "Matches a series of regular expressions against the actor field", - suggestions: [ - %{ - ~r/https:\/\/example.com/s => [Pleroma.Web.ActivityPub.MRF.DropPolicy] - } - ] - } - ] - }, - %{ - group: :pleroma, - key: :mrf_rejectnonpublic, - tab: :mrf, - related_policy: "Pleroma.Web.ActivityPub.MRF.RejectNonPublic", - description: "RejectNonPublic drops posts with non-public visibility settings.", - label: "MRF Reject Non Public", - type: :group, - children: [ - %{ - key: :allow_followersonly, - label: "Allow followers-only", - type: :boolean, - description: "Whether to allow followers-only posts" - }, - %{ - key: :allow_direct, - type: :boolean, - description: "Whether to allow direct messages" - } - ] - }, - %{ - group: :pleroma, - key: :mrf_hellthread, - tab: :mrf, - related_policy: "Pleroma.Web.ActivityPub.MRF.HellthreadPolicy", - label: "MRF Hellthread", - type: :group, - description: "Block messages with excessive user mentions", - children: [ - %{ - key: :delist_threshold, - type: :integer, - description: - "Number of mentioned users after which the message gets removed from timelines and" <> - "disables notifications. Set to 0 to disable.", - suggestions: [10] - }, - %{ - key: :reject_threshold, - type: :integer, - description: - "Number of mentioned users after which the messaged gets rejected. Set to 0 to disable.", - suggestions: [20] - } - ] - }, - %{ - group: :pleroma, - key: :mrf_keyword, - tab: :mrf, - related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy", - label: "MRF Keyword", - type: :group, - description: - "Reject or Word-Replace messages matching a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).", - children: [ - %{ - key: :reject, - type: {:list, :string}, - description: """ - A list of patterns which result in message being rejected. - - Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. - """, - suggestions: ["foo", ~r/foo/iu] - }, - %{ - key: :federated_timeline_removal, - type: {:list, :string}, - description: """ - A list of patterns which result in message being removed from federated timelines (a.k.a unlisted). - - Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. - """, - suggestions: ["foo", ~r/foo/iu] - }, - %{ - key: :replace, - type: {:list, :tuple}, - description: """ - **Pattern**: a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. - - **Replacement**: a string. Leaving the field empty is permitted. - """ - } - ] - }, - %{ - group: :pleroma, - key: :mrf_mention, - tab: :mrf, - related_policy: "Pleroma.Web.ActivityPub.MRF.MentionPolicy", - label: "MRF Mention", - type: :group, - description: "Block messages which mention a specific user", - children: [ - %{ - key: :actors, - type: {:list, :string}, - description: "A list of actors for which any post mentioning them will be dropped", - suggestions: ["actor1", "actor2"] - } - ] - }, - %{ - group: :pleroma, - key: :mrf_vocabulary, - tab: :mrf, - related_policy: "Pleroma.Web.ActivityPub.MRF.VocabularyPolicy", - label: "MRF Vocabulary", - type: :group, - description: "Filter messages which belong to certain activity vocabularies", - children: [ - %{ - key: :accept, - type: {:list, :string}, - description: - "A list of ActivityStreams terms to accept. If empty, all supported messages are accepted.", - suggestions: ["Create", "Follow", "Mention", "Announce", "Like"] - }, - %{ - key: :reject, - type: {:list, :string}, - description: - "A list of ActivityStreams terms to reject. If empty, no messages are rejected.", - suggestions: ["Create", "Follow", "Mention", "Announce", "Like"] - } - ] - }, - # %{ - # group: :pleroma, - # key: :mrf_user_allowlist, - # tab: :mrf, - # related_policy: "Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy", - # type: :map, - # description: - # "The keys in this section are the domain names that the policy should apply to." <> - # " Each key should be assigned a list of users that should be allowed through by their ActivityPub ID", - # suggestions: [ - # %{"example.org" => ["https://example.org/users/admin"]} - # ] - # ] - # }, %{ group: :pleroma, key: :media_proxy, @@ -3159,22 +2900,6 @@ } ] }, - %{ - group: :pleroma, - key: :mrf_normalize_markup, - tab: :mrf, - related_policy: "Pleroma.Web.ActivityPub.MRF.NormalizeMarkup", - label: "MRF Normalize Markup", - description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.", - type: :group, - children: [ - %{ - key: :scrub_policy, - type: :module, - suggestions: [Pleroma.HTML.Scrubber.Default] - } - ] - }, %{ group: :pleroma, key: Pleroma.User, @@ -3364,33 +3089,6 @@ } ] }, - %{ - group: :pleroma, - key: :mrf_object_age, - tab: :mrf, - related_policy: "Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy", - label: "MRF Object Age", - type: :group, - description: - "Rejects or delists posts based on their timestamp deviance from your server's clock.", - children: [ - %{ - key: :threshold, - type: :integer, - description: "Required age (in seconds) of a post before actions are taken.", - suggestions: [172_800] - }, - %{ - key: :actions, - type: {:list, :atom}, - description: - "A list of actions to apply to the post. `:delist` removes the post from public timelines; " <> - "`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines; " <> - "`:reject` rejects the message entirely", - suggestions: [:delist, :strip_followers, :reject] - } - ] - }, %{ group: :pleroma, key: :modules, diff --git a/lib/pleroma/docs/json.ex b/lib/pleroma/docs/json.ex index 13618b509..a583e2a5b 100644 --- a/lib/pleroma/docs/json.ex +++ b/lib/pleroma/docs/json.ex @@ -11,7 +11,11 @@ defmodule Pleroma.Docs.JSON do @spec compile :: :ok def compile do - :persistent_term.put(@term, Pleroma.Docs.Generator.convert_to_strings(@raw_descriptions)) + descriptions = + Pleroma.Web.ActivityPub.MRF.config_descriptions() + |> Enum.reduce(@raw_descriptions, fn description, acc -> [description | acc] end) + + :persistent_term.put(@term, Pleroma.Docs.Generator.convert_to_strings(descriptions)) end @spec compiled_descriptions :: Map.t() diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex index 5e5361082..656e4c7ca 100644 --- a/lib/pleroma/web/activity_pub/mrf.ex +++ b/lib/pleroma/web/activity_pub/mrf.ex @@ -3,7 +3,26 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF do + require Logger + + @default_description %{ + label: "", + description: "", + children: [] + } + + @required_description_keys [:key, :related_policy] + @callback filter(Map.t()) :: {:ok | :reject, Map.t()} + @callback describe() :: {:ok | :error, Map.t()} + @callback config_description() :: %{ + optional(:children) => [map()], + key: atom(), + related_policy: String.t(), + label: String.t(), + description: String.t() + } + @optional_callbacks config_description: 0 def filter(policies, %{} = message) do policies @@ -51,8 +70,6 @@ def subdomain_match?(domains, host) do Enum.any?(domains, fn domain -> Regex.match?(domain, host) end) end - @callback describe() :: {:ok | :error, Map.t()} - def describe(policies) do {:ok, policy_configs} = policies @@ -82,4 +99,41 @@ def describe(policies) do end def describe, do: get_policies() |> describe() + + def config_descriptions do + Pleroma.Web.ActivityPub.MRF + |> Pleroma.Docs.Generator.list_behaviour_implementations() + |> config_descriptions() + end + + def config_descriptions(policies) do + Enum.reduce(policies, [], fn policy, acc -> + if function_exported?(policy, :config_description, 0) do + description = + @default_description + |> Map.merge(policy.config_description) + |> Map.put(:group, :pleroma) + |> Map.put(:tab, :mrf) + |> Map.put(:type, :group) + + if Enum.all?(@required_description_keys, &Map.has_key?(description, &1)) do + [description | acc] + else + Logger.warn( + "#{policy} config description doesn't have one or all required keys #{ + inspect(@required_description_keys) + }" + ) + + acc + end + else + Logger.info( + "#{policy} is excluded from config descriptions, because does not implement `config_description/0` method." + ) + + acc + end + end) + end end diff --git a/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex b/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex index bee47b4ed..655a2ced0 100644 --- a/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex @@ -40,4 +40,22 @@ defp maybe_add_expiration(activity) do _ -> Map.put(activity, "expires_at", expires_at) end end + + @impl true + def config_description do + %{ + key: :mrf_activity_expiration, + related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy", + label: "MRF Activity Expiration Policy", + description: "Adds automatic expiration to all local activities", + children: [ + %{ + key: :days, + type: :integer, + description: "Default global expiration time for all local activities (in days)", + suggestions: [90, 365] + } + ] + } + end end diff --git a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex index 9ba07b4e3..3fd5c1e0a 100644 --- a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex @@ -97,4 +97,31 @@ def filter(message), do: {:ok, message} @impl true def describe, do: {:ok, %{mrf_hellthread: Pleroma.Config.get(:mrf_hellthread) |> Enum.into(%{})}} + + @impl true + def config_description do + %{ + key: :mrf_hellthread, + related_policy: "Pleroma.Web.ActivityPub.MRF.HellthreadPolicy", + label: "MRF Hellthread", + description: "Block messages with excessive user mentions", + children: [ + %{ + key: :delist_threshold, + type: :integer, + description: + "Number of mentioned users after which the message gets removed from timelines and" <> + "disables notifications. Set to 0 to disable.", + suggestions: [10] + }, + %{ + key: :reject_threshold, + type: :integer, + description: + "Number of mentioned users after which the messaged gets rejected. Set to 0 to disable.", + suggestions: [20] + } + ] + } + end end diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex index db66cfa3e..ded0fe7f2 100644 --- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex @@ -126,4 +126,46 @@ def describe do {:ok, %{mrf_keyword: mrf_keyword}} end + + @impl true + def config_description do + %{ + key: :mrf_keyword, + related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy", + label: "MRF Keyword", + description: + "Reject or Word-Replace messages matching a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).", + children: [ + %{ + key: :reject, + type: {:list, :string}, + description: """ + A list of patterns which result in message being rejected. + + Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. + """, + suggestions: ["foo", ~r/foo/iu] + }, + %{ + key: :federated_timeline_removal, + type: {:list, :string}, + description: """ + A list of patterns which result in message being removed from federated timelines (a.k.a unlisted). + + Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. + """, + suggestions: ["foo", ~r/foo/iu] + }, + %{ + key: :replace, + type: {:list, :tuple}, + description: """ + **Pattern**: a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. + + **Replacement**: a string. Leaving the field empty is permitted. + """ + } + ] + } + end end diff --git a/lib/pleroma/web/activity_pub/mrf/mention_policy.ex b/lib/pleroma/web/activity_pub/mrf/mention_policy.ex index 7910ca131..9c096712a 100644 --- a/lib/pleroma/web/activity_pub/mrf/mention_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/mention_policy.ex @@ -25,4 +25,22 @@ def filter(message), do: {:ok, message} @impl true def describe, do: {:ok, %{}} + + @impl true + def config_description do + %{ + key: :mrf_mention, + related_policy: "Pleroma.Web.ActivityPub.MRF.MentionPolicy", + label: "MRF Mention", + description: "Block messages which mention a specific user", + children: [ + %{ + key: :actors, + type: {:list, :string}, + description: "A list of actors for which any post mentioning them will be dropped", + suggestions: ["actor1", "actor2"] + } + ] + } + end end diff --git a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex index 7abae37ae..e00575c2a 100644 --- a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex +++ b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do @behaviour Pleroma.Web.ActivityPub.MRF + @impl true def filter(%{"type" => "Create", "object" => child_object} = object) do scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy]) @@ -22,5 +23,23 @@ def filter(%{"type" => "Create", "object" => child_object} = object) do def filter(object), do: {:ok, object} + @impl true def describe, do: {:ok, %{}} + + @impl true + def config_description do + %{ + key: :mrf_normalize_markup, + related_policy: "Pleroma.Web.ActivityPub.MRF.NormalizeMarkup", + label: "MRF Normalize Markup", + description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.", + children: [ + %{ + key: :scrub_policy, + type: :module, + suggestions: [Pleroma.HTML.Scrubber.Default] + } + ] + } + end end diff --git a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex index d45d2d7e3..eb0481f20 100644 --- a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex @@ -106,4 +106,32 @@ def describe do {:ok, %{mrf_object_age: mrf_object_age}} end + + @impl true + def config_description do + %{ + key: :mrf_object_age, + related_policy: "Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy", + label: "MRF Object Age", + description: + "Rejects or delists posts based on their timestamp deviance from your server's clock.", + children: [ + %{ + key: :threshold, + type: :integer, + description: "Required age (in seconds) of a post before actions are taken.", + suggestions: [172_800] + }, + %{ + key: :actions, + type: {:list, :atom}, + description: + "A list of actions to apply to the post. `:delist` removes the post from public timelines; " <> + "`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines; " <> + "`:reject` rejects the message entirely", + suggestions: [:delist, :strip_followers, :reject] + } + ] + } + end end diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex index 0b9ed2224..cd7665e31 100644 --- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex +++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex @@ -48,4 +48,27 @@ def filter(object), do: {:ok, object} @impl true def describe, do: {:ok, %{mrf_rejectnonpublic: Config.get(:mrf_rejectnonpublic) |> Enum.into(%{})}} + + @impl true + def config_description do + %{ + key: :mrf_rejectnonpublic, + related_policy: "Pleroma.Web.ActivityPub.MRF.RejectNonPublic", + description: "RejectNonPublic drops posts with non-public visibility settings.", + label: "MRF Reject Non Public", + children: [ + %{ + key: :allow_followersonly, + label: "Allow followers-only", + type: :boolean, + description: "Whether to allow followers-only posts" + }, + %{ + key: :allow_direct, + type: :boolean, + description: "Whether to allow direct messages" + } + ] + } + end end diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index 161177727..6cd91826d 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -244,4 +244,78 @@ def describe do {:ok, %{mrf_simple: mrf_simple}} end + + @impl true + def config_description do + %{ + key: :mrf_simple, + related_policy: "Pleroma.Web.ActivityPub.MRF.SimplePolicy", + label: "MRF Simple", + description: "Simple ingress policies", + children: [ + %{ + key: :media_removal, + type: {:list, :string}, + description: "List of instances to strip media attachments from", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :media_nsfw, + label: "Media NSFW", + type: {:list, :string}, + description: "List of instances to tag all media as NSFW (sensitive) from", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :federated_timeline_removal, + type: {:list, :string}, + description: + "List of instances to remove from the Federated (aka The Whole Known Network) Timeline", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :reject, + type: {:list, :string}, + description: "List of instances to reject activities from (except deletes)", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :accept, + type: {:list, :string}, + description: "List of instances to only accept activities from (except deletes)", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :followers_only, + type: {:list, :string}, + description: "Force posts from the given instances to be visible by followers only", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :report_removal, + type: {:list, :string}, + description: "List of instances to reject reports from", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :avatar_removal, + type: {:list, :string}, + description: "List of instances to strip avatars from", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :banner_removal, + type: {:list, :string}, + description: "List of instances to strip banners from", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :reject_deletes, + type: {:list, :string}, + description: "List of instances to reject deletions from", + suggestions: ["example.com", "*.example.com"] + } + ] + } + end end diff --git a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex index 048052da6..2ec45260a 100644 --- a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex @@ -39,4 +39,28 @@ def filter(message), do: {:ok, message} @impl true def describe, do: {:ok, %{}} + + @impl true + def config_description do + %{ + key: :mrf_subchain, + related_policy: "Pleroma.Web.ActivityPub.MRF.SubchainPolicy", + label: "MRF Subchain", + description: + "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.", + children: [ + %{ + key: :match_actor, + type: {:map, {:list, :string}}, + description: "Matches a series of regular expressions against the actor field", + suggestions: [ + %{ + ~r/https:\/\/example.com/s => [Pleroma.Web.ActivityPub.MRF.DropPolicy] + } + ] + } + ] + } + end end diff --git a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex index 1a28f2ba2..885bcca6f 100644 --- a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex @@ -41,4 +41,18 @@ def describe do {:ok, %{mrf_user_allowlist: mrf_user_allowlist}} end + + @impl true + def config_description do + %{ + key: :mrf_user_allowlist, + related_policy: "Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy", + description: + "The keys in this section are the domain names that the policy should apply to." <> + " Each key should be assigned a list of users that should be allowed through by their ActivityPub ID", + suggestions: [ + %{"example.org" => ["https://example.org/users/admin"]} + ] + } + end end diff --git a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex index a6c545570..f325cb680 100644 --- a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do @behaviour Pleroma.Web.ActivityPub.MRF + @impl true def filter(%{"type" => "Undo", "object" => child_message} = message) do with {:ok, _} <- filter(child_message) do {:ok, message} @@ -36,6 +37,33 @@ def filter(%{"type" => message_type} = message) do def filter(message), do: {:ok, message} + @impl true def describe, do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Enum.into(%{})}} + + @impl true + def config_description do + %{ + key: :mrf_vocabulary, + related_policy: "Pleroma.Web.ActivityPub.MRF.VocabularyPolicy", + label: "MRF Vocabulary", + description: "Filter messages which belong to certain activity vocabularies", + children: [ + %{ + key: :accept, + type: {:list, :string}, + description: + "A list of ActivityStreams terms to accept. If empty, all supported messages are accepted.", + suggestions: ["Create", "Follow", "Mention", "Announce", "Like"] + }, + %{ + key: :reject, + type: {:list, :string}, + description: + "A list of ActivityStreams terms to reject. If empty, no messages are rejected.", + suggestions: ["Create", "Follow", "Mention", "Announce", "Like"] + } + ] + } + end end diff --git a/test/fixtures/modules/good_mrf.ex b/test/fixtures/modules/good_mrf.ex new file mode 100644 index 000000000..39d0f14ec --- /dev/null +++ b/test/fixtures/modules/good_mrf.ex @@ -0,0 +1,19 @@ +defmodule Fixtures.Modules.GoodMRF do + @behaviour Pleroma.Web.ActivityPub.MRF + + @impl true + def filter(a), do: {:ok, a} + + @impl true + def describe, do: %{} + + @impl true + def config_description do + %{ + key: :good_mrf, + related_policy: "Fixtures.Modules.GoodMRF", + label: "Good MRF", + description: "Some description" + } + end +end diff --git a/test/pleroma/web/activity_pub/mrf_test.exs b/test/pleroma/web/activity_pub/mrf_test.exs index e8cdde2e1..17aef2e16 100644 --- a/test/pleroma/web/activity_pub/mrf_test.exs +++ b/test/pleroma/web/activity_pub/mrf_test.exs @@ -87,4 +87,21 @@ test "it works as expected with mock policy" do {:ok, ^expected} = MRF.describe() end end + + test "config_descriptions/0" do + descriptions = MRF.config_descriptions() + + good_mrf = Enum.find(descriptions, fn %{key: key} -> key == :good_mrf end) + + assert good_mrf == %{ + key: :good_mrf, + related_policy: "Fixtures.Modules.GoodMRF", + label: "Good MRF", + description: "Some description", + children: [], + group: :pleroma, + tab: :mrf, + type: :group + } + end end From efc27f64643f24e97c604efa02c15bfe3210cf3b Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 11 Nov 2020 10:10:57 +0300 Subject: [PATCH 33/34] fix for adminFE - revert UserAllowPolicy description - MRF descriptions order --- config/description.exs | 34 --------------- lib/pleroma/web/activity_pub/mrf.ex | 42 +++++++++++++++++-- .../mrf/user_allow_list_policy.ex | 32 ++++++++------ 3 files changed, 58 insertions(+), 50 deletions(-) diff --git a/config/description.exs b/config/description.exs index 69072312c..0552b37e0 100644 --- a/config/description.exs +++ b/config/description.exs @@ -1554,40 +1554,6 @@ } ] }, - %{ - group: :pleroma, - key: :mrf, - tab: :mrf, - label: "MRF", - type: :group, - description: "General MRF settings", - children: [ - %{ - key: :policies, - type: [:module, {:list, :module}], - description: - "A list of MRF policies enabled. Module names are shortened (removed leading `Pleroma.Web.ActivityPub.MRF.` part), but on adding custom module you need to use full name.", - suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF} - }, - %{ - key: :transparency, - label: "MRF transparency", - type: :boolean, - description: - "Make the content of your Message Rewrite Facility settings public (via nodeinfo)" - }, - %{ - key: :transparency_exclusions, - label: "MRF transparency exclusions", - type: {:list, :string}, - description: - "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.", - suggestions: [ - "exclusion.com" - ] - } - ] - }, %{ group: :pleroma, key: :media_proxy, diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex index 656e4c7ca..ce125a696 100644 --- a/lib/pleroma/web/activity_pub/mrf.ex +++ b/lib/pleroma/web/activity_pub/mrf.ex @@ -5,10 +5,46 @@ defmodule Pleroma.Web.ActivityPub.MRF do require Logger + @mrf_config_descriptions [ + %{ + group: :pleroma, + key: :mrf, + tab: :mrf, + label: "MRF", + type: :group, + description: "General MRF settings", + children: [ + %{ + key: :policies, + type: [:module, {:list, :module}], + description: + "A list of MRF policies enabled. Module names are shortened (removed leading `Pleroma.Web.ActivityPub.MRF.` part), but on adding custom module you need to use full name.", + suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF} + }, + %{ + key: :transparency, + label: "MRF transparency", + type: :boolean, + description: + "Make the content of your Message Rewrite Facility settings public (via nodeinfo)" + }, + %{ + key: :transparency_exclusions, + label: "MRF transparency exclusions", + type: {:list, :string}, + description: + "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.", + suggestions: [ + "exclusion.com" + ] + } + ] + } + ] + @default_description %{ label: "", - description: "", - children: [] + description: "" } @required_description_keys [:key, :related_policy] @@ -107,7 +143,7 @@ def config_descriptions do end def config_descriptions(policies) do - Enum.reduce(policies, [], fn policy, acc -> + Enum.reduce(policies, @mrf_config_descriptions, fn policy, acc -> if function_exported?(policy, :config_description, 0) do description = @default_description diff --git a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex index 885bcca6f..f2859abde 100644 --- a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex @@ -42,17 +42,23 @@ def describe do {:ok, %{mrf_user_allowlist: mrf_user_allowlist}} end - @impl true - def config_description do - %{ - key: :mrf_user_allowlist, - related_policy: "Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy", - description: - "The keys in this section are the domain names that the policy should apply to." <> - " Each key should be assigned a list of users that should be allowed through by their ActivityPub ID", - suggestions: [ - %{"example.org" => ["https://example.org/users/admin"]} - ] - } - end + # TODO: change way of getting settings on `lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex:18` to use `hosts` subkey + # @impl true + # def config_description do + # %{ + # key: :mrf_user_allowlist, + # related_policy: "Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy", + # description: "Accept-list of users from specified instances", + # children: [ + # %{ + # key: :hosts, + # type: :map, + # description: + # "The keys in this section are the domain names that the policy should apply to." <> + # " Each key should be assigned a list of users that should be allowed through by their ActivityPub ID", + # suggestions: [%{"example.org" => ["https://example.org/users/admin"]}] + # } + # ] + # } + # end end From f97f24b067b6d0205f093b04aeb08c3d56faa7b4 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 11 Nov 2020 10:48:03 +0300 Subject: [PATCH 34/34] making credo happy and test fix --- lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex | 3 ++- test/pleroma/web/activity_pub/mrf_test.exs | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex index f2859abde..e9d0d0503 100644 --- a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex @@ -55,7 +55,8 @@ def describe do # type: :map, # description: # "The keys in this section are the domain names that the policy should apply to." <> - # " Each key should be assigned a list of users that should be allowed through by their ActivityPub ID", + # " Each key should be assigned a list of users that should be allowed " <> + # "through by their ActivityPub ID", # suggestions: [%{"example.org" => ["https://example.org/users/admin"]}] # } # ] diff --git a/test/pleroma/web/activity_pub/mrf_test.exs b/test/pleroma/web/activity_pub/mrf_test.exs index 17aef2e16..44a9cf086 100644 --- a/test/pleroma/web/activity_pub/mrf_test.exs +++ b/test/pleroma/web/activity_pub/mrf_test.exs @@ -98,7 +98,6 @@ test "config_descriptions/0" do related_policy: "Fixtures.Modules.GoodMRF", label: "Good MRF", description: "Some description", - children: [], group: :pleroma, tab: :mrf, type: :group